套餐相关

This commit is contained in:
SjS 2025-04-26 20:40:03 +08:00
parent 32daf27287
commit 054375ebc0
24 changed files with 899 additions and 45 deletions

View File

@ -12,13 +12,11 @@ import lombok.Getter;
@AllArgsConstructor
public enum LogBizType {
UNKNOWN("0", "未知"),
PRICE("1", "单价"),
REPORT("2", "报表"),
PROD_ORDER("3", "生产订单" ),
MATERIAL("4", "物料"),
UNKNOWN("UNKNOWN", "未知"),
ORDER("ORDER", "订单"),
UNIT("5", "单位"),
SHIFT("6", "调班");
SHIFT("6", "调班"),
SUIT("SUIT", "套餐");
private final String type;
private final String msg;

View File

@ -1,5 +0,0 @@
package com.ruoyi.bst.bonus.service;
public interface BonusConverter {
}

View File

@ -1,10 +0,0 @@
package com.ruoyi.bst.bonus.service.impl;
import com.ruoyi.bst.bonus.service.BonusConverter;
import org.springframework.stereotype.Service;
@Service
public class BonusConverterImpl implements BonusConverter {
}

View File

@ -9,7 +9,6 @@ import com.ruoyi.bst.bonus.domain.enums.BonusArrivalType;
import com.ruoyi.bst.bonus.domain.enums.BonusBstType;
import com.ruoyi.bst.bonus.domain.enums.BonusStatus;
import com.ruoyi.bst.bonus.mapper.BonusMapper;
import com.ruoyi.bst.bonus.service.BonusConverter;
import com.ruoyi.bst.bonus.service.BonusService;
import com.ruoyi.bst.bonus.utils.BonusUtil;
import com.ruoyi.bst.bonusRefund.domain.BonusRefund;
@ -27,7 +26,6 @@ import org.springframework.transaction.support.TransactionTemplate;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
@ -62,8 +60,6 @@ public class BonusServiceImpl implements BonusService
@Autowired
private BonusRefundConverter bonusRefundConverter;
@Autowired
private BonusConverter bonusConverter;
/**
* 查询分成明细

View File

@ -1,6 +1,7 @@
package com.ruoyi.bst.order.domain;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
@ -78,6 +79,6 @@ public class Order extends BaseEntity
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "支付超时时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty("支付超时时间")
private Date payExpireTime;
private LocalDateTime payExpireTime;
}

View File

@ -1,7 +1,20 @@
package com.ruoyi.bst.order.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
public class OrderQuery extends OrderVO{
@ApiModelProperty("订单id列表")
private List<Long> orderIds;
@ApiModelProperty("精准订单编号")
private String eqNo;
@ApiModelProperty("订单状态列表")
private List<String> statusList;
}

View File

@ -1,7 +1,14 @@
package com.ruoyi.bst.order.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class OrderVO extends Order{
// 支付
@ApiModelProperty("支付单号")
private String payNo;
}

View File

@ -0,0 +1,48 @@
package com.ruoyi.bst.order.domain.bo;
import com.ruoyi.bst.app.domain.AppVO;
import com.ruoyi.bst.channel.domain.ChannelVO;
import com.ruoyi.bst.device.domain.DeviceVO;
import com.ruoyi.bst.model.domain.ModelVO;
import com.ruoyi.bst.order.domain.Order;
import com.ruoyi.bst.order.domain.dto.OrderCreateDTO;
import com.ruoyi.bst.order.domain.vo.OrderPrePriceVO;
import com.ruoyi.bst.pay.domain.vo.DoPayVO;
import com.ruoyi.bst.suit.domain.SuitVO;
import com.ruoyi.bst.userApp.domain.UserAppVO;
import com.ruoyi.common.core.domain.vo.UserVO;
import lombok.Data;
import java.util.List;
@Data
public class OrderCreateBO {
// 原始参数
private OrderCreateDTO dto;
// 套餐
private SuitVO suit;
// 下单用户
private UserVO user;
// 计算的价格信息
private OrderPrePriceVO prePrice;
// 渠道
private ChannelVO channel;
// APP信息
private AppVO app;
// 用户APP绑定信息
private UserAppVO userApp;
// 生成的订单
private Order order;
// 调起支付的返回值
private DoPayVO doPayVO;
}

View File

@ -0,0 +1,35 @@
package com.ruoyi.bst.order.domain.dto;
import com.ruoyi.bst.order.domain.vo.OrderPrePriceVO;
import com.ruoyi.common.core.interfaces.LogBizParam;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
public class OrderCreateDTO implements LogBizParam {
@ApiModelProperty("下单用户ID")
private Long userId;
@ApiModelProperty("套餐ID")
@NotNull(message = "套餐ID不能为空")
private Long suitId;
@ApiModelProperty("APP ID")
@NotNull(message = "APP ID不能为空")
private Long appId;
@ApiModelProperty("支付渠道ID")
@NotNull(message = "支付渠道ID不能为空")
private Long channelId;
@ApiModelProperty("价格信息")
private OrderPrePriceVO price;
@Override
public Object logBizId() {
return suitId;
}
}

View File

@ -0,0 +1,69 @@
package com.ruoyi.bst.order.domain.enums;
import com.ruoyi.common.utils.collection.CollectionUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
@Getter
@AllArgsConstructor
public enum OrderStatus {
WAIT_PAY("WAIT_PAY", "待支付"),
PROCESSING("PROCESSING", "进行中"),
FINISHED("FINISHED", "已结束"),
CANCELED("CANCELED", "已取消"),
WAIT_VERIFY("WAIT_VERIFY", "待审核"),
REJECTED("REJECTED", "已驳回"),
REFUNDED("REFUNDED", "已退款");
private final String code;
private final String name;
// 允许支付的订单状态
public static List<String> canPay() {
return CollectionUtils.map(OrderStatus::getCode, WAIT_PAY);
}
// 正在使用中的订单状态
public static List<String> inUse() {
return CollectionUtils.map(OrderStatus::getCode, PROCESSING);
}
// 可以支付成功的订单状态
public static List<String> canPaySuccess() {
return CollectionUtils.map(OrderStatus::getCode, WAIT_PAY);
}
// 可以结束的订单状态
public static List<String> canEnd() {
return CollectionUtils.map(OrderStatus::getCode, PROCESSING);
}
// 可以退款的订单状态
public static List<String> canRefund() {
return CollectionUtils.map(OrderStatus::getCode, FINISHED, REFUNDED, REJECTED, WAIT_VERIFY);
}
// 未支付的订单状态
public static List<String> unPayList() {
return CollectionUtils.map(OrderStatus::getCode, WAIT_PAY);
}
// 可以取消支付的订单状态
public static List<String> canCancelPay() {
return CollectionUtils.map(OrderStatus::getCode, WAIT_PAY);
}
// 可以审核的订单状态
public static List<String> canVerify() {
return CollectionUtils.map(OrderStatus::getCode, WAIT_VERIFY);
}
// 有效的订单状态已支付的订单
public static List<String> valid() {
return CollectionUtils.map(OrderStatus::getCode, PROCESSING, FINISHED, WAIT_VERIFY, REJECTED, REFUNDED);
}
}

View File

@ -0,0 +1,14 @@
package com.ruoyi.bst.order.domain.vo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class OrderPrePriceVO {
@ApiModelProperty("支付金额")
private BigDecimal payAmount;
}

View File

@ -71,4 +71,21 @@ public interface OrderMapper
* @return 结果
*/
public int deleteOrderByIds(Long[] ids);
/**
* 根据查询条件查询订单ID列表
* @param query 查询条件
* @return 订单ID列表
*/
List<Long> selectIdByQuery(@Param("query") OrderQuery query);
/**
* 根据查询条件更新订单
* @param data 更新数据
* @param query 查询条件
* @return 结果
*/
int updateByQuery(@Param("data") Order data, @Param("query") OrderQuery query);
}

