refactor: Seata实验室案例调整为Seata官网购买商品示例

This commit is contained in:
haoxr 2022-11-29 23:33:37 +08:00
parent fdfcd93b4b
commit 43ee28ac8a
23 changed files with 359 additions and 225 deletions

View File

@ -2,8 +2,8 @@ package com.youlai.laboratory.seata.controller;
import com.youlai.common.result.Result;
import com.youlai.laboratory.seata.pojo.form.SeataForm;
import com.youlai.laboratory.seata.pojo.vo.SeataDataVO;
import com.youlai.laboratory.seata.service.ISeataService;
import com.youlai.laboratory.seata.pojo.vo.SeataVO;
import com.youlai.laboratory.seata.service.SeataService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
@ -22,26 +22,12 @@ import org.springframework.web.bind.annotation.*;
@Slf4j
public class SeataController {
private final ISeataService seataService;
@ApiOperation("订单支付")
@PostMapping("/order/_pay")
public Result payOrder(@RequestBody SeataForm seataForm) {
boolean openTx = seataForm.isOpenTx();
boolean result;
if (openTx) {
result = seataService.payOrderWithGlobalTx(seataForm);
} else {
result = seataService.payOrder(seataForm);
}
return Result.success(result);
}
private final SeataService seataService;
@ApiOperation("获取模拟数据")
@GetMapping("/data")
public Result getData() {
SeataDataVO result = seataService.getData();
SeataVO result = seataService.getData();
return Result.success(result);
}
@ -52,4 +38,19 @@ public class SeataController {
return Result.success(result);
}
@ApiOperation("购买商品")
@PostMapping("/purchaseGoods")
public Result purchaseGoods(@RequestBody SeataForm seataForm) {
boolean openTx = seataForm.isOpenTx();
String orderSn = null;
if (openTx) {
// 开启全局事务
orderSn = seataService.purchaseGoodsWithGlobalTx(seataForm);
} else {
orderSn = seataService.purchaseGoods(seataForm);
}
return Result.success(orderSn);
}
}

View File

@ -3,12 +3,28 @@ package com.youlai.laboratory.seata.pojo.form;
import lombok.Data;
/**
*
*
* @author haoxr
* @date 2022/4/21 23:16
*/
@Data
public class SeataForm {
/**
* 会员ID
*/
private Long memberId;
/**
* 商品ID
*/
private Long skuId;
/**
* 订单金额
*/
private Long amount;
/**
* 是否开启事务
@ -16,9 +32,9 @@ public class SeataForm {
private boolean openTx;
/**
* 订单异常
* 是否开启异常
*/
private boolean orderEx;
private boolean openEx;
}

View File

