文章目录
1、提交订单 业务 2、在 OrderController 创建 submitOrder 方法 3、 在 OrderServiceImpl 中实现 submitOrder 方法 4、根据id查询sku详情(service-product") 5、查询用户地址保存到订单项中(service-user) 6、删除购物车选中的购物项接口(service-cart) 7、验价验库存 8、创建订单持久化到mysql 9、清空选中的购物车
2、立即购买,不经过购物车 2.1、在 OrderController 创建 buy方法 2.2、在 OrderServiceImpl 中实现 buy 方法
3、订单幂等性校验分析
1、提交订单 业务
2、在 OrderController 创建 submitOrder 方法
@RestController
@Tag ( name = "订单管理模块" , description = "订单管理模块" )
@RequestMapping ( "/api/order/orderInfo" )
public class OrderController {
@Operation ( summary = "提交订单" )
@PostMapping ( "/auth/submitOrder" )
public Result submitOrder ( @RequestBody OrderSubmitDto orderSubmitDto) {
Long orderId = orderService. submitOrder ( orderSubmitDto) ;
return Result. ok ( orderId) ;
}
}
3、 在 OrderServiceImpl 中实现 submitOrder 方法
@Service
public class OrderServiceImpl extends ServiceImpl < OrderMapper , OrderInfo > implements OrderService {
@Resource
private CartClient cartClient;
@Resource
private ProductClient productClient;
@Resource
private UserClient userClient;
@Resource
private OrderItemService orderItemService;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Transactional ( rollbackFor = Exception . class )
@Override
public Long submitOrder ( OrderSubmitDto orderSubmitDto) {
List < OrderItem > orderItemList = orderSubmitDto. getOrderItemList ( ) ;
if ( CollectionUtils . isEmpty ( orderItemList) ) {
throw new SpzxException ( ResultCodeEnum . FAIL , null ) ;
}
Long token = orderItemList. get ( 0 ) . getOrderId ( ) ;
Boolean delete = stringRedisTemplate. delete ( "spzx:order:" + token. toString ( ) ) ;
if ( ! delete) {
throw new SpzxException ( ResultCodeEnum . ORDER_SUBMIT_REPEAT , null ) ;
}
orderItemList. forEach ( orderItem -> {
Result < ProductSku > skuResult = productClient. getById ( orderItem. getSkuId ( ) ) ;
if ( skuResult. getCode ( ) != 200 ) {
throw new SpzxException ( ResultCodeEnum . FAIL , null ) ;
}
ProductSku sku = skuResult. getData ( ) ;
if ( sku. getSalePrice ( ) . compareTo ( orderItem. getSkuPrice ( ) ) != 0 ) {
throw new SpzxException ( ResultCodeEnum . ORDER_ITEM_PRICE_ERROR , null ) ;
}
if ( sku. getStockNum ( ) < orderItem. getSkuNum ( ) ) {
throw new SpzxException ( ResultCodeEnum . ORDER_ITEM_STOCK_ERROR , null ) ;
}
} ) ;
Result < UserAddress > userAddressResult = userClient. findUserAddressById ( orderSubmitDto. getUserAddressId ( ) ) ;
if ( userAddressResult. getCode ( ) != 200 ) {
throw new SpzxException ( ResultCodeEnum . FAIL , null ) ;
}
OrderInfo orderInfo = new OrderInfo ( ) ;
UserInfo userInfo = SpzxServiceAuthInterceptor . THREAD_LOCAL . get ( ) ;
orderInfo. setUserId ( userInfo. getId ( ) ) ;
orderInfo. setNickName ( userInfo. getNickName ( ) ) ;
String orderNo = IdUtil . getSnowflake ( 1 , 1 ) . nextIdStr ( ) ;
orderInfo. setOrderNo ( orderNo) ;
orderInfo. setCouponId ( 0L ) ;
BigDecimal totalAmount = orderItemList. stream ( ) . map ( orderItem -> {
return orderItem. getSkuPrice ( ) . multiply ( new BigDecimal ( orderItem. getSkuNum ( ) ) ) ;
} ) . reduce ( BigDecimal :: add ) . get ( ) ;
orderInfo. setTotalAmount ( totalAmount) ;
orderInfo. setCouponAmount ( new BigDecimal ( "0" ) ) ;
orderInfo. setOriginalTotalAmount ( totalAmount) ;
orderInfo. setFreightFee ( orderSubmitDto. getFreightFee ( ) ) ;
orderInfo. setOrderStatus ( 0 ) ;
UserAddress userAddress = userAddressResult. getData ( ) ;
orderInfo. setReceiverName ( userAddress. getName ( ) ) ;
orderInfo. setReceiverPhone ( userAddress. getPhone ( ) ) ;
orderInfo. setReceiverTagName ( userAddress. getTagName ( ) ) ;
orderInfo. setReceiverProvince ( userAddress. getProvinceCode ( ) ) ;
orderInfo. setReceiverCity ( userAddress. getCityCode ( ) ) ;
orderInfo. setReceiverDistrict ( userAddress. getDistrictCode ( ) ) ;
orderInfo. setReceiverAddress ( userAddress. getAddress ( ) ) ;
orderInfo. setPaymentTime ( new Date ( ) ) ;
orderInfo. setDeliveryTime ( new Date ( ) ) ;
orderInfo. setReceiveTime ( new Date ( ) ) ;
orderInfo. setCancelTime ( new Date ( ) ) ;
orderInfo. setCancelReason ( "" ) ;
orderInfo. setRemark ( orderSubmitDto. getRemark ( ) ) ;
this . save ( orderInfo) ;
Long orderId = orderInfo. getId ( ) ;
orderItemList. forEach ( orderItem -> {
orderItem. setOrderId ( orderId) ;
} ) ;
orderItemService. saveBatch ( orderItemList) ;
cartClient. delCheckedCartInfos ( ) ;
return orderId;
}
}
4、根据id查询sku详情(service-product")
@RestController
@Tag ( name = "商品模块" )
@RequestMapping ( value = "/api/product" )
public class ProductController {
@Operation ( summary = "根据id查询sku商品详情" )
@GetMapping ( "getById/{skuId}" )
public Result getById ( @PathVariable ( "skuId" ) Long skuId) {
ProductSku productSku = productSkuService. getById ( skuId) ;
return Result. ok ( productSku) ;
}
}
@FeignClient ( value = "service-product" )
public interface ProductClient {
@GetMapping ( "/api/product/getById/{skuId}" )
public Result< ProductSku> getById ( @PathVariable ( "skuId" ) Long skuId) ;
@PutMapping ( "/api/product/updateSkuSaleAndStock/{skuId}/{skuNum}" )
public Result updateSkuSaleAndStock ( @PathVariable Long skuId, @PathVariable Integer skuNum) ;
}
5、查询用户地址保存到订单项中(service-user)
@RestController
@Tag ( name = "用户地址管理模块" , description = "用户地址管理模块" )
@RequestMapping ( "/api/user/userAddress" )
public class UserAddressController {
@Operation ( summary = "获取地址信息" )
@GetMapping ( "/getUserAddress/{id}" )
public Result findUserAddressById ( @PathVariable ( "id" ) Long id) {
UserAddress userAddress = userAddressService. getById ( id) ;
return Result. ok ( userAddress) ;
}
}
@FeignClient ( value = "service-user" )
public interface UserClient {
@Operation ( summary = "获取地址信息" )
@GetMapping ( "/api/user/userAddress/getUserAddress/{id}" )
public Result< UserAddress> findUserAddressById ( @PathVariable ( "id" ) Long id) ;
}
6、删除购物车选中的购物项接口(service-cart)
@RestController
@RequestMapping ( "/api/order/cart" )
@Tag ( name = "购物车模块" )
public class CartController {
@Resource
private CartService cartService;
@Operation ( summary = "删除购物车选中购物项" )
@DeleteMapping ( "/auth/delCheckedCartInfos" )
public Result delCheckedCartInfos ( )
{
cartService. deleteCheckedCartInfos ( ) ;
return Result. ok ( ) ;
}
}
@FeignClient ( value = "service-cart" )
public interface CartClient {
@GetMapping ( "/api/order/cart/auth/checkedCartInfos" )
public Result< List< CartInfo>> checkedCartInfos ( ) ;
@DeleteMapping ( "/api/order/cart/auth/delCheckedCartInfos" )
public Result< Void> delCheckedCartInfos ( ) ;
}
7、验价验库存
@Service
public class OrderServiceImpl extends ServiceImpl < OrderMapper , OrderInfo > implements OrderService {
public Long submitOrder ( OrderSubmitDto orderSubmitDto) {
List < OrderItem > orderItemList = orderSubmitDto. getOrderItemList ( ) ;
if ( CollectionUtils . isEmpty ( orderItemList) ) {
throw new SpzxException ( ResultCodeEnum . FAIL , null ) ;
}
orderItemList. forEach ( orderItem -> {
Result < ProductSku > skuResult = productClient. getById ( orderItem. getSkuId ( ) ) ;
if ( skuResult. getCode ( ) != 200 ) {
throw new SpzxException ( ResultCodeEnum . FAIL , null ) ;
}
ProductSku sku = skuResult. getData ( ) ;
if ( sku. getSalePrice ( ) . compareTo ( orderItem. getSkuPrice ( ) ) != 0 ) {
throw new SpzxException ( ResultCodeEnum . ORDER_ITEM_PRICE_ERROR , null ) ;
}
if ( sku. getStockNum ( ) < orderItem. getSkuNum ( ) ) {
throw new SpzxException ( ResultCodeEnum . ORDER_ITEM_STOCK_ERROR , null ) ;
}
} ) ;
}
}
8、创建订单持久化到mysql
@Service
public class OrderServiceImpl extends ServiceImpl < OrderMapper , OrderInfo > implements OrderService {
public Long submitOrder ( OrderSubmitDto orderSubmitDto) {
Result < UserAddress > userAddressResult = userClient. findUserAddressById ( orderSubmitDto. getUserAddressId ( ) ) ;
if ( userAddressResult. getCode ( ) != 200 ) {
throw new SpzxException ( ResultCodeEnum . FAIL , null ) ;
}
OrderInfo orderInfo = new OrderInfo ( ) ;
UserInfo userInfo = SpzxServiceAuthInterceptor . THREAD_LOCAL . get ( ) ;
orderInfo. setUserId ( userInfo. getId ( ) ) ;
orderInfo. setNickName ( userInfo. getNickName ( ) ) ;
String orderNo = IdUtil . getSnowflake ( 1 , 1 ) . nextIdStr ( ) ;
orderInfo. setOrderNo ( orderNo) ;
orderInfo. setCouponId ( 0L ) ;
BigDecimal totalAmount = orderItemList. stream ( ) . map ( orderItem -> {
return orderItem. getSkuPrice ( ) . multiply ( new BigDecimal ( orderItem. getSkuNum ( ) ) ) ;
} ) . reduce ( BigDecimal :: add ) . get ( ) ;
orderInfo. setTotalAmount ( totalAmount) ;
orderInfo. setCouponAmount ( new BigDecimal ( "0" ) ) ;
orderInfo. setOriginalTotalAmount ( totalAmount) ;
orderInfo. setFreightFee ( orderSubmitDto. getFreightFee ( ) ) ;
orderInfo. setOrderStatus ( 0 ) ;
UserAddress userAddress = userAddressResult. getData ( ) ;
orderInfo. setReceiverName ( userAddress. getName ( ) ) ;
orderInfo. setReceiverPhone ( userAddress. getPhone ( ) ) ;
orderInfo. setReceiverTagName ( userAddress. getTagName ( ) ) ;
orderInfo. setReceiverProvince ( userAddress. getProvinceCode ( ) ) ;
orderInfo. setReceiverCity ( userAddress. getCityCode ( ) ) ;
orderInfo. setReceiverDistrict ( userAddress. getDistrictCode ( ) ) ;
orderInfo. setReceiverAddress ( userAddress. getAddress ( ) ) ;
orderInfo. setPaymentTime ( new Date ( ) ) ;
orderInfo. setDeliveryTime ( new Date ( ) ) ;
orderInfo. setReceiveTime ( new Date ( ) ) ;
orderInfo. setCancelTime ( new Date ( ) ) ;
orderInfo. setCancelReason ( "" ) ;
orderInfo. setRemark ( orderSubmitDto. getRemark ( ) ) ;
this . save ( orderInfo) ;
Long orderId = orderInfo. getId ( ) ;
orderItemList. forEach ( orderItem -> {
orderItem. setOrderId ( orderId) ;
} ) ;
orderItemService. saveBatch ( orderItemList) ;
}
9、清空选中的购物车
@Service
public class OrderServiceImpl extends ServiceImpl < OrderMapper , OrderInfo > implements OrderService {
@Transactional ( rollbackFor = Exception . class )
@Override
public Long submitOrder ( OrderSubmitDto orderSubmitDto) {
cartClient. delCheckedCartInfos ( ) ;
}
}
2、立即购买,不经过购物车
2.1、在 OrderController 创建 buy方法
@RestController
@Tag ( name = "订单管理模块" , description = "订单管理模块" )
@RequestMapping ( "/api/order/orderInfo" )
public class OrderController {
@Operation ( summary = "立即购买:商品详情页立即购买按钮" )
@GetMapping ( "/auth/buy/{skuId}" )
public Result buy ( @PathVariable ( "skuId" ) Long skuId) {
TradeVo tradeVo = orderService. buy ( skuId) ;
return Result . ok ( tradeVo) ;
}
}
2.2、在 OrderServiceImpl 中实现 buy 方法
@Override
public TradeVo buy ( Long skuId) {
Result < ProductSku > skuResult = productClient. getById ( skuId) ;
if ( skuResult. getCode ( ) != 200 ) {
throw new SpzxException ( ResultCodeEnum . FAIL , null ) ;
}
ProductSku sku = skuResult. getData ( ) ;
Long token = IdUtil . getSnowflake ( 1 , 1 ) . nextId ( ) ;
stringRedisTemplate. opsForValue ( ) . set ( "spzx:order:" + token. toString ( ) , "1" , 30 , TimeUnit . MINUTES ) ;
OrderItem orderItem = new OrderItem ( ) ;
orderItem. setOrderId ( token) ;
orderItem. setSkuNum ( 1 ) ;
orderItem. setSkuPrice ( sku. getSalePrice ( ) ) ;
orderItem. setThumbImg ( sku. getThumbImg ( ) ) ;
orderItem. setSkuId ( skuId) ;
orderItem. setSkuName ( sku. getSkuName ( ) ) ;
TradeVo tradeVo = new TradeVo ( ) ;
tradeVo. setTotalAmount ( orderItem. getSkuPrice ( ) ) ;
tradeVo. setOrderItemList ( List . of ( orderItem) ) ;
return tradeVo;
}
3、订单幂等性校验分析
幂等性如何保证:相同的请求多次提交如果处理的结果一致
解决思路:和防止表单重复提交一样