View File

@ -25,23 +25,29 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<sql id="selectOrderVo">
select
id,
order_no,
store_id,
store_name,
user_id,
pay_id,
pay_amount,
mark,
status,
create_time,
suit_id,
suit_name,
suit_num,
suit_amount,
cancel_remark,
pay_expire_time
from bst_order
bo.id,
bo.order_no,
bo.store_id,
bo.store_name,
bo.user_id,
bo.pay_id,
bo.pay_amount,
bo.mark,
bo.status,
bo.create_time,
bo.suit_id,
bo.suit_name,
bo.suit_num,
bo.suit_amount,
bo.cancel_remark,
bo.pay_expire_time
from <include refid="searchTables"></include>
</sql>
<sql id="searchTables">
bst_order bo
left join sys_user su on bo.user_id = su.user_id
left join bst_pay bp on bo.pay_id = bp.id
</sql>
<sql id="searchCondition">
@ -73,6 +79,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<include refid="selectOrderVo"/>
where id = #{id}
</select>
<!-- selectIdByQuery -->
<select id="selectIdByQuery" resultType="Long">
select bo.id
from <include refid="searchTables"/>
<where>
<include refid="searchCondition"/>
</where>
</select>
<insert id="insertOrder" parameterType="Order" useGeneratedKeys="true" keyProperty="id">
insert into bst_order
@ -336,6 +352,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where id = #{data.id}
</update>
<!-- updateByQuery -->
<update id="updateByQuery">
update bst_order bo
<trim prefix="SET" suffixOverrides=",">
<include refid="updateColumns"/>
</trim>
<where>
<include refid="searchCondition"/>
</where>
</update>
<sql id="updateColumns">
<if test="data.orderNo != null and data.orderNo != ''">order_no = #{data.orderNo},</if>
<if test="data.storeId != null">store_id = #{data.storeId},</if>