@ -12,7 +12,7 @@ import lombok.Data;
*/
@ApiModel("Seata模拟数据视图对象")
@Data
public class SeataDataVO {
public class SeataVO {
@ApiModelProperty("商品库存信息")
private StockInfo stockInfo;

View File

@ -1,35 +1,20 @@
package com.youlai.laboratory.seata.service;
import com.youlai.laboratory.seata.pojo.form.SeataForm;
import com.youlai.laboratory.seata.pojo.vo.SeataDataVO;
import com.youlai.laboratory.seata.pojo.vo.SeataVO;
/**
* @author haoxr
* @date 2022/4/16 20:49
*/
public interface ISeataService {
/**
* 模拟订单支付
*
* @return
*/
boolean payOrder(SeataForm seataForm);
/**
* 模拟订单支付(分布式事务)
*
* @param seataForm
* @return
*/
boolean payOrderWithGlobalTx(SeataForm seataForm);
public interface SeataService {
/**
* 获取模拟数据
*
* @return
*/
SeataDataVO getData();
SeataVO getData();
/**
* 重置模拟数据
@ -37,4 +22,18 @@ public interface ISeataService {
* @return
*/
boolean resetData();
/**
* 购买商品
*
* @return 订单号
*/
String purchaseGoods(SeataForm seataForm);
/**
* 购买商品(全局事务)
*
* @return 订单号
*/
String purchaseGoodsWithGlobalTx(SeataForm seataForm);
}

View File

@ -1,11 +1,13 @@
package com.youlai.laboratory.seata.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.youlai.common.result.Result;
import com.youlai.laboratory.seata.pojo.form.SeataForm;
import com.youlai.laboratory.seata.pojo.vo.SeataDataVO;
import com.youlai.laboratory.seata.service.ISeataService;
import com.youlai.laboratory.seata.pojo.vo.SeataVO;
import com.youlai.laboratory.seata.service.SeataService;
import com.youlai.mall.oms.api.OrderFeignClient;
import com.youlai.mall.oms.dto.OrderInfoDTO;
import com.youlai.mall.oms.dto.SeataOrderDTO;
import com.youlai.mall.pms.api.SkuFeignClient;
import com.youlai.mall.pms.pojo.dto.SkuInfoDTO;
import com.youlai.mall.ums.api.MemberFeignClient;
@ -24,7 +26,7 @@ import org.springframework.stereotype.Service;
@Service
@Slf4j
@RequiredArgsConstructor
public class SeataServiceImpl implements ISeataService {
public class SeataServiceImpl implements SeataService {
private final SkuFeignClient skuFeignClient;
private final OrderFeignClient orderFeignClient;
@ -66,12 +68,14 @@ public class SeataServiceImpl implements ISeataService {
log.info("========扣减商品库存(Seata)========");
skuFeignClient.deductStock(skuId, 1); // 扣减库存
log.info("========扣减账户余额(Seata)========");
memberFeignClient.deductBalance(memberId, 1000 * 100l); // 扣款1000
log.info("========修改订单状态(Seata)========");
orderFeignClient.createOrder(orderId, 201, seataForm.isOrderEx()); // 已支付
log.info("========修改订单状态(Seata)========");
orderFeignClient.updateOrderStatus(orderId, 201, seataForm.isOrderEx()); // 已支付
log.info("========扣减账户余额(Seata)========");
memberFeignClient.deductBalance(memberId, 1000 * 100l); // 扣款1000
return true;
}
@ -81,26 +85,26 @@ public class SeataServiceImpl implements ISeataService {
* @return
*/
@Override
public SeataDataVO getData() {
SeataDataVO seataDataVO = new SeataDataVO();
public SeataVO getData() {
SeataVO seataVO = new SeataVO();
SkuInfoDTO skuInfoDTO = skuFeignClient.getSkuInfo(skuId).getData();
SeataDataVO.StockInfo stockInfo = new SeataDataVO.StockInfo();
SeataVO.StockInfo stockInfo = new SeataVO.StockInfo();
BeanUtil.copyProperties(skuInfoDTO, stockInfo);
stockInfo.setName(skuInfoDTO.getSkuName());
seataDataVO.setStockInfo(stockInfo);
seataVO.setStockInfo(stockInfo);
MemberInfoDTO memberInfoDTO = memberFeignClient.getMemberInfo(memberId).getData();
SeataDataVO.AccountInfo accountInfo = new SeataDataVO.AccountInfo();
SeataVO.AccountInfo accountInfo = new SeataVO.AccountInfo();
BeanUtil.copyProperties(memberInfoDTO, accountInfo);
seataDataVO.setAccountInfo(accountInfo);
seataVO.setAccountInfo(accountInfo);
OrderInfoDTO orderInfoDTO = orderFeignClient.getOrderInfo(orderId).getData();
SeataDataVO.OrderInfo orderInfo = new SeataDataVO.OrderInfo();
SeataVO.OrderInfo orderInfo = new SeataVO.OrderInfo();
BeanUtil.copyProperties(orderInfoDTO, orderInfo);
seataDataVO.setOrderInfo(orderInfo);
seataVO.setOrderInfo(orderInfo);
return seataDataVO;
return seataVO;
}
/**
@ -116,4 +120,52 @@ public class SeataServiceImpl implements ISeataService {
return true;
}
/**
* 购买商品
*
* @return 订单号
*/
@Override
public String purchaseGoods(SeataForm seataForm) {
log.info("========扣减商品库存(全局事务)========");
skuFeignClient.deductStock(seataForm.getSkuId(), 1); // 扣减库存
log.info("========创建订单(全局事务)========");
SeataOrderDTO seataOrderDTO = new SeataOrderDTO(
seataForm.getMemberId(),
seataForm.getSkuId(),
seataForm.getAmount()
);
boolean openEx = seataForm.isOpenEx(); // 是否开启异常
Result<String> result = orderFeignClient.createOrder(seataOrderDTO, openEx);
String orderSn = result.getData();
return orderSn;
}
/**
* 购买商品(全局事务)
*
* @return 订单号
*/
@GlobalTransactional
@Override
public String purchaseGoodsWithGlobalTx(SeataForm seataForm) {
log.info("========扣减商品库存(全局事务)========");
skuFeignClient.deductStock(seataForm.getSkuId(), 1); // 扣减库存
log.info("========创建订单(全局事务)========");
SeataOrderDTO seataOrderDTO = new SeataOrderDTO(
seataForm.getMemberId(),
seataForm.getSkuId(),
seataForm.getAmount()
);
boolean openEx = seataForm.isOpenEx(); // 是否开启异常
Result<String> result = orderFeignClient.createOrder(seataOrderDTO, openEx);
String orderSn = result.getData();
return orderSn;
}
}

View File

@ -2,11 +2,9 @@ package com.youlai.mall.oms.api;
import com.youlai.common.result.Result;
import com.youlai.mall.oms.dto.OrderInfoDTO;
import com.youlai.mall.oms.dto.SeataOrderDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.*;
/**
* 订单Feign客户端
@ -38,4 +36,13 @@ public interface OrderFeignClient {
@GetMapping("/api/v1/orders/{orderId}/info")
Result<OrderInfoDTO> getOrderInfo(@PathVariable Long orderId);
/**
* 实验室创建订单
*
* @param orderDTO
* @param openEx 是否出现异常
* @return
*/
@PostMapping("/api/v1/orders")
Result<String> createOrder(SeataOrderDTO orderDTO, boolean openEx);
}

View File

@ -3,10 +3,10 @@ package com.youlai.mall.oms.dto;
import lombok.Data;
/**
* 订单信息传输层对象
* 订单传输层对象
*
* @author <a href="mailto:xianrui0365@163.com">haoxr</a>
* @date 2022/4/17 21:12
* @author haoxr
* @date 2022/4/17
*/
@Data
public class OrderInfoDTO {

View File

@ -0,0 +1,28 @@
package com.youlai.mall.oms.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SeataOrderDTO {
/**
* 会员ID
*/
private Long memberId;
/**
* 商品ID
*/
private Long skuId;
/**
* 订单金额
*/
private Long amount;
}

View File

@ -11,17 +11,17 @@ import lombok.Getter;
*/
@AllArgsConstructor
public enum OrderTypeEnum {
public enum OrderSourceTypeEnum {
WEB(0), // PC订单
PC(0), // PC订单
APP(1), // APP订单
;
@Getter
private Integer code;
public static OrderTypeEnum getValue(Integer code){
for (OrderTypeEnum value : values()) {
public static OrderSourceTypeEnum getValue(Integer code){
for (OrderSourceTypeEnum value : values()) {
if (value.getCode().equals(code)) {
return value;
}

View File

@ -1,46 +1,56 @@
package com.youlai.mall.oms.enums;
import lombok.AllArgsConstructor;
import com.youlai.common.base.IBaseEnum;
import lombok.Getter;
/**
* @author huawei
* @desc
* @email huawei_code@163.com
* @date 2021/1/16
* 订单状态枚举
*
* @author haoxr
* @date 2022/11/28
*/
@AllArgsConstructor
public enum OrderStatusEnum {
public enum OrderStatusEnum implements IBaseEnum<Integer> {
PENDING_PAYMENT(101, "待支付"),
USER_CANCEL(102, "用户取消"),
AUTO_CANCEL(103, "系统自动取消"),
/**
* 1. 订单创建阶段
*/
PENDING_PAYMENT(10, "待支付"),
USER_CANCEL(11, "用户取消"),
AUTO_CANCEL(12, "系统自动取消"),
PAYED(201, "已支付"),
APPLY_REFUND(202, "申请退款"),
REFUNDED(203, "已退款"),
/**
* 2. 订单付款阶段
*/
PAYED(20, "已支付"),
APPLY_REFUND(21, "申请退款"),
REFUNDED(22, "已退款"),
DELIVERED(301, "已发货"),
/**
* 订单发货阶段
*/
DELIVERED(30, "已发货"),
USER_RECEIVE(401, "用户收货"),
AUTO_RECEIVE(402, "系统自动收货"),
/**
* 订单收货阶段
*/
USER_RECEIVE(40, "用户收货"),
AUTO_RECEIVE(41, "系统自动收货"),
FINISHED(901, "已完成");
@Getter
private Integer code;
@Getter
private String text;
/**
* 订单完结
*/
COMPLETED(99, "已完成");
public static OrderStatusEnum getValue(Integer code) {
for (OrderStatusEnum value : values()) {
if (value.getCode().equals(code)) {
return value;
}
}
return null;
OrderStatusEnum(int value, String label) {
this.value = value;
this.label = label;
}
@Getter
private Integer value;
@Getter
private String label;
}

View File

@ -48,22 +48,7 @@
<scope>test</scope>
</dependency>
<!-- 分布式事务 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>${seata.version}</version>
</dependency>
<dependency>
<groupId>com.youlai</groupId>
@ -104,6 +89,12 @@
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
</dependency>
<dependency>
<groupId>com.youlai</groupId>
<artifactId>common-seata</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.youlai.common.result.PageResult;
import com.youlai.common.result.Result;
import com.youlai.mall.oms.dto.OrderInfoDTO;
import com.youlai.mall.oms.dto.SeataOrderDTO;
import com.youlai.mall.oms.pojo.dto.OrderDTO;
import com.youlai.mall.oms.pojo.entity.OmsOrder;
import com.youlai.mall.oms.pojo.entity.OmsOrderItem;
@ -24,7 +25,7 @@ import java.util.Optional;
/**
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
* @date 2020/12/30
*/
@Api(tags = "「管理端」订单管理")
@RestController
@ -62,11 +63,11 @@ public class OmsOrderController {
return Result.success(orderDTO);
}
@ApiOperation(value = "修改订单状态", notes = "实验室模拟接口", hidden = true)
@PutMapping("/{orderId}/status")
public Result updateOrderStatus(@PathVariable Long orderId, @RequestParam Integer status,@RequestParam Boolean orderEx) {
boolean result = orderService.updateOrderStatus(orderId, status,orderEx);
return Result.judge(result);
@ApiOperation(value = "「实验室」创建订单", notes = "实验室模拟接口", hidden = true)
@PostMapping
public Result<String> createOrder(@RequestBody SeataOrderDTO orderDTO, @RequestParam Boolean openEx) {
String orderSn = orderService.createOrder(orderDTO, openEx);
return Result.success(orderSn);
}
@ApiOperation(value = "获取订单信息", notes = "实验室模拟接口", hidden = true)

View File

@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.youlai.mall.oms.dto.OrderInfoDTO;
import com.youlai.mall.oms.dto.SeataOrderDTO;
import com.youlai.mall.oms.enums.PayTypeEnum;
import com.youlai.mall.oms.pojo.entity.OmsOrder;
import com.youlai.mall.oms.pojo.query.OrderPageQuery;
@ -36,7 +37,7 @@ public interface IOrderService extends IService<OmsOrder> {
/**
* 订单提交
*/
OrderSubmitVO submitOrder(OrderSubmitForm orderSubmitForm) ;
OrderSubmitVO submitOrder(OrderSubmitForm orderSubmitForm);
/**
* 订单支付
@ -84,18 +85,22 @@ public interface IOrderService extends IService<OmsOrder> {
*/
IPage<OmsOrder> listOrderPages(OrderPageQuery queryParams);
/**
* 修改订单状态
*
* @param orderId 订单ID
* @param status 订单状态
* @param orderEx 订单是否异常
* @return
*/
boolean updateOrderStatus(Long orderId, Integer status,Boolean orderEx);
/**
* 获取订单信息
* 实验室创建订单
* <p>
* 非商城业务
*
* @param orderDTO
* @param openEx
* @return
*/
String createOrder(SeataOrderDTO orderDTO, Boolean openEx);
/**
* 实验室获取订单信息
* <p>
* 非商城业务
*
* @param orderId
* @return

View File

@ -8,7 +8,6 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@ -21,15 +20,15 @@ import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.youlai.common.enums.BusinessTypeEnum;
import com.youlai.common.redis.BusinessNoGenerator;
import com.youlai.common.redis.BusinessSnGenerator;
import com.youlai.common.result.Result;
import com.youlai.common.security.util.SecurityUtils;
import com.youlai.common.web.exception.ApiException;
import com.youlai.mall.oms.config.WxPayProperties;
import com.youlai.mall.oms.dto.OrderInfoDTO;
import com.youlai.mall.oms.dto.SeataOrderDTO;
import com.youlai.mall.oms.enums.OrderStatusEnum;
import com.youlai.mall.oms.enums.OrderTypeEnum;
import com.youlai.mall.oms.enums.OrderSourceTypeEnum;
import com.youlai.mall.oms.enums.PayTypeEnum;
import com.youlai.mall.oms.mapper.OrderMapper;
import com.youlai.mall.oms.pojo.dto.CartItemDTO;
@ -58,7 +57,6 @@ import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
@ -78,9 +76,9 @@ import static com.youlai.mall.oms.constant.OmsConstants.*;
* @author haoxr
* @date 2022/2/12
*/
@RequiredArgsConstructor
@Slf4j
@Service
@Slf4j
@RequiredArgsConstructor
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> implements IOrderService {
private final WxPayProperties wxPayProperties;
@ -90,7 +88,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
private final StringRedisTemplate redisTemplate;
private final ThreadPoolExecutor threadPoolExecutor;
private final MemberFeignClient memberFeignClient;
private final BusinessNoGenerator businessNoGenerator;
private final BusinessSnGenerator businessSnGenerator;
private final SkuFeignClient skuFeignClient;
private final RedissonClient redissonClient;
private final WxPayService wxPayService;
@ -128,7 +126,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
CompletableFuture<Void> getOrderItemsFuture = CompletableFuture.runAsync(() -> {
// 请求参数传递给子线程
RequestContextHolder.setRequestAttributes(attributes);
List<OrderItemDTO> orderItems = this.getOrderItems(skuId,memberId);
List<OrderItemDTO> orderItems = this.getOrderItems(skuId, memberId);
orderConfirmVO.setOrderItems(orderItems);
}, threadPoolExecutor);
@ -147,7 +145,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
// 进入订单确认页面生成唯一token,订单提交根据此token判断是否重复提交
CompletableFuture<Void> getOrderTokenFuture = CompletableFuture.runAsync(() -> {
RequestContextHolder.setRequestAttributes(attributes);
String orderToken = businessNoGenerator.generate(BusinessTypeEnum.ORDER);
String orderToken = businessSnGenerator.generateSerialNo();
orderConfirmVO.setOrderToken(orderToken);
redisTemplate.opsForValue().set(ORDER_TOKEN_PREFIX + orderToken, orderToken);
}, threadPoolExecutor);
@ -186,7 +184,15 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
// 创建订单
order = new OmsOrder().setOrderSn(orderToken) // 把orderToken赋值给订单编号
.setStatus(OrderStatusEnum.PENDING_PAYMENT.getCode()).setSourceType(OrderTypeEnum.APP.getCode()).setMemberId(SecurityUtils.getMemberId()).setRemark(orderSubmitForm.getRemark()).setPayAmount(orderSubmitForm.getPayAmount()).setTotalQuantity(orderItems.stream().map(OrderItemDTO::getCount).reduce(0, Integer::sum)).setTotalAmount(orderItems.stream().map(item -> item.getPrice() * item.getCount()).reduce(0L, Long::sum));
.setStatus(OrderStatusEnum.PENDING_PAYMENT.getValue())
.setSourceType(OrderSourceTypeEnum.APP.getCode())
.setMemberId(SecurityUtils.getMemberId())
.setRemark(orderSubmitForm.getRemark())
.setPayAmount(orderSubmitForm.getPayAmount())
.setTotalQuantity(orderItems.stream()
.map(OrderItemDTO::getCount).reduce(0, Integer::sum))
.setTotalAmount(orderItems.stream().map(item -> item.getPrice() * item.getCount())
.reduce(0L, Long::sum));
boolean result = this.save(order);
// 添加订单明细
@ -225,7 +231,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
public <T> T pay(Long orderId, String appId, PayTypeEnum payTypeEnum) {
OmsOrder order = this.getById(orderId);
Assert.isTrue(order != null, "订单不存在");
Assert.isTrue(OrderStatusEnum.PENDING_PAYMENT.getCode().equals(order.getStatus()), "订单不可支付,请检查订单状态");
Assert.isTrue(OrderStatusEnum.PENDING_PAYMENT.getValue().equals(order.getStatus()), "订单不可支付,请检查订单状态");
RLock lock = redissonClient.getLock(ORDER_SN_PREFIX + order.getOrderSn());
try {
@ -266,7 +272,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
Assert.isTrue(Result.isSuccess(deductBalanceResult), "扣减账户余额失败");
// 更新订单状态
order.setStatus(OrderStatusEnum.PAYED.getCode());
order.setStatus(OrderStatusEnum.PAYED.getValue());
order.setPayType(PayTypeEnum.BALANCE.getValue());
order.setPayTime(new Date());
this.updateById(order);
@ -318,7 +324,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
public boolean closeOrder(String orderToken) {
log.info("订单超时取消orderToken:{}", orderToken);
OmsOrder order = this.getOne(new LambdaQueryWrapper<OmsOrder>().eq(OmsOrder::getOrderSn, orderToken));
if (order == null || !OrderStatusEnum.PENDING_PAYMENT.getCode().equals(order.getStatus())) {
if (order == null || !OrderStatusEnum.PENDING_PAYMENT.getValue().equals(order.getStatus())) {
return false;
}
// 如果已经有outTradeNo了就先进行关单
@ -331,7 +337,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
throw new ApiException("微信关单异常");
}
}
order.setStatus(OrderStatusEnum.AUTO_CANCEL.getCode());
order.setStatus(OrderStatusEnum.AUTO_CANCEL.getValue());
return this.updateById(order);
}
@ -343,7 +349,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
throw new ApiException("订单不存在");
}
if (!OrderStatusEnum.PENDING_PAYMENT.getCode().equals(order.getStatus())) {
if (!OrderStatusEnum.PENDING_PAYMENT.getValue().equals(order.getStatus())) {
throw new ApiException("取消失败,订单状态不支持取消"); // 通过自定义异常将异常信息抛出由异常处理器捕获显示给前端页面
}
// 如果已经有outTradeNo了就先进行关单
@ -356,7 +362,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
throw new ApiException("微信关单异常");
}
}
order.setStatus(OrderStatusEnum.USER_CANCEL.getCode());
order.setStatus(OrderStatusEnum.USER_CANCEL.getValue());
boolean result = this.updateById(order);
if (result) {
// 释放被锁定的库存
@ -369,11 +375,11 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteOrder(Long id) {
log.info("=======================订单删除订单ID{}=======================", id);
OmsOrder order = this.getById(id);
if (order != null && !OrderStatusEnum.AUTO_CANCEL.getCode().equals(order.getStatus()) && !OrderStatusEnum.USER_CANCEL.getCode().equals(order.getStatus())) {
if (order != null && !OrderStatusEnum.AUTO_CANCEL.getValue().equals(order.getStatus())
&& !OrderStatusEnum.USER_CANCEL.getValue().equals(order.getStatus())) {
throw new ApiException("订单删除失败,订单不存在或订单状态不支持删除");
}
return this.removeById(id);
@ -387,12 +393,12 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
final WxPayOrderNotifyV3Result.DecryptNotifyResult result = this.wxPayService.parseOrderNotifyV3Result(notifyData, signatureHeader).getResult();
log.debug("支付通知解密成功:[{}]", result.toString());
// 根据商户订单号查询订单
QueryWrapper<OmsOrder> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(OmsOrder::getOutTradeNo, result.getOutTradeNo());
OmsOrder orderDO = this.getOne(wrapper);
OmsOrder orderDO = this.getOne(new LambdaQueryWrapper<OmsOrder>()
.eq(OmsOrder::getOutTradeNo, result.getOutTradeNo())
);
// 支付成功处理
if (WxPayConstants.WxpayTradeStatus.SUCCESS.equals(result.getTradeState())) {
orderDO.setStatus(OrderStatusEnum.PAYED.getCode());
orderDO.setStatus(OrderStatusEnum.PAYED.getValue());
orderDO.setTransactionId(result.getTransactionId());
orderDO.setPayTime(new Date());
this.updateById(orderDO);
@ -414,7 +420,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
OmsOrder orderDO = this.getOne(wrapper);
// 退款成功处理
if (WxPayConstants.RefundStatus.SUCCESS.equals(result.getRefundStatus())) {
orderDO.setStatus(OrderStatusEnum.REFUNDED.getCode());
orderDO.setStatus(OrderStatusEnum.REFUNDED.getValue());
orderDO.setRefundId(result.getRefundId());
this.updateById(orderDO);
}
@ -455,7 +461,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
* @param skuId 直接购买必有值购物车结算必没值
* @return
*/
private List<OrderItemDTO> getOrderItems(Long skuId,Long memberId) {
private List<OrderItemDTO> getOrderItems(Long skuId, Long memberId) {
List<OrderItemDTO> orderItems;
if (skuId != null) { // 直接购买
orderItems = new ArrayList<>();
@ -486,7 +492,12 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
LockStockDTO lockStockDTO = new LockStockDTO();
lockStockDTO.setOrderToken(orderToken);
List<LockStockDTO.LockedSku> lockedSkuList = orderItems.stream().map(orderItem -> new LockStockDTO.LockedSku().setSkuId(orderItem.getSkuId()).setCount(orderItem.getCount())).collect(Collectors.toList());
List<LockStockDTO.LockedSku> lockedSkuList = orderItems.stream()
.map(orderItem -> new LockStockDTO
.LockedSku()
.setSkuId(orderItem.getSkuId())
.setCount(orderItem.getCount()))
.collect(Collectors.toList());
lockStockDTO.setLockedSkuList(lockedSkuList);
skuFeignClient.lockStock(lockStockDTO);
@ -494,26 +505,42 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
/**
* 实验室修改订单状态
* 实验室创建订单
* <p>
* 非商城业务
*
* @param orderId 订单ID
* @param status 订单状态
* @param orderEx 订单是否异常
* @param orderDTO
* @param openEx
* @return
*/
@Override
@Transactional
public boolean updateOrderStatus(Long orderId, Integer status, Boolean orderEx) {
boolean result = this.update(new LambdaUpdateWrapper<OmsOrder>().eq(OmsOrder::getId, orderId).set(OmsOrder::getStatus, status));
public String createOrder(SeataOrderDTO orderDTO, Boolean openEx) {
if (orderEx) {
// 扣减账户越
Long memberId = orderDTO.getMemberId();
Long amount = orderDTO.getAmount();
memberFeignClient.deductBalance(memberId, amount);
// 生成订单
String orderSn = businessSnGenerator.generateSerialNo();
new OmsOrder().setOrderSn(orderSn)
.setStatus(OrderStatusEnum.COMPLETED.getValue())
.setSourceType(OrderSourceTypeEnum.APP.getCode())
.setMemberId(memberId)
.setPayAmount(amount)
.setTotalQuantity(1)
.setTotalAmount(amount);
// 开启异常制造运行时异常
if (openEx) {
int i = 1 / 0;
}
return result;
return orderSn;
}
/**
* 获取订单信息
* 实验室获取订单信息
* <p>
* 非商城业务
*
* @param orderId
* @return

View File

@ -58,10 +58,10 @@ public interface SkuFeignClient {
* 实验室扣减商品库存
*
* @param skuId
* @param num 扣减数量
* @param count 扣减数量
* @return
*/
@PutMapping("/api/v1/sku/{skuId}/stock/_deduct")
Result deductStock(@PathVariable Long skuId, @RequestParam Integer num);
Result deductStock(@PathVariable Long skuId, @RequestParam Integer count);
}

View File

@ -11,6 +11,12 @@ import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 会员Feign客户端
*
* @author haoxr
* @date 2022/11/29
*/
@FeignClient(name = "mall-ums", contextId = "member")
public interface MemberFeignClient {
@ -61,7 +67,6 @@ public interface MemberFeignClient {
@GetMapping("/app-api/v1/members/mobile/{mobile}")
Result<MemberAuthDTO> loadUserByMobile(@PathVariable String mobile);
/**
* 获取会员地址列表
*
@ -71,16 +76,6 @@ public interface MemberFeignClient {
@GetMapping("/app-api/v1/members/{memberId}/addresses")
Result<List<MemberAddressDTO>> listMemberAddresses(@PathVariable Long memberId);
/**
* 实验室修改会员余额
*
* @param memberId
* @param balance 余额(单位:)
* @return
*/
@PutMapping("/api/v1/members/{memberId}/balance")
Result updateBalance(@PathVariable Long memberId, @RequestParam Integer balance);
/**
* 实验室扣减会员余额
*

View File

@ -81,17 +81,6 @@ public class UmsMemberController {
return Result.judge(status);
}
@ApiOperation(value = "修改会员余额", notes = "实验室模拟", hidden = true)
@PutMapping(value = "/{memberId}/balance")
public Result updateBalance(
@PathVariable Long memberId,
@RequestParam Long balance
) {
boolean result = memberService.updateBalance(memberId, balance);
return Result.judge(result);
}
@ApiOperation(value = "扣减会员余额", notes = "实验室模拟", hidden = true)
@PutMapping(value = "/{memberId}/balance/_deduct")
public Result deductBalance(

View File

@ -276,6 +276,12 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.youlai</groupId>
<artifactId>common-seata</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>

View File

@ -1,28 +0,0 @@
package com.youlai.common.enums;
import com.youlai.common.base.IBaseEnum;
import lombok.Getter;
/**
* 业务类型枚举
*
* @author haoxr
* @date 2021-02-17
*/
public enum BusinessTypeEnum implements IBaseEnum<Integer> {
USER(100, "用户"),
MEMBER(200, "会员"),
ORDER(300, "订单");
@Getter
private Integer value;
@Getter
private String label;
BusinessTypeEnum(Integer value, String label) {
this.value = value;
this.label = label;
}
}

View File

@ -1,7 +1,6 @@
package com.youlai.common.redis;
import com.youlai.common.constant.RedisConstants;
import com.youlai.common.enums.BusinessTypeEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
@ -14,26 +13,24 @@ import java.time.format.DateTimeFormatter;
@Component
@Slf4j
@RequiredArgsConstructor
public class BusinessNoGenerator {
public class BusinessSnGenerator {
private final RedisTemplate redisTemplate;
/**
* @param businessType 业务类型枚举
* @param digit 业务序号位数
* @return
*/
public String generate(BusinessTypeEnum businessType, Integer digit) {
public String generateSerialNo(Integer digit) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
String date = LocalDateTime.now(ZoneOffset.of("+8")).format(formatter);
String key = RedisConstants.BUSINESS_NO_PREFIX + businessType.getValue() + ":" + date;
String key = RedisConstants.BUSINESS_NO_PREFIX +":" + date;
Long increment = redisTemplate.opsForValue().increment(key);
return date + businessType.getValue() + String.format("%0" + digit + "d", increment);
return date + String.format("%0" + digit + "d", increment);
}
public String generate(BusinessTypeEnum businessType) {
Integer defaultDigit = 6;
return generate(businessType, defaultDigit);
public String generateSerialNo(){
return this.generateSerialNo(6);
}
}

View File

@ -1,6 +1,6 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.youlai.common.redis.RedisConfig,\
com.youlai.common.redis.BusinessNoGenerator,\
com.youlai.common.redis.BusinessSnGenerator,\
com.youlai.common.redis.RedissonConfig,\
com.youlai.common.redis.RedisCacheConfig

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>youlai-common</artifactId>
<groupId>com.youlai</groupId>
<version>2.1.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common-seata</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>${seata.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -24,5 +24,6 @@
<module>common-sms</module>
<module>common-file</module>
<module>common-security</module>
<module>common-seata</module>
</modules>
</project>