refactor: Seata实验室案例由商品购买换位订单支付流程

This commit is contained in:
haoxr 2022-12-04 01:35:06 +08:00
parent a909075175
commit a46173d3c1
19 changed files with 138 additions and 136 deletions

View File

@ -15,8 +15,8 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
* @date 2021/11/29 0029 22:50
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackageClasses = {OrderFeignClient.class, SkuFeignClient.class, MemberFeignClient.class})
@EnableDiscoveryClient
public class LaboratoryApplication {
public static void main(String[] args) {
SpringApplication.run(LaboratoryApplication.class, args);

View File

@ -27,29 +27,29 @@ public class SeataController {
@ApiOperation("获取模拟数据")
@GetMapping("/data")
public Result getData(String orderSn) {
SeataVO result = seataService.getData(orderSn);
public Result getData() {
SeataVO result = seataService.getData();
return Result.success(result);
}
@ApiOperation("重置模拟数据")
@PutMapping("/data/_reset")
public Result resetData(String orderSn) {
boolean result = seataService.resetData(orderSn);
public Result resetData() {
boolean result = seataService.resetData();
return Result.success(result);
}
@ApiOperation("购买商品")
@PostMapping("/_purchase")
public Result purchaseGoods(@RequestBody SeataForm seataForm) {
public Result payOrder(@RequestBody SeataForm seataForm) {
boolean openTx = seataForm.isOpenTx();
String orderSn = null;
if (openTx) {
// 开启全局事务
orderSn = seataService.purchaseGoodsWithGlobalTx(seataForm);
orderSn = seataService.payOrderWithGlobalTx(seataForm);
} else {
orderSn = seataService.purchaseGoods(seataForm);
orderSn = seataService.payOrder(seataForm);
}
return Result.success(orderSn);
}

View File

@ -11,16 +11,6 @@ import lombok.Data;
@Data
public class SeataForm {
/**
* 会员ID
*/
private Long memberId;
/**
* 商品ID
*/
private Long skuId;
/**
* 订单金额
*/

View File

@ -14,26 +14,26 @@ public interface SeataService {
*
* @return
*/
SeataVO getData(String orderSn);
SeataVO getData();
/**
* 重置模拟数据
*
* @return
*/
boolean resetData(String orderSn);
boolean resetData();
/**
* 购买商品
*
* @return 订单号
*/
String purchaseGoods(SeataForm seataForm);
String payOrder(SeataForm seataForm);
/**
* 购买商品(全局事务)
*
* @return 订单号
*/
String purchaseGoodsWithGlobalTx(SeataForm seataForm);
boolean payOrderWithGlobalTx(SeataForm seataForm);
}

View File

@ -1,7 +1,6 @@
package com.youlai.laboratory.seata.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.youlai.common.result.Result;
import com.youlai.laboratory.seata.pojo.form.SeataForm;
import com.youlai.laboratory.seata.pojo.vo.SeataVO;
@ -33,8 +32,11 @@ public class SeataServiceImpl implements SeataService {
private final OrderFeignClient orderFeignClient;
private final MemberFeignClient memberFeignClient;
private static Long skuId = 1l; // SkuID
private static Long memberId = 1l; // 会员ID
private static Long orderId = 1l;// 订单ID
/**
* 获取模拟数据
@ -42,7 +44,9 @@ public class SeataServiceImpl implements SeataService {
* @return
*/
@Override
public SeataVO getData(String orderSn) {
public SeataVO getData() {
SeataVO seataVO = new SeataVO();
SkuInfoDTO skuInfoDTO = skuFeignClient.getSkuInfo(skuId).getData();
@ -57,10 +61,8 @@ public class SeataServiceImpl implements SeataService {
seataVO.setAccountInfo(accountInfo);
SeataVO.OrderInfo orderInfo = new SeataVO.OrderInfo();
if(StrUtil.isNotBlank(orderSn)){
OrderInfoDTO orderInfoDTO = orderFeignClient.getOrderInfo(orderSn).getData();
OrderInfoDTO orderInfoDTO = orderFeignClient.getOrderInfo(orderId).getData();
BeanUtil.copyProperties(orderInfoDTO, orderInfo);
}
seataVO.setOrderInfo(orderInfo);
return seataVO;
@ -72,61 +74,55 @@ public class SeataServiceImpl implements SeataService {
* @return
*/
@Override
public boolean resetData(String orderSn) {
public boolean resetData() {
skuFeignClient.resetStock(skuId); // 还原库存
memberFeignClient.resetBalance(memberId); // 还原余额
if (StrUtil.isNotBlank(orderSn)) {
orderFeignClient.deleteOrder(orderSn); // 删除订单
}
orderFeignClient.resetOrder(orderId); // 删除订单
return true;
}
/**
* 购买商品
*
* @return 订单号
* 订单支付
*/
@Override
public String purchaseGoods(SeataForm seataForm) {
public String payOrder(SeataForm seataForm) {
log.info("========扣减商品库存(全局事务)========");
skuFeignClient.deductStock(seataForm.getSkuId(), 1); // 扣减库存
skuFeignClient.deductStock(skuId, 1); // 扣减库存
log.info("========创建订单(全局事务)========");
SeataOrderDTO seataOrderDTO = new SeataOrderDTO(
seataForm.getMemberId(),
seataForm.getSkuId(),
seataForm.getAmount()
memberId,
skuId,
seataForm.getAmount(),
seataForm.isOpenEx()
);
boolean openEx = seataForm.isOpenEx(); // 是否开启异常
Result<String> result = orderFeignClient.createOrder(seataOrderDTO, openEx);
Result<String> result = orderFeignClient.payOrder(orderId, seataOrderDTO);
String orderSn = result.getData();
return orderSn;
}
/**
* 购买商品(全局事务)
*
* @return 订单号
* 订单支付(全局事务)
*/
@GlobalTransactional
@Override
public String purchaseGoodsWithGlobalTx(SeataForm seataForm) {
@GlobalTransactional
public boolean payOrderWithGlobalTx(SeataForm seataForm) {
log.info("========扣减商品库存(全局事务)========");
skuFeignClient.deductStock(seataForm.getSkuId(), 1); // 扣减库存
log.info("========扣减商品库存========");
skuFeignClient.deductStock(skuId, 1); // 扣减库存
log.info("========创建订单(全局事务)========");
log.info("========订单支付========");
SeataOrderDTO seataOrderDTO = new SeataOrderDTO(
seataForm.getMemberId(),
seataForm.getSkuId(),
seataForm.getAmount()
memberId,
skuId,
seataForm.getAmount(),
seataForm.isOpenEx()
);
boolean openEx = seataForm.isOpenEx(); // 是否开启异常
Result<String> result = orderFeignClient.createOrder(seataOrderDTO, openEx);
String orderSn = result.getData();
return orderSn;
orderFeignClient.payOrder(orderId, seataOrderDTO);
return true;
}
}

View File

@ -7,10 +7,10 @@ import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
/**
* 订单Feign客户端
* 订单 Feign Client
*
* @author haoxr
* @createTime 2021/3/13 11:59
* @date 2021/3/13
*/
@FeignClient(value = "mall-oms", contextId = "order")
public interface OrderFeignClient {
@ -18,27 +18,25 @@ public interface OrderFeignClient {
/**
* 实验室获取订单信息
*
* @param orderSn
* @param orderId
* @return
*/
@GetMapping("/api/v1/orders/orderSn/{orderSn}")
Result<OrderInfoDTO> getOrderInfo(@PathVariable String orderSn);
@GetMapping("/api/v1/orders/{orderId}/orderInfo")
Result<OrderInfoDTO> getOrderInfo(@PathVariable Long orderId);
/**
* 实验室创建订单
* 实验室订单支付
*
* @param orderDTO
* @param openEx 是否出现异常
* @return
*/
@PostMapping("/api/v1/orders")
Result<String> createOrder(@RequestBody SeataOrderDTO orderDTO, @RequestParam boolean openEx);
@PutMapping("/api/v1/orders/{orderId}/_pay")
Result<String> payOrder(@PathVariable Long orderId, @RequestBody SeataOrderDTO orderDTO);
/**
* 实验室删除订单
* 实验室订单重置
*/
@DeleteMapping("/api/v1/orders/orderSn/{orderSn}")
Result deleteOrder(@PathVariable String orderSn);
@PutMapping("/api/v1/orders/{orderId}/_reset")
Result resetOrder(@PathVariable Long orderId);
}

View File

@ -25,4 +25,7 @@ public class SeataOrderDTO {
private Long amount;
private Boolean openEx;
}

View File

@ -4,11 +4,12 @@ import com.youlai.mall.pms.api.SkuFeignClient;
import com.youlai.mall.ums.api.MemberFeignClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@EnableFeignClients(basePackageClasses = { MemberFeignClient.class, SkuFeignClient.class})
@EnableTransactionManagement

View File

@ -1,10 +1,13 @@
package com.youlai.mall.oms.controller.admin;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
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.enums.OrderStatusEnum;
import com.youlai.mall.oms.pojo.dto.OrderDTO;
import com.youlai.mall.oms.pojo.entity.OmsOrder;
import com.youlai.mall.oms.pojo.entity.OmsOrderItem;
@ -18,6 +21,7 @@ import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@ -57,37 +61,40 @@ public class OmsOrderController {
List<OmsOrderItem> orderItems = orderItemService.list(
new LambdaQueryWrapper<OmsOrderItem>().eq(OmsOrderItem::getOrderId, orderId)
);
orderItems = Optional.ofNullable(orderItems).orElse(new ArrayList<>());
orderItems = Optional.ofNullable(orderItems).orElse(Collections.EMPTY_LIST);
orderDTO.setOrder(order).setOrderItems(orderItems);
return Result.success(orderDTO);
}
@ApiOperation(value = "「实验室」获取订单信息", hidden = true)
@GetMapping("/orderSn/{orderSn}")
public Result<OmsOrder> getOrderInfo(
@ApiParam("订单ID") @PathVariable String orderSn
@GetMapping("/{orderId}/orderInfo")
public Result<OrderInfoDTO> getOrderInfo(
@ApiParam("订单ID") @PathVariable Long orderId
) {
OmsOrder orderInfo = orderService.getOne(new LambdaQueryWrapper<OmsOrder>()
.eq(OmsOrder::getOrderSn, orderSn)
.select(OmsOrder::getOrderSn, OmsOrder::getStatus)
.last("limit 1")
);
OrderInfoDTO orderInfo = new OrderInfoDTO();
OmsOrder order = orderService.getById(orderId);
if (order != null) {
orderInfo.setOrderSn(order.getOrderSn());
orderInfo.setStatus(order.getStatus());
}
return Result.success(orderInfo);
}
@ApiOperation(value = "「实验室」创建订单", 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 = "「实验室」订单支付", hidden = true)
@PutMapping("/{orderId}/_pay")
public Result payOrder(@PathVariable Long orderId, @RequestBody SeataOrderDTO orderDTO) {
Boolean result = orderService.payOrder(orderId, orderDTO);
return Result.judge(result);
}
@ApiOperation(value = "「实验室」删除订单", hidden = true)
@DeleteMapping("/orderSn/{orderSn}")
public Result deleteOrder(@PathVariable String orderSn) {
boolean result = orderService.remove(new LambdaQueryWrapper<OmsOrder>()
.eq(OmsOrder::getOrderSn, orderSn)
@ApiOperation(value = "「实验室」订单重置", hidden = true)
@PutMapping("/{orderId}/_reset")
public Result resetOrder(@PathVariable Long orderId) {
boolean result = orderService.update(new LambdaUpdateWrapper<OmsOrder>()
.eq(OmsOrder::getId, orderId)
.set(OmsOrder::getStatus, OrderStatusEnum.WAIT_PAY.getValue())
);
return Result.judge(result);
}

View File

@ -39,11 +39,12 @@ public class OmsOrder extends BaseEntity {
*/
private Integer totalQuantity;
/**
* 订单来源[0->PC订单1->app订单]
* 订单来源(0-PC订单1-app订单)
*/
private Integer sourceType;
/**
* 订单状态101->待付款102->用户取消103->系统取消201->已付款202->申请退款203->已退款301->待发货401->已发货501->用户收货502->系统收货901->已完成
* 订单状态(1-待付款;2-待发货;3-已发货;4-已完成;5-已关闭;6-已取消;)
*/
private Integer status;
/**

View File

@ -5,6 +5,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.sun.org.apache.xpath.internal.operations.Bool;
import com.youlai.mall.oms.dto.SeataOrderDTO;
import com.youlai.mall.oms.enums.PayTypeEnum;
import com.youlai.mall.oms.pojo.entity.OmsOrder;
@ -86,15 +87,14 @@ public interface IOrderService extends IService<OmsOrder> {
/**
* 实验室创建订单
* 实验室订单支付
* <p>
* 非商城业务
*
* @param orderDTO
* @param openEx
* @return
*/
String createOrder(SeataOrderDTO orderDTO, Boolean openEx);
Boolean payOrder(Long orderId, SeataOrderDTO orderDTO);
}

View File

@ -8,6 +8,7 @@ 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;
@ -47,6 +48,7 @@ import com.youlai.mall.pms.pojo.dto.SkuInfoDTO;
import com.youlai.mall.pms.pojo.dto.LockStockDTO;
import com.youlai.mall.ums.api.MemberFeignClient;
import com.youlai.mall.ums.dto.MemberAddressDTO;
import io.seata.core.context.RootContext;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -242,7 +244,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
result = (T) wxJsapiPay(appId, order);
break;
default:
result = (T)balancePay(order);
result = (T) balancePay(order);
break;
}
// 扣减库存
@ -268,7 +270,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
// 扣减余额
Long memberId = SecurityUtils.getMemberId();
Long amount = order.getPayAmount();
Result<?> deductBalanceResult = memberFeignClient.deductBalance(memberId,amount);
Result<?> deductBalanceResult = memberFeignClient.deductBalance(memberId, amount);
Assert.isTrue(Result.isSuccess(deductBalanceResult), "扣减账户余额失败");
// 更新订单状态
@ -504,37 +506,38 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, OmsOrder> impleme
/**
* 实验室创建订单
* 实验室订单支付
* <p>
* 非商城业务
*
* @param orderDTO
* @param openEx
* @return
*/
@Override
public String createOrder(SeataOrderDTO orderDTO, Boolean openEx) {
public Boolean payOrder(Long orderId, SeataOrderDTO orderDTO) {
// 扣减账户越
Long memberId = orderDTO.getMemberId();
Long amount = orderDTO.getAmount();
// 扣减账户余额
memberFeignClient.deductBalance(memberId, amount);
// 开启异常
// 是否开启异常
Boolean openEx = orderDTO.getOpenEx();
if (openEx) {
int i = 1 / 0;
}
// 生成订单
// 修改订单已支付
String orderSn = businessSnGenerator.generateSerialNo();
new OmsOrder().setOrderSn(orderSn)
.setStatus(OrderStatusEnum.FINISHED.getValue())
.setSourceType(OrderSourceTypeEnum.APP.getCode())
.setMemberId(memberId)
.setPayAmount(amount)
.setTotalQuantity(1)
.setTotalAmount(amount);
return orderSn;
boolean result = this.update(new LambdaUpdateWrapper<OmsOrder>()
.eq(OmsOrder::getId, orderId)
.set(OmsOrder::getOrderSn, orderSn)
.set(OmsOrder::getStatus, OrderStatusEnum.WAIT_SHIPPING.getValue())
);
return result;
}
}

View File

@ -14,7 +14,7 @@ public class RabbitMQTest {
private RabbitTemplate rabbitTemplate;
@Test
public void createOrderTest() {
public void payOrderTest() {
rabbitTemplate.convertAndSend("order.exchange", "order.create.routing.key", "4acd475a-c6aa-4d9a-a3a5-40da7472cbee");
}
}

View File

@ -8,10 +8,9 @@ import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients(basePackageClasses = {MemberFeignClient.class})
@EnableTransactionManagement
@EnableDiscoveryClient
public class PmsApplication {
public static void main(String[] args) {
SpringApplication.run(PmsApplication.class, args);

View File

@ -5,7 +5,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@SpringBootApplication
@EnableDiscoveryClient
public class UmsApplication {
public static void main(String[] args) {

View File

@ -37,15 +37,6 @@ public class UmsMemberController {
return PageResult.success(result);
}
@ApiOperation(value = "获取会员信息")
@GetMapping("/{memberId}/info")
public Result<MemberInfoDTO> getMemberInfo(
@ApiParam("会员ID") @PathVariable Long memberId
) {
MemberInfoDTO memberInfoDTO = memberService.getMemberInfo(memberId);
return Result.success(memberInfoDTO);
}
@ApiOperation(value = "修改会员")
@PutMapping(value = "/{memberId}")
public <T> Result<T> update(
@ -81,4 +72,23 @@ public class UmsMemberController {
return Result.judge(status);
}
@ApiOperation(value = "「实验室」获取会员信息")
@GetMapping("/{memberId}/info")
public Result<MemberInfoDTO> getMemberInfo(
@ApiParam("会员ID") @PathVariable Long memberId
) {
MemberInfoDTO memberInfoDTO = memberService.getMemberInfo(memberId);
return Result.success(memberInfoDTO);
}
@ApiOperation(value = "「实验室」扣减会员余额")
@PutMapping("/{memberId}/balances/_deduct")
public Result deductBalance(@PathVariable Long memberId, @RequestParam Long amount) {
boolean result = memberService.update(new LambdaUpdateWrapper<UmsMember>()
.setSql("balance = balance - " + amount)
.eq(UmsMember::getId, memberId));
return Result.judge(result);
}
}

View File

@ -55,7 +55,7 @@ public class MemberController {
@ApiOperation(value = "扣减会员余额")
@PutMapping("/{memberId}/balances/_deduct")
public <T> Result<T> deductBalance(@PathVariable Long memberId, @RequestParam Long amount) {
public Result deductBalance(@PathVariable Long memberId, @RequestParam Long amount) {
boolean result = memberService.update(new LambdaUpdateWrapper<UmsMember>()
.setSql("balance = balance - " + amount)
.eq(UmsMember::getId, memberId));
@ -89,12 +89,7 @@ public class MemberController {
return Result.success(memberAuthInfo);
}
/**
* 根据手机号获取会员认证信息
*
* @param mobile 手机号码
* @return
*/
@ApiOperation(value = "根据手机号获取会员认证信息",hidden = true)
@GetMapping("/mobile/{mobile}")
public Result<MemberAuthDTO> getMemberByMobile(
@ApiParam("手机号码") @PathVariable String mobile

View File

@ -95,5 +95,4 @@ public interface IUmsMemberService extends IService<UmsMember> {
MemberInfoDTO getMemberInfo(Long memberId);
}

View File

@ -15,9 +15,9 @@ public enum DataScopeEnum implements IBaseEnum<Integer> {
* value 越小数据权限范围越大
*/
ALL(0, "所有数据"),
DEPT_AND_SUB(10, "部门及子部门数据"),
DEPT(20, "本部门数据"),
SELF(30, "本人数据");
DEPT_AND_SUB(1, "部门及子部门数据"),
DEPT(2, "本部门数据"),
SELF(3, "本人数据");
@Getter
private Integer value;