View File

@ -0,0 +1,16 @@
package com.ruoyi.bst.order.service;
import com.ruoyi.bst.order.domain.Order;
import com.ruoyi.bst.order.domain.OrderVO;
import com.ruoyi.bst.order.domain.bo.OrderCreateBO;
import com.ruoyi.bst.order.domain.dto.*;
public interface OrderConverter {
/**
* 订单创建将DTO转换为BO
*/
OrderCreateBO toCreateBO(OrderCreateDTO dto);
}

View File

@ -4,6 +4,8 @@ import java.util.List;
import com.ruoyi.bst.order.domain.Order;
import com.ruoyi.bst.order.domain.OrderVO;
import com.ruoyi.bst.order.domain.OrderQuery;
import com.ruoyi.bst.order.domain.dto.OrderCreateDTO;
import com.ruoyi.bst.pay.domain.vo.DoPayVO;
/**
* 订单列表Service接口
@ -60,4 +62,50 @@ public interface OrderService
* @return 结果
*/
public int deleteOrderById(Long id);
/**
* 创建订单
* @param dto 创建订单DTO
* @return 订单
*/
public DoPayVO createOrder(OrderCreateDTO dto);
/**
* 查询订单
* @param no 订单编号
* @param scope 是否数据隔离
* @return 订单
*/
public OrderVO selectOrderByNo(String no, boolean scope);
/**
* 查询订单
* @param no 订单编号
* @return 订单
*/
default OrderVO selectOrderByNo(String no) {
return this.selectOrderByNo(no, false);
}
/**
* 查询单个订单
* @param query 查询条件
* @return 订单
*/
public OrderVO selectOne(OrderQuery query);
/**
* 取消过期订单
* @param order 订单
*/
public void cancelWhenExpired(Order order);
/**
* 取消订单
* @param id 订单ID
* @return 结果
*/
public int cancelOrder(Long id, String remark);
}

View File

@ -0,0 +1,55 @@
package com.ruoyi.bst.order.service;
import com.ruoyi.bst.device.domain.DeviceVO;
import com.ruoyi.bst.order.domain.OrderVO;
import com.ruoyi.bst.order.domain.bo.OrderCreateBO;
public interface OrderValidator {
/**
* 校验订单BO
* @param bo 订单BO
*/
void validate(OrderCreateBO bo);
/**
* 校验用户是否可以支付指定订单
* @param order 订单
* @param userId 用户ID
* @return 是否可以支付
*/
boolean canPay(OrderVO order, Long userId);
/**
* 校验普通用户是否可以结束指定订单
* @param order 订单
* @param userId 用户ID
* @return 是否可以结束
*/
boolean canEnd(OrderVO order, Long userId);
/**
* 校验用户是否可以操作设备开启
* @param order 订单
* @param userId 用户ID
* @return 是否可以操作设备开启
*/
boolean canOpenDevice(OrderVO order, Long userId);
/**
* 校验用户是否可以操作设备关闭
* @param order 订单
* @param userId 用户ID
* @return 是否可以操作设备关闭
*/
boolean canCloseDevice(OrderVO order, Long userId);
/**
* 校验用户是否可以操作指定订单
* @param orderId 订单ID
* @return 是否可以操作
*/
boolean canOperate(Long orderId);
}

View File

@ -0,0 +1,133 @@
package com.ruoyi.bst.order.service.impl;
import com.ruoyi.bst.app.domain.AppVO;
import com.ruoyi.bst.app.service.AppService;
import com.ruoyi.bst.channel.domain.ChannelVO;
import com.ruoyi.bst.channel.service.ChannelService;
import com.ruoyi.bst.device.service.DeviceIotService;
import com.ruoyi.bst.device.service.DeviceService;
import com.ruoyi.bst.model.service.ModelService;
import com.ruoyi.bst.order.domain.Order;
import com.ruoyi.bst.order.domain.bo.OrderCreateBO;
import com.ruoyi.bst.order.domain.dto.*;
import com.ruoyi.bst.order.domain.vo.OrderPrePriceVO;
import com.ruoyi.bst.order.service.OrderConverter;
import com.ruoyi.bst.order.service.OrderService;
import com.ruoyi.bst.order.utils.OrderUtil;
import com.ruoyi.bst.suit.domain.SuitVO;
import com.ruoyi.bst.suit.service.SuitService;
import com.ruoyi.bst.userApp.domain.UserAppVO;
import com.ruoyi.bst.userApp.service.UserAppService;
import com.ruoyi.common.core.domain.vo.UserVO;
import com.ruoyi.system.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
@Service
public class OrderConverterImpl implements OrderConverter{
@Autowired
private SuitService suitService;
@Autowired
private DeviceService deviceService;
@Autowired
private UserService userService;
@Autowired
private ChannelService channelService;
@Autowired
private AppService appService;
@Autowired
private UserAppService userAppService;
@Autowired
private DeviceIotService deviceIotService;
@Autowired
private OrderService orderService;
@Autowired
private ModelService modelService;
@Override
public OrderCreateBO toCreateBO(OrderCreateDTO dto) {
if (dto == null) {
return null;
}
OrderCreateBO bo = new OrderCreateBO();
bo.setDto(dto);
// 套餐
SuitVO suit = suitService.selectSuitById(dto.getSuitId());
bo.setSuit(suit);
// 用户
UserVO user = userService.selectUserById(dto.getUserId());
bo.setUser(user);
// 查询渠道
ChannelVO channel = channelService.selectChannelByChannelId(dto.getChannelId());
bo.setChannel(channel);
// 查询应用
AppVO app = appService.selectAppById(dto.getAppId());
bo.setApp(app);
// 价格信息
OrderPrePriceVO prePrice = OrderUtil.calcPrePrice(suit);
bo.setPrePrice(prePrice);
// 查询用户应用信息
if (user != null && app != null) {
UserAppVO userApp = userAppService.selectByUserApp(user.getUserId(), app.getId());
bo.setUserApp(userApp);
}
// 生成订单
Order order = this.generateOrder(bo);
bo.setOrder(order);
return bo;
}
// 生成订单
private Order generateOrder(OrderCreateBO bo) {
if (bo == null) {
return null;
}
Order order = new Order();
// 基础信息
order.setPayExpireTime(LocalDateTime.now().plusMinutes(3));
// 用户信息
UserVO user = bo.getUser();
if (user != null) {
order.setUserId(user.getUserId());
}
// 套餐信息
SuitVO suit = bo.getSuit();
if (suit != null) {
order.setStoreId(suit.getStoreId());
order.setSuitId(suit.getSuitId());
order.setSuitName(suit.getSuitName());
order.setPayAmount(suit.getRechargeAmount());
order.setSuitNum(suit.getLightingNums());
}
return order;
}
}

View File

@ -1,7 +1,28 @@
package com.ruoyi.bst.order.service.impl;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.github.pagehelper.PageHelper;
import com.ruoyi.bst.order.domain.bo.OrderCreateBO;
import com.ruoyi.bst.order.domain.dto.OrderCreateDTO;
import com.ruoyi.bst.order.domain.enums.OrderStatus;
import com.ruoyi.bst.order.service.OrderConverter;
import com.ruoyi.bst.order.service.OrderValidator;
import com.ruoyi.bst.pay.domain.Pay;
import com.ruoyi.bst.pay.domain.enums.PayBstType;
import com.ruoyi.bst.pay.domain.vo.DoPayVO;
import com.ruoyi.bst.pay.service.PayConverter;
import com.ruoyi.bst.pay.service.PayService;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.SnowFlakeUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.collection.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.bst.order.mapper.OrderMapper;
@ -9,6 +30,7 @@ import com.ruoyi.bst.order.domain.Order;
import com.ruoyi.bst.order.domain.OrderVO;
import com.ruoyi.bst.order.domain.OrderQuery;
import com.ruoyi.bst.order.service.OrderService;
import org.springframework.transaction.support.TransactionTemplate;
/**
* 订单列表Service业务层处理
@ -21,7 +43,18 @@ public class OrderServiceImpl implements OrderService
{
@Autowired
private OrderMapper orderMapper;
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private OrderConverter orderConverter;
@Autowired
private OrderValidator orderValidator;
@Autowired
private PayConverter payConverter;
@Autowired
private PayService payService;
@Autowired
private ScheduledExecutorService scheduledExecutorService;
/**
* 查询订单列表
*
@ -56,6 +89,7 @@ public class OrderServiceImpl implements OrderService
public int insertOrder(Order order)
{
order.setCreateTime(DateUtils.getNowDate());
order.setOrderNo(String.valueOf(SnowFlakeUtil.newId()));
return orderMapper.insertOrder(order);
}
@ -94,4 +128,125 @@ public class OrderServiceImpl implements OrderService
{
return orderMapper.deleteOrderById(id);
}
@Override
public DoPayVO createOrder(OrderCreateDTO dto) {
// 转为BO对象
OrderCreateBO bo = orderConverter.toCreateBO(dto);
// 校验参数
orderValidator.validate(bo);
return transactionTemplate.execute(status -> {
// 创建订单
Order order = bo.getOrder();
int rows = this.insertOrder(order);
ServiceUtil.assertion(rows != 1, "创建订单失败");
// 查询订单
OrderVO vo = this.selectOrderByNo(order.getOrderNo());
ServiceUtil.assertion(vo == null, "查询创建的订单失败");
// 支付
DoPayVO doPayVO = this.pay(bo);
ServiceUtil.assertion(doPayVO == null, "支付失败");
bo.setDoPayVO(doPayVO);
// 加入延时队列超时取消
this.cancelWhenExpired(order);
return doPayVO;
});
}
private DoPayVO pay(OrderCreateBO bo) {
// 转为支付PO
Pay pay = payConverter.toPayPO(bo);
ServiceUtil.assertion(pay == null, "转为支付单失败");
return transactionTemplate.execute(status -> {
// 创建并调起支付
return payService.createPayBill(pay);
});
}
// 加入延时队列超时取消
@Override
public void cancelWhenExpired(Order order) {
if (order == null || order.getPayExpireTime() == null) {
return;
}
Duration duration = Duration.between(LocalDateTime.now(), order.getPayExpireTime());
long delay = duration.getSeconds();
if (delay <= 0) {
delay = 1;
}
scheduledExecutorService.schedule(() -> {
// 尝试取消订单
this.cancelOrder(order.getId(), "订单超时取消");
}, delay, TimeUnit.SECONDS);
}
// 取消订单
@Override
public int cancelOrder(Long id, String remark) {
if (id == null) {
return 0;
}
// 查询订单
OrderVO order = this.selectOrderById(id);
ServiceUtil.assertion(order == null, "ID为%s的订单不存在", id);
ServiceUtil.assertion(!OrderStatus.canCancelPay().contains(order.getStatus()), "ID为%s的订单当前状态不允许取消", id);
// 刷新支付结果
if (order.getPayNo() != null) {
payService.refreshPayResult(order.getPayNo());
}
// 查询最新订单状态
order = this.selectOrderById(id);
ServiceUtil.assertion(order == null, "ID为%s的订单不存在", id);
ServiceUtil.assertion(!OrderStatus.canCancelPay().contains(order.getStatus()), "ID为%s的订单当前状态不允许取消", id);
// 取消订单
Integer result = transactionTemplate.execute(status -> {
// 更新订单状态
Order data = new Order();
data.setStatus(OrderStatus.CANCELED.getCode());
data.setCancelRemark(remark);
OrderQuery query = new OrderQuery();
query.setId(id);
query.setStatusList(OrderStatus.canCancelPay());
int rows = orderMapper.updateByQuery(data, query);
ServiceUtil.assertion(rows != 1, "ID为%s的订单取消失败状态已发生变化", id);
// 取消支付单
boolean cancelPay = payService.closeByBstId(PayBstType.ORDER.getType(), id);
ServiceUtil.assertion(!cancelPay, "ID为%s的订单取消支付单失败", id);
return rows;
});
return result == null ? 0 : result;
}
@Override
public OrderVO selectOrderByNo(String no, boolean scope) {
if (StringUtils.isBlank(no)) {
return null;
}
OrderQuery query = new OrderQuery();
query.setEqNo(no);
query.setScope(scope);
return this.selectOne(query);
}
@Override
public OrderVO selectOne(OrderQuery query) {
PageHelper.startPage(1, 1);
List<OrderVO> list = this.selectOrderList(query);
return CollectionUtils.firstElement(list);
}
}

View File

@ -0,0 +1,115 @@
package com.ruoyi.bst.order.service.impl;
import com.ruoyi.bst.app.domain.AppVO;
import com.ruoyi.bst.channel.domain.ChannelVO;
import com.ruoyi.bst.device.service.DeviceService;
import com.ruoyi.bst.order.domain.OrderQuery;
import com.ruoyi.bst.order.domain.OrderVO;
import com.ruoyi.bst.order.domain.bo.OrderCreateBO;
import com.ruoyi.bst.order.domain.dto.OrderCreateDTO;
import com.ruoyi.bst.order.domain.vo.OrderPrePriceVO;
import com.ruoyi.bst.order.mapper.OrderMapper;
import com.ruoyi.bst.order.service.OrderValidator;
import com.ruoyi.bst.order.utils.OrderUtil;
import com.ruoyi.bst.suit.domain.SuitVO;
import com.ruoyi.common.core.domain.vo.UserVO;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.collection.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@Service
@Slf4j
public class OrderValidatorImpl implements OrderValidator{
@Autowired
private OrderMapper orderMapper;
@Override
public void validate(OrderCreateBO bo) {
ServiceUtil.assertion(bo == null, "参数错误");
OrderCreateDTO dto = bo.getDto();
ServiceUtil.assertion(dto == null, "参数错误");
// 套餐
SuitVO suit = bo.getSuit();
ServiceUtil.assertion(suit == null, "ID为%s的套餐不存在", dto.getSuitId());
// 用户
UserVO user = bo.getUser();
ServiceUtil.assertion(user == null, "ID为%s的用户不存在", dto.getUserId());
// 价格
OrderPrePriceVO prePrice = bo.getPrePrice();
ServiceUtil.assertion(prePrice == null, "价格计算失败");
// 是否和前端传输的价格一致
boolean priceEquals = OrderUtil.isEquals(prePrice, dto.getPrice());
ServiceUtil.assertion(!priceEquals, "当前价格已发生变化,请刷新后重新下单");
// 应用
AppVO app = bo.getApp();
ServiceUtil.assertion(app == null, "ID为%s的应用不存在", dto.getAppId());
// 渠道
ChannelVO channel = bo.getChannel();
ServiceUtil.assertion(channel == null, "ID为%s的渠道不存在", dto.getChannelId());
ServiceUtil.assertion(channel.getEnabled() == null || !channel.getEnabled(), "ID为%s的渠道暂不可用", channel.getChannelId());
ServiceUtil.assertion(channel.getAppIds() == null || !channel.getAppIds().contains(app.getId()), "ID为%s的渠道不支持ID为%s的应用", channel.getChannelId(), app.getId());
}
@Override
public boolean canPay(OrderVO order, Long userId) {
return isUser(order, userId);
}
@Override
public boolean canEnd(OrderVO order, Long userId) {
return isUser(order, userId);
}
// 是否是下单用户
private boolean isUser(OrderVO order, Long userId) {
return order != null && userId != null && Objects.equals(order.getUserId(), userId);
}
@Override
public boolean canOpenDevice(OrderVO order, Long userId) {
return isUser(order, userId);
}
@Override
public boolean canCloseDevice(OrderVO order, Long userId) {
return isUser(order, userId);
}
@Override
public boolean canOperate(Long orderId) {
return hasPermission(Collections.singletonList(orderId));
}
private boolean hasPermission(List<Long> orderIds) {
if (CollectionUtils.isEmptyElement(orderIds)) {
return true;
}
OrderQuery query = new OrderQuery();
query.setScope(true);
query.setOrderIds(orderIds);
List<Long> list = orderMapper.selectIdByQuery(query);
return new HashSet<>(list).containsAll(orderIds);
}
}

View File

@ -0,0 +1,50 @@
package com.ruoyi.bst.order.utils;
import com.ruoyi.bst.device.domain.DeviceQuery;
import com.ruoyi.bst.device.domain.DeviceVO;
import com.ruoyi.bst.device.domain.enums.DeviceStatus;
import com.ruoyi.bst.order.domain.OrderVO;
import com.ruoyi.bst.order.domain.vo.OrderPrePriceVO;
import com.ruoyi.bst.suit.domain.SuitVO;
import com.ruoyi.common.utils.MathUtils;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
public class OrderUtil {
/**
* 判断两个价格是否一致
* @param a 价格A
* @param b 价格B
* @return 是否一致
*/
public static boolean isEquals(OrderPrePriceVO a, OrderPrePriceVO b) {
if (a == null || b == null) {
return false;
}
return MathUtils.equals(a.getPayAmount(), b.getPayAmount());
}
public static OrderPrePriceVO calcPrePrice(SuitVO suit) {
if (suit == null) {
return null;
}
OrderPrePriceVO vo = new OrderPrePriceVO();
vo.setPayAmount(suit.getRechargeAmount());
return vo;
}
//
// public static BigDecimal calcCanRefundAmount(OrderVO order) {
// if (order == null) {
// return BigDecimal.ZERO;
// }
// return MathUtils.subtractDecimal(order.getPayAmount(), order.getPayRefunding(), order.getPayRefunded());
// }
}

View File

@ -1,7 +1,15 @@
package com.ruoyi.bst.pay.service;
import com.ruoyi.bst.order.domain.bo.OrderCreateBO;
import com.ruoyi.bst.pay.domain.Pay;
public interface PayConverter {
/**
* 将订单支付BO转换为支付PO
*/
Pay toPayPO(OrderCreateBO bo);
}

View File

@ -2,6 +2,8 @@ package com.ruoyi.bst.pay.service.impl;
import com.ruoyi.bst.app.domain.AppVO;
import com.ruoyi.bst.channel.domain.ChannelVO;
import com.ruoyi.bst.order.domain.Order;
import com.ruoyi.bst.order.domain.bo.OrderCreateBO;
import com.ruoyi.bst.pay.domain.Pay;
import com.ruoyi.bst.pay.domain.enums.PayBstType;
import com.ruoyi.bst.pay.domain.enums.PayStatus;
@ -17,5 +19,45 @@ import java.time.LocalDateTime;
@Service
public class PayConverterImpl implements PayConverter {
@Override
public Pay toPayPO(OrderCreateBO bo) {
if (bo == null) {
return null;
}
Order order = bo.getOrder();
UserVO user = bo.getUser();
ChannelVO channel = bo.getChannel();
UserAppVO userApp = bo.getUserApp();
AppVO app = bo.getApp();
if (order == null || user == null || channel == null || userApp == null || app == null) {
return null;
}
Pay pay = new Pay();
// 基础信息
pay.setStatus(PayStatus.WAIT_PAY.getStatus());
pay.setExpireTime(LocalDateTime.now().plusMinutes(10));
// 订单信息
pay.setBstType(PayBstType.ORDER.getType());
pay.setBstId(order.getId());
pay.setAmount(order.getPayAmount());
// 用户信息
pay.setAccount(userApp.getOpenId());
pay.setIp(IpUtils.getIpAddr());
// 应用信息
pay.setAppId(app.getId());
pay.setAppName(app.getName());
// 渠道信息
pay.setChannelId(channel.getChannelId());
pay.setChannelName(channel.getName());
// 渠道成本放最后
pay.setChannelCost(MathUtils.mulDecimal(channel.getCostRate(), pay.getAmount()));
return pay;
}
}

View File

@ -1,12 +1,33 @@
package com.ruoyi.web.app;
import com.ruoyi.bst.order.domain.dto.OrderCreateDTO;
import com.ruoyi.bst.order.service.OrderService;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.enums.LogBizType;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/app/order")
public class AppOrderController {
public class AppOrderController extends BaseController {
@Autowired
private OrderService orderService;
@ApiOperation("下单并支付")
@PostMapping
@Log(title = "下单并支付", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.SUIT)
public AjaxResult createOrder(@RequestBody @Validated OrderCreateDTO dto) {
dto.setUserId(getUserId());
return success(orderService.createOrder(dto));
}
}

View File

@ -39,6 +39,7 @@ public class OrderController extends BaseController
{
startPage();
startOrderBy();
query.setScope(true);
List<OrderVO> list = orderService.selectOrderList(query);
return getDataTable(list);
}