Browse Source

将 mall/order 从 content-service 抽离出来放到 file-service 中, 这样 mall 的购物流程形成 product(content-service) -> order(file-service) -> wallet(user-service) 调用路径, 可用于验证分布式事务等技术

reghao 1 year ago
parent
commit
b3b813b3c5
27 changed files with 406 additions and 312 deletions
  1. 22 0
      content/content-api/src/main/java/cn/reghao/tnb/content/api/dto/ItemSnapshot.java
  2. 6 2
      content/content-api/src/main/java/cn/reghao/tnb/content/api/iface/MallService.java
  3. 12 1
      content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/controller/CartController.java
  4. 0 35
      content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/controller/PayController.java
  5. 12 1
      content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/controller/ProductController.java
  6. 2 0
      content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/db/mapper/ProductSnapshotMapper.java
  7. 7 34
      content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/db/repository/MallRepository.java
  8. 1 1
      content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/model/dto/BuyItem.java
  9. 2 2
      content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/model/dto/BuyProductDto.java
  10. 0 11
      content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/model/po/OrderProduct.java
  11. 26 12
      content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/rpc/MallServiceImpl.java
  12. 75 0
      content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/service/BuyService.java
  13. 0 115
      content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/service/OrderService.java
  14. 0 54
      content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/service/PayService.java
  15. 5 0
      content/content-service/src/main/resources/mapper/mall/ProductSnapshotMapper.xml
  16. 24 0
      file/file-api/src/main/java/cn/reghao/file/api/dto/BuyDto.java
  17. 11 0
      file/file-api/src/main/java/cn/reghao/file/api/iface/NewOrderService.java
  18. 12 13
      file/file-service/src/main/java/cn/reghao/tnb/file/app/controller/OrderController.java
  19. 2 2
      file/file-service/src/main/java/cn/reghao/tnb/file/app/db/mapper/OrderMapper.java
  20. 26 4
      file/file-service/src/main/java/cn/reghao/tnb/file/app/delay/task/OrderTask.java
  21. 1 1
      file/file-service/src/main/java/cn/reghao/tnb/file/app/model/constant/OrderStatus.java
  22. 8 8
      file/file-service/src/main/java/cn/reghao/tnb/file/app/model/po/Order.java
  23. 9 9
      file/file-service/src/main/java/cn/reghao/tnb/file/app/model/vo/OrderDetail.java
  24. 7 4
      file/file-service/src/main/java/cn/reghao/tnb/file/app/rpc/JobServiceImpl.java
  25. 38 0
      file/file-service/src/main/java/cn/reghao/tnb/file/app/rpc/NewOrderServiceImpl.java
  26. 95 0
      file/file-service/src/main/java/cn/reghao/tnb/file/app/service/OrderService.java
  27. 3 3
      file/file-service/src/main/resources/mapper/OrderMapper.xml

+ 22 - 0
content/content-api/src/main/java/cn/reghao/tnb/content/api/dto/ItemSnapshot.java

@@ -0,0 +1,22 @@
+package cn.reghao.tnb.content.api.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+
+/**
+ * @author reghao
+ * @date 2025-02-24 10:21:19
+ */
+@Getter
+@Setter
+public class ItemSnapshot implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private Long orderId;
+    private Long itemId;
+    private String title;
+    private String picUrl;
+    private Double price;
+}

+ 6 - 2
content/content-api/src/main/java/cn/reghao/tnb/content/api/iface/MallService.java

@@ -1,11 +1,15 @@
 package cn.reghao.tnb.content.api.iface;
 
-import cn.reghao.jutil.jdk.result.Result;
+import cn.reghao.tnb.content.api.dto.ItemSnapshot;
 
 /**
  * @author reghao
  * @date 2024-12-04 11:37:08
  */
 public interface MallService {
-    Result orderTimeout(long orderId);
+    void updateCancelOrder(long productId, int amount);
+    double getProductPrice(long itemId);
+    void deleteCartItem(long userId, long productId);
+    ItemSnapshot getItemSnapshot(long orderId);
+    long getSellerId(long itemId);
 }

+ 12 - 1
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/controller/CartController.java

@@ -4,7 +4,9 @@ import cn.reghao.jutil.web.WebResult;
 import cn.reghao.tnb.common.auth.AuthUser;
 import cn.reghao.tnb.common.auth.UserContext;
 import cn.reghao.tnb.content.app.mall.model.dto.CartDto;
+import cn.reghao.tnb.content.app.mall.model.dto.BuyProductDto;
 import cn.reghao.tnb.content.app.mall.model.vo.CartCard;
+import cn.reghao.tnb.content.app.mall.service.BuyService;
 import cn.reghao.tnb.content.app.mall.service.CartService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -23,9 +25,11 @@ import java.util.List;
 @RequestMapping("/api/mall/cart")
 public class CartController {
     private final CartService cartService;
+    private final BuyService buyService;
 
-    public CartController(CartService cartService) {
+    public CartController(CartService cartService, BuyService buyService) {
         this.cartService = cartService;
+        this.buyService = buyService;
     }
 
     @ApiOperation(value = "将商品加入购物车", notes = "N")
@@ -56,4 +60,11 @@ public class CartController {
         List<CartCard> list = cartService.getUserCart(loginUser);
         return WebResult.success(list);
     }
+
+    @ApiOperation(value = "购买购物车商品", notes = "N")
+    @PostMapping("/buy")
+    public String buyCartItems(@RequestBody @Validated BuyProductDto buyProductDto) {
+        long orderId = buyService.buyCartItems(buyProductDto);
+        return WebResult.success(orderId);
+    }
 }

+ 0 - 35
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/controller/PayController.java

@@ -1,35 +0,0 @@
-package cn.reghao.tnb.content.app.mall.controller;
-
-import cn.reghao.jutil.jdk.result.Result;
-import cn.reghao.jutil.web.WebResult;
-import cn.reghao.tnb.common.auth.AuthUser;
-import cn.reghao.tnb.content.app.mall.service.PayService;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-/**
- * @author reghao
- * @date 2024-12-05 15:25:25
- */
-@Api(tags = "支付接口")
-@RestController
-@RequestMapping("/api/mall/pay")
-public class PayController {
-    private final PayService payService;
-
-    public PayController(PayService payService) {
-        this.payService = payService;
-    }
-
-    @ApiOperation(value = "支付订单", notes = "N")
-    @AuthUser
-    @PostMapping("/order/{orderId}")
-    public String payOrder(@PathVariable("orderId") Long orderId) {
-        Result result = payService.payOrder(orderId);
-        return WebResult.result(result);
-    }
-}

+ 12 - 1
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/controller/ProductController.java

@@ -4,8 +4,10 @@ import cn.reghao.jutil.jdk.db.PageList;
 import cn.reghao.jutil.jdk.result.Result;
 import cn.reghao.jutil.web.WebResult;
 import cn.reghao.tnb.content.api.dto.TaobaoItem;
+import cn.reghao.tnb.content.app.mall.model.dto.BuyProductDto;
 import cn.reghao.tnb.content.app.mall.model.dto.ProductAddDto;
 import cn.reghao.tnb.content.app.mall.model.po.Product;
+import cn.reghao.tnb.content.app.mall.service.BuyService;
 import cn.reghao.tnb.content.app.mall.service.ProductService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -22,9 +24,11 @@ import org.springframework.web.bind.annotation.*;
 @RequestMapping("/api/mall/product")
 public class ProductController {
     private final ProductService productService;
+    private final BuyService buyService;
 
-    public ProductController(ProductService productService) {
+    public ProductController(ProductService productService, BuyService buyService) {
         this.productService = productService;
+        this.buyService = buyService;
     }
 
     @ApiOperation(value = "添加淘宝商品", notes = "N")
@@ -54,4 +58,11 @@ public class ProductController {
         Product product = productService.getProduct(itemId);
         return WebResult.success(product);
     }
+
+    @ApiOperation(value = "购买商品", notes = "N")
+    @PostMapping("/buy")
+    public String buyProduct(@RequestBody @Validated BuyProductDto buyProductDto) {
+        long orderId = buyService.buyProduct(buyProductDto);
+        return WebResult.success(orderId);
+    }
 }

+ 2 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/db/mapper/ProductSnapshotMapper.java

@@ -1,6 +1,7 @@
 package cn.reghao.tnb.content.app.mall.db.mapper;
 
 import cn.reghao.jutil.jdk.db.BaseMapper;
+import cn.reghao.tnb.content.api.dto.ItemSnapshot;
 import cn.reghao.tnb.content.app.mall.model.po.ProductSnapshot;
 import org.apache.ibatis.annotations.Mapper;
 
@@ -11,4 +12,5 @@ import org.apache.ibatis.annotations.Mapper;
 @Mapper
 public interface ProductSnapshotMapper extends BaseMapper<ProductSnapshot> {
     ProductSnapshot findByOrderId(long orderId);
+    ItemSnapshot findItemSnapshotByOrderId(long orderId);
 }

+ 7 - 34
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/db/repository/MallRepository.java

@@ -1,13 +1,10 @@
 package cn.reghao.tnb.content.app.mall.db.repository;
 
-import cn.reghao.tnb.content.app.mall.db.mapper.OrderMapper;
+import cn.reghao.tnb.content.api.dto.ItemSnapshot;
 import cn.reghao.tnb.content.app.mall.db.mapper.ProductMapper;
 import cn.reghao.tnb.content.app.mall.db.mapper.ProductSnapshotMapper;
-import cn.reghao.tnb.content.app.mall.model.constant.OrderStatus;
-import cn.reghao.tnb.content.app.mall.model.po.Order;
 import cn.reghao.tnb.content.app.mall.model.po.Product;
 import cn.reghao.tnb.content.app.mall.model.po.ProductSnapshot;
-import cn.reghao.tnb.content.app.mall.model.vo.OrderDetail;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Repository;
@@ -22,41 +19,25 @@ import org.springframework.transaction.annotation.Transactional;
 public class MallRepository {
     private final ProductMapper productMapper;
     private final ProductSnapshotMapper productSnapshotMapper;
-    private final OrderMapper orderMapper;
 
-    public MallRepository(ProductMapper productMapper, ProductSnapshotMapper productSnapshotMapper,
-                          OrderMapper orderMapper) {
+    public MallRepository(ProductMapper productMapper, ProductSnapshotMapper productSnapshotMapper) {
         this.productMapper = productMapper;
         this.productSnapshotMapper = productSnapshotMapper;
-        this.orderMapper = orderMapper;
     }
 
     // TODO 需要锁来保证库存变化的准确性
     @Transactional(rollbackFor = Exception.class)
-    public void saveOrder(Order order, ProductSnapshot productSnapshot) {
-        long productId = order.getProductId();
-        int amount = order.getAmount();
+    public void updateProduct(int amount, ProductSnapshot productSnapshot) {
+        long productId = productSnapshot.getItemId();
         // 减商品库存
         productMapper.updateStockMinus(productId, amount);
-
-        orderMapper.save(order);
         productSnapshotMapper.save(productSnapshot);
     }
 
     @Transactional(rollbackFor = Exception.class)
-    public void updateCancelOrder(Order order) {
-        long productId = order.getProductId();
-        int amount = order.getAmount();
+    public void updateCancelOrder(long productId, int amount) {
         // 恢复商品库存
         productMapper.updateStockMinus(productId, amount);
-
-        long orderId = order.getOrderId();
-        orderMapper.updateOrderStatus(orderId, OrderStatus.cancelled.getCode());
-    }
-
-    public void updatePayOrder(long orderId) {
-        int status = OrderStatus.toConfirm.getCode();
-        orderMapper.updateOrderStatus(orderId, status);
     }
 
     @Cacheable(cacheNames = "tnb:mall:product", key = "#itemId", unless = "#result == null")
@@ -65,15 +46,7 @@ public class MallRepository {
         return productMapper.findByItemId(itemId);
     }
 
-    public OrderDetail getOrderDetail(long orderId) {
-        Order order = orderMapper.findByOrderId(orderId);
-        ProductSnapshot productSnapshot = productSnapshotMapper.findByOrderId(orderId);
-        OrderDetail orderDetail = new OrderDetail(order, productSnapshot);
-        return orderDetail;
-    }
-
-    public Order getOrder(long orderId) {
-        Order order = orderMapper.findByOrderId(orderId);
-        return order;
+    public ItemSnapshot getItemSnapshot(long orderId) {
+        return productSnapshotMapper.findItemSnapshotByOrderId(orderId);
     }
 }

+ 1 - 1
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/model/dto/OrderItem.java → content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/model/dto/BuyItem.java

@@ -11,7 +11,7 @@ import javax.validation.constraints.NotNull;
  */
 @Setter
 @Getter
-public class OrderItem {
+public class BuyItem {
     @NotNull
     private Long itemId;
     @NotNull

+ 2 - 2
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/model/dto/OrderDto.java → content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/model/dto/BuyProductDto.java

@@ -11,9 +11,9 @@ import java.util.List;
  * @date 2024-04-16 15:59:27
  */
 @Getter
-public class OrderDto {
+public class BuyProductDto {
     @NotNull
     private Long deliveryId;
     @Size(min = 1, max = 100)
-    private List<OrderItem> items;
+    private List<BuyItem> items;
 }

+ 0 - 11
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/model/po/OrderProduct.java

@@ -1,11 +0,0 @@
-package cn.reghao.tnb.content.app.mall.model.po;
-
-/**
- * @author reghao
- * @date 2024-04-26 15:42:06
- */
-public class OrderProduct {
-    private Long orderId;
-    private Long itemId;
-    private Integer num;
-}

+ 26 - 12
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/rpc/MallServiceImpl.java

@@ -1,10 +1,10 @@
 package cn.reghao.tnb.content.app.mall.rpc;
 
-import cn.reghao.jutil.jdk.result.Result;
+import cn.reghao.tnb.content.api.dto.ItemSnapshot;
 import cn.reghao.tnb.content.api.iface.MallService;
 import cn.reghao.tnb.content.app.mall.db.repository.MallRepository;
-import cn.reghao.tnb.content.app.mall.model.constant.OrderStatus;
-import cn.reghao.tnb.content.app.mall.model.po.Order;
+import cn.reghao.tnb.content.app.mall.model.po.Product;
+import cn.reghao.tnb.content.app.mall.service.CartService;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboService;
 import org.springframework.stereotype.Service;
@@ -18,19 +18,33 @@ import org.springframework.stereotype.Service;
 @Service
 public class MallServiceImpl implements MallService {
     private final MallRepository mallRepository;
+    private final CartService cartService;
 
-    public MallServiceImpl(MallRepository mallRepository) {
+    public MallServiceImpl(MallRepository mallRepository,CartService cartService) {
         this.mallRepository = mallRepository;
+        this.cartService = cartService;
     }
 
-    public Result orderTimeout(long orderId) {
-        Order order = mallRepository.getOrder(orderId);
-        if (order != null && order.getStatus() == OrderStatus.toPay.getCode()) {
-            mallRepository.updateCancelOrder(order);
-            log.info("cancel order");
-            return Result.success();
-        }
+    public void updateCancelOrder(long productId, int amount) {
+        mallRepository.updateCancelOrder(productId, amount);
+        log.info("cancel order");
+    }
+
+    public double getProductPrice(long itemId) {
+        Product product = mallRepository.getProduct(itemId);
+        return product.getPrice();
+    }
+
+    public void deleteCartItem(long userId, long productId) {
+        cartService.deleteCartItem(userId, productId);
+    }
+
+    public ItemSnapshot getItemSnapshot(long orderId) {
+        return mallRepository.getItemSnapshot(orderId);
+    }
 
-        return Result.fail(String.format("order %s not exist", orderId));
+    public long getSellerId(long itemId) {
+        Product product = mallRepository.getProduct(itemId);
+        return product.getSellerId();
     }
 }

+ 75 - 0
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/service/BuyService.java

@@ -0,0 +1,75 @@
+package cn.reghao.tnb.content.app.mall.service;
+
+import cn.reghao.file.api.dto.BuyDto;
+import cn.reghao.file.api.iface.NewOrderService;
+import cn.reghao.tnb.common.auth.UserContext;
+import cn.reghao.tnb.content.app.mall.db.mapper.ProductMapper;
+import cn.reghao.tnb.content.app.mall.db.repository.MallRepository;
+import cn.reghao.tnb.content.app.mall.model.dto.BuyItem;
+import cn.reghao.tnb.content.app.mall.model.dto.BuyProductDto;
+import cn.reghao.tnb.content.app.mall.model.po.Product;
+import cn.reghao.tnb.content.app.mall.model.po.ProductSnapshot;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author reghao
+ * @date 2025-02-24 15:00:52
+ */
+@Service
+public class BuyService {
+    @DubboReference(check = false)
+    private NewOrderService newOrderService;
+
+    private final CartService cartService;
+    private final ProductMapper productMapper;
+    private final MallRepository mallRepository;
+
+    public BuyService(CartService cartService, ProductMapper productMapper, MallRepository mallRepository) {
+        this.cartService = cartService;
+        this.productMapper = productMapper;
+        this.mallRepository = mallRepository;
+    }
+
+    public long buyProduct(BuyProductDto buyProductDto) {
+        long deliveryId = buyProductDto.getDeliveryId();
+        BuyItem buyItem = buyProductDto.getItems().get(0);
+        long productId = buyItem.getItemId();
+        int amount = buyItem.getNum();
+        Product product = productMapper.findByItemId(productId);
+        double price = product.getPrice();
+        BuyDto buyDto = new BuyDto(productId, price, amount, deliveryId);
+
+        // TODO 创建订单和修改库存是一个分布式事务
+        long orderId = newOrderService.createOrder(buyDto);
+        ProductSnapshot productSnapshot = new ProductSnapshot(orderId, product);
+        mallRepository.updateProduct(amount, productSnapshot);
+        return orderId;
+    }
+
+    public long buyCartItems(BuyProductDto buyProductDto) {
+        long deliveryId = buyProductDto.getDeliveryId();
+        // shop -> products
+        Map<Long, List<BuyItem>> map = buyProductDto.getItems().stream().collect(Collectors.groupingBy(BuyItem::getShopId));
+        map.forEach((shopId, items) -> {
+            items.forEach(item -> {
+                long orderId = 1L;
+                Product product = mallRepository.getProduct(item.getItemId());
+            });
+        });
+
+        long loginUser = UserContext.getUser();
+        map.forEach((shopId, items) -> {
+            items.forEach(item -> {
+                long itemId = item.getItemId();
+                cartService.deleteCartItem(loginUser, itemId);
+            });
+        });
+
+        return 0;
+    }
+}

+ 0 - 115
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/service/OrderService.java

@@ -1,115 +0,0 @@
-package cn.reghao.tnb.content.app.mall.service;
-
-import cn.reghao.file.api.iface.JobService;
-import cn.reghao.jutil.tool.id.SnowFlake;
-import cn.reghao.tnb.common.auth.UserContext;
-import cn.reghao.tnb.content.app.mall.db.mapper.OrderMapper;
-import cn.reghao.tnb.content.app.mall.db.repository.MallRepository;
-import cn.reghao.tnb.content.app.mall.model.constant.OrderStatus;
-import cn.reghao.tnb.content.app.mall.model.dto.OrderDto;
-import cn.reghao.tnb.content.app.mall.model.dto.OrderItem;
-import cn.reghao.tnb.content.app.mall.model.po.Order;
-import cn.reghao.tnb.content.app.mall.model.po.Product;
-import cn.reghao.tnb.content.app.mall.model.po.ProductSnapshot;
-import cn.reghao.tnb.content.app.mall.model.vo.OrderDetail;
-import org.apache.dubbo.config.annotation.DubboReference;
-import org.springframework.stereotype.Service;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-/**
- * @author reghao
- * @date 2024-04-16 11:00:24
- */
-@Service
-public class OrderService {
-    @DubboReference(check = false)
-    private JobService jobService;
-    private final OrderMapper orderMapper;
-    private final SnowFlake idGenerator;
-    private final CartService cartService;
-    private final MallRepository mallRepository;
-
-    public OrderService(OrderMapper orderMapper, CartService cartService,
-                        MallRepository mallRepository) {
-        this.orderMapper = orderMapper;
-        this.idGenerator = new SnowFlake(1L, 1L);
-        this.cartService = cartService;
-        this.mallRepository = mallRepository;
-    }
-
-    public long submitCart(OrderDto orderDto) {
-        long deliveryId = orderDto.getDeliveryId();
-        // shop -> products
-        Map<Long, List<OrderItem>> map = orderDto.getItems().stream().collect(Collectors.groupingBy(OrderItem::getShopId));
-        List<Order> orders = new ArrayList<>();
-        map.forEach((shopId, items) -> {
-            items.forEach(item -> {
-                long orderId = idGenerator.nextId();
-                Product product = mallRepository.getProduct(item.getItemId());
-                Order order = new Order(deliveryId, product.getPrice(), orderId, item);
-                orders.add(order);
-            });
-        });
-
-        if (!orders.isEmpty()) {
-            orderMapper.saveAll(orders);
-        }
-
-        long loginUser = UserContext.getUser();
-        map.forEach((shopId, items) -> {
-            items.forEach(item -> {
-                long itemId = item.getItemId();
-                cartService.deleteCartItem(loginUser, itemId);
-            });
-        });
-
-        return 0;
-    }
-
-    public long submitOrder(OrderDto orderDto) {
-        long deliveryId = orderDto.getDeliveryId();
-        OrderItem orderItem = orderDto.getItems().get(0);
-        long orderId = idGenerator.nextId();
-        Product product = mallRepository.getProduct(orderItem.getItemId());
-        Order order = new Order(orderId, product.getPrice(), deliveryId, orderItem);
-        ProductSnapshot productSnapshot = new ProductSnapshot(orderId, product);
-        mallRepository.saveOrder(order, productSnapshot);
-
-        long timeout = 15*60;
-        long jobId = jobService.addOrderTimeoutJob(orderId, timeout);
-        return orderId;
-    }
-
-    public void deliveryOrder(long orderId) {
-        int status = OrderStatus.toConfirm.getCode();
-        orderMapper.updateOrderStatus(orderId, status);
-    }
-
-    public void receiveOrder(long orderId) {
-        int status = OrderStatus.completed.getCode();
-        orderMapper.updateOrderStatus(orderId, status);
-    }
-
-    public List<OrderDetail> getOrders(int pageNumber) {
-        long loginUser = UserContext.getUser();
-        int pageSize = 100;
-        List<Order> list = orderMapper.findByOwner(pageSize, loginUser);
-        return list.stream().map(order -> {
-            long orderId = order.getOrderId();
-            return getOrderDetail(orderId);
-        }).collect(Collectors.toList());
-    }
-
-    public Order getOrder(long orderId) {
-        return orderMapper.findByOrderId(orderId);
-    }
-
-    public OrderDetail getOrderDetail(long orderId) {
-        OrderDetail orderDetail = mallRepository.getOrderDetail(orderId);
-        return orderDetail;
-    }
-}

+ 0 - 54
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/service/PayService.java

@@ -1,54 +0,0 @@
-package cn.reghao.tnb.content.app.mall.service;
-
-import cn.reghao.jutil.jdk.result.Result;
-import cn.reghao.jutil.jdk.result.ResultStatus;
-import cn.reghao.tnb.common.auth.UserContext;
-import cn.reghao.tnb.content.app.mall.db.mapper.ProductMapper;
-import cn.reghao.tnb.content.app.mall.db.repository.MallRepository;
-import cn.reghao.tnb.content.app.mall.model.constant.OrderStatus;
-import cn.reghao.tnb.content.app.mall.model.po.Order;
-import cn.reghao.tnb.user.api.iface.UserWalletService;
-import org.apache.dubbo.config.annotation.DubboReference;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-/**
- * @author reghao
- * @date 2024-04-19 10:12:58
- */
-@Service
-public class PayService {
-    @DubboReference(check = false)
-    private UserWalletService userWalletService;
-
-    private final OrderService orderService;
-    private final MallRepository mallRepository;
-
-    public PayService(OrderService orderService, MallRepository mallRepository) {
-        this.orderService = orderService;
-        this.mallRepository = mallRepository;
-    }
-
-    @Transactional(rollbackFor = Exception.class)
-    public Result payOrder(long orderId) {
-        Order order = mallRepository.getOrder(orderId);
-        if (order != null && order.getStatus() == OrderStatus.toPay.getCode()) {
-            long sellerId = mallRepository.getProduct(order.getProductId()).getSellerId();
-            int amount = order.getAmount();
-            long loginUser = UserContext.getUser();
-            double quantity = amount*order.getPrice();
-
-            // TODO 需要使用分布式事务来保证扣款和修改订单状态在一个事务内完成
-            // 支付订单
-            Result result = userWalletService.pay(loginUser, quantity, sellerId);
-            if (result.getCode() == ResultStatus.SUCCESS.getCode()) {
-                // 更新订单状态
-                mallRepository.updatePayOrder(orderId);
-            }
-
-            return  result;
-        }
-
-        return Result.fail("订单支付失败");
-    }
-}

+ 5 - 0
content/content-service/src/main/resources/mapper/mall/ProductSnapshotMapper.xml

@@ -14,4 +14,9 @@
         from mall_product_snapshot
         where order_id=#{orderId}
     </select>
+    <select id="findItemSnapshotByOrderId" resultType="cn.reghao.tnb.content.api.dto.ItemSnapshot">
+        select *
+        from mall_product_snapshot
+        where order_id=#{orderId}
+    </select>
 </mapper>

+ 24 - 0
file/file-api/src/main/java/cn/reghao/file/api/dto/BuyDto.java

@@ -0,0 +1,24 @@
+package cn.reghao.file.api.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author reghao
+ * @date 2024-04-16 15:59:27
+ */
+@AllArgsConstructor
+@Getter
+public class BuyDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private long productId;
+    private double price;
+    private int amount;
+    private long deliveryId;
+}

+ 11 - 0
file/file-api/src/main/java/cn/reghao/file/api/iface/NewOrderService.java

@@ -0,0 +1,11 @@
+package cn.reghao.file.api.iface;
+
+import cn.reghao.file.api.dto.BuyDto;
+
+/**
+ * @author reghao
+ * @date 2025-02-24 10:58:36
+ */
+public interface NewOrderService {
+    long createOrder(BuyDto buyDto);
+}

+ 12 - 13
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/controller/OrderController.java → file/file-service/src/main/java/cn/reghao/tnb/file/app/controller/OrderController.java

@@ -1,13 +1,12 @@
-package cn.reghao.tnb.content.app.mall.controller;
+package cn.reghao.tnb.file.app.controller;
 
+import cn.reghao.jutil.jdk.result.Result;
 import cn.reghao.jutil.web.WebResult;
 import cn.reghao.tnb.common.auth.AuthUser;
-import cn.reghao.tnb.content.app.mall.model.dto.OrderDto;
-import cn.reghao.tnb.content.app.mall.model.vo.OrderDetail;
-import cn.reghao.tnb.content.app.mall.service.OrderService;
+import cn.reghao.tnb.file.app.model.vo.OrderDetail;
+import cn.reghao.tnb.file.app.service.OrderService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
-import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
@@ -26,14 +25,6 @@ public class OrderController {
         this.orderService = orderService;
     }
 
-    @AuthUser
-    @ApiOperation(value = "提交订单", notes = "N")
-    @PostMapping("")
-    public String submitOrder(@RequestBody @Validated OrderDto orderDto) {
-        long orderId = orderService.submitOrder(orderDto);
-        return WebResult.success(orderId);
-    }
-
     @AuthUser
     @ApiOperation(value = "取消订单", notes = "N")
     @PostMapping("/cancel/{orderId}")
@@ -63,4 +54,12 @@ public class OrderController {
         OrderDetail orderDetail = orderService.getOrderDetail(orderId);
         return WebResult.success(orderDetail);
     }
+
+    @ApiOperation(value = "支付订单", notes = "N")
+    @AuthUser
+    @PostMapping("/pay/{orderId}")
+    public String payOrder(@PathVariable("orderId") Long orderId) {
+        Result result = orderService.payOrder(orderId);
+        return WebResult.result(result);
+    }
 }

+ 2 - 2
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/db/mapper/OrderMapper.java → file/file-service/src/main/java/cn/reghao/tnb/file/app/db/mapper/OrderMapper.java

@@ -1,7 +1,7 @@
-package cn.reghao.tnb.content.app.mall.db.mapper;
+package cn.reghao.tnb.file.app.db.mapper;
 
 import cn.reghao.jutil.jdk.db.BaseMapper;
-import cn.reghao.tnb.content.app.mall.model.po.Order;
+import cn.reghao.tnb.file.app.model.po.Order;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 

+ 26 - 4
file/file-service/src/main/java/cn/reghao/tnb/file/app/delay/task/OrderTask.java

@@ -1,22 +1,44 @@
 package cn.reghao.tnb.file.app.delay.task;
 
 import cn.reghao.tnb.content.api.iface.MallService;
+import cn.reghao.tnb.file.app.db.mapper.OrderMapper;
+import cn.reghao.tnb.file.app.model.constant.OrderStatus;
+import cn.reghao.tnb.file.app.model.po.Order;
+import lombok.extern.slf4j.Slf4j;
 
 /**
  * @author reghao
  * @date 2024-12-04 13:35:31
  */
+@Slf4j
 public class OrderTask implements Runnable {
-    private final MallService mallService;
     private final long orderId;
+    private final MallService mallService;
+    private final OrderMapper orderMapper;
 
-    public OrderTask(MallService mallService, long orderId) {
-        this.mallService =mallService;
+    public OrderTask(MallService mallService, OrderMapper orderMapper, long orderId) {
+        this.mallService = mallService;
+        this.orderMapper = orderMapper;
         this.orderId = orderId;
     }
 
     @Override
     public void run() {
-        mallService.orderTimeout(orderId);
+        try {
+            orderTimeout(orderId);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void orderTimeout(long orderId) {
+        Order order = orderMapper.findByOrderId(orderId);
+        if (order != null && order.getStatus() == OrderStatus.toPay.getCode()) {
+            long productId = order.getProductId();
+            int amount = order.getAmount();
+            orderMapper.updateOrderStatus(orderId, OrderStatus.cancelled.getCode());
+            mallService.updateCancelOrder(productId, amount);
+            log.info("cancel order");
+        }
     }
 }

+ 1 - 1
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/model/constant/OrderStatus.java → file/file-service/src/main/java/cn/reghao/tnb/file/app/model/constant/OrderStatus.java

@@ -1,4 +1,4 @@
-package cn.reghao.tnb.content.app.mall.model.constant;
+package cn.reghao.tnb.file.app.model.constant;
 
 import java.util.HashMap;
 import java.util.Map;

+ 8 - 8
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/model/po/Order.java → file/file-service/src/main/java/cn/reghao/tnb/file/app/model/po/Order.java

@@ -1,9 +1,9 @@
-package cn.reghao.tnb.content.app.mall.model.po;
+package cn.reghao.tnb.file.app.model.po;
 
+import cn.reghao.file.api.dto.BuyDto;
 import cn.reghao.jutil.jdk.db.BaseObject;
 import cn.reghao.tnb.common.auth.UserContext;
-import cn.reghao.tnb.content.app.mall.model.constant.OrderStatus;
-import cn.reghao.tnb.content.app.mall.model.dto.OrderItem;
+import cn.reghao.tnb.file.app.model.constant.OrderStatus;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 import lombok.NoArgsConstructor;
@@ -26,12 +26,12 @@ public class Order extends BaseObject<Integer> {
     private Long owner;
     private Integer status;
 
-    public Order(long orderId, double price, long deliveryId, OrderItem orderItem) {
+    public Order(long orderId, BuyDto buyDto) {
         this.orderId = orderId;
-        this.deliveryId = deliveryId;
-        this.productId = orderItem.getItemId();
-        this.price = price;
-        this.amount = orderItem.getNum();
+        this.deliveryId = buyDto.getDeliveryId();
+        this.productId = buyDto.getProductId();
+        this.price = buyDto.getPrice();
+        this.amount = buyDto.getAmount();
         this.owner = UserContext.getUser();
         this.status = OrderStatus.toPay.getCode();
     }

+ 9 - 9
content/content-service/src/main/java/cn/reghao/tnb/content/app/mall/model/vo/OrderDetail.java → file/file-service/src/main/java/cn/reghao/tnb/file/app/model/vo/OrderDetail.java

@@ -1,9 +1,9 @@
-package cn.reghao.tnb.content.app.mall.model.vo;
+package cn.reghao.tnb.file.app.model.vo;
 
 import cn.reghao.jutil.jdk.converter.DateTimeConverter;
-import cn.reghao.tnb.content.app.mall.model.constant.OrderStatus;
-import cn.reghao.tnb.content.app.mall.model.po.Order;
-import cn.reghao.tnb.content.app.mall.model.po.ProductSnapshot;
+import cn.reghao.tnb.content.api.dto.ItemSnapshot;
+import cn.reghao.tnb.file.app.model.constant.OrderStatus;
+import cn.reghao.tnb.file.app.model.po.Order;
 
 /**
  * @author reghao
@@ -20,12 +20,12 @@ public class OrderDetail {
     private String statusText;
     private String createAt;
 
-    public OrderDetail(Order order, ProductSnapshot productSnapshot) {
-        this.itemId = productSnapshot.getItemId();
+    public OrderDetail(Order order, ItemSnapshot itemSnapshot) {
+        this.itemId = itemSnapshot.getItemId();
         this.orderId = order.getOrderId();
-        this.title = productSnapshot.getTitle();
-        this.picUrl = productSnapshot.getPicUrl();
-        this.price = productSnapshot.getPrice();
+        this.title = itemSnapshot.getTitle();
+        this.picUrl = itemSnapshot.getPicUrl();
+        this.price = itemSnapshot.getPrice();
         this.amount = order.getAmount();
         this.status = OrderStatus.getByCode(order.getStatus()).getCode();
         this.statusText = OrderStatus.getByCode(order.getStatus()).getDesc();

+ 7 - 4
file/file-service/src/main/java/cn/reghao/tnb/file/app/rpc/JobServiceImpl.java

@@ -9,6 +9,7 @@ import cn.reghao.tnb.content.api.iface.MallService;
 import cn.reghao.tnb.content.api.iface.UserContentService;
 import cn.reghao.tnb.file.app.config.OssConsoleClientFactory;
 import cn.reghao.tnb.file.app.db.mapper.JobDetailMapper;
+import cn.reghao.tnb.file.app.db.mapper.OrderMapper;
 import cn.reghao.tnb.file.app.delay.JobContext;
 import cn.reghao.tnb.file.app.delay.DelayJob;
 import cn.reghao.tnb.file.app.delay.task.ConvertTask;
@@ -34,29 +35,31 @@ public class JobServiceImpl implements JobService {
     @DubboReference(check = false, timeout = 60_000)
     private UserContentService userContentService;
     @DubboReference(check = false, timeout = 60_000)
-    private MallService mallService;
-    @DubboReference(check = false, timeout = 60_000)
     private AdminVideoService adminVideoService;
+    @DubboReference(check = false, timeout = 60_000)
+    private MallService mallService;
 
     private final SnowFlake idGenerator;
     private final JobContext jobContext;
     private final OssConsoleClientFactory ossConsoleClientFactory;
     private final JobDetailMapper jobDetailMapper;
     private final String baseDir;
+    private final OrderMapper orderMapper;
 
     public JobServiceImpl(JobContext jobContext, OssConsoleClientFactory ossConsoleClientFactory,
-                          JobDetailMapper jobDetailMapper, ServerProperties serverProperties) {
+                          JobDetailMapper jobDetailMapper, ServerProperties serverProperties, OrderMapper orderMapper) {
         this.idGenerator = new SnowFlake(1L, 1L);
         this.jobContext = jobContext;
         this.ossConsoleClientFactory = ossConsoleClientFactory;
         this.jobDetailMapper = jobDetailMapper;
         this.baseDir = serverProperties.getTomcat().getBasedir().getAbsolutePath();
+        this.orderMapper = orderMapper;
     }
 
     @Override
     public long addOrderTimeoutJob(long orderId, long delaySecond) {
         long jobId = idGenerator.nextId();
-        OrderTask orderTask = new OrderTask(mallService, orderId);
+        OrderTask orderTask = new OrderTask(mallService, orderMapper, orderId);
         DelayJob delayJob = new DelayJob(jobId, orderTask, delaySecond);
         jobContext.addJob(delayJob);
 

+ 38 - 0
file/file-service/src/main/java/cn/reghao/tnb/file/app/rpc/NewOrderServiceImpl.java

@@ -0,0 +1,38 @@
+package cn.reghao.tnb.file.app.rpc;
+
+import cn.reghao.file.api.dto.BuyDto;
+import cn.reghao.file.api.iface.NewOrderService;
+import cn.reghao.file.api.iface.JobService;
+import cn.reghao.jutil.tool.id.SnowFlake;
+import cn.reghao.tnb.file.app.db.mapper.OrderMapper;
+import cn.reghao.tnb.file.app.model.po.Order;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author reghao
+ * @date 2025-02-24 11:00:51
+ */
+@DubboService
+@Service
+public class NewOrderServiceImpl implements NewOrderService {
+    private final JobService jobService;
+    private final OrderMapper orderMapper;
+    private final SnowFlake idGenerator;
+
+    public NewOrderServiceImpl(JobService jobService, OrderMapper orderMapper) {
+        this.jobService = jobService;
+        this.orderMapper = orderMapper;
+        this.idGenerator = new SnowFlake(1L, 1L);
+    }
+
+    public long createOrder(BuyDto buyDto) {
+        long orderId = idGenerator.nextId();
+        Order order = new Order(orderId, buyDto);
+        orderMapper.save(order);
+
+        long timeout = 15*60;
+        long jobId = jobService.addOrderTimeoutJob(orderId, timeout);
+        return orderId;
+    }
+}

+ 95 - 0
file/file-service/src/main/java/cn/reghao/tnb/file/app/service/OrderService.java

@@ -0,0 +1,95 @@
+package cn.reghao.tnb.file.app.service;
+
+import cn.reghao.file.api.iface.JobService;
+import cn.reghao.jutil.jdk.result.Result;
+import cn.reghao.jutil.jdk.result.ResultStatus;
+import cn.reghao.tnb.common.auth.UserContext;
+import cn.reghao.tnb.content.api.dto.ItemSnapshot;
+import cn.reghao.tnb.content.api.iface.MallService;
+import cn.reghao.tnb.file.app.db.mapper.OrderMapper;
+import cn.reghao.tnb.file.app.model.constant.OrderStatus;
+import cn.reghao.tnb.file.app.model.po.Order;
+import cn.reghao.tnb.file.app.model.vo.OrderDetail;
+import cn.reghao.tnb.user.api.iface.UserWalletService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author reghao
+ * @date 2024-04-16 11:00:24
+ */
+@Slf4j
+@Service
+public class OrderService {
+    @DubboReference(check = false)
+    private MallService mallService;
+    @DubboReference(check = false)
+    private UserWalletService userWalletService;
+
+    private JobService jobService;
+    private final OrderMapper orderMapper;
+
+    public OrderService(JobService jobService, OrderMapper orderMapper) {
+        this.jobService = jobService;
+        this.orderMapper = orderMapper;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public Result payOrder(long orderId) {
+        Order order = orderMapper.findByOrderId(orderId);
+        if (order != null && order.getStatus() == OrderStatus.toPay.getCode()) {
+            long sellerId = mallService.getSellerId(order.getProductId());
+            int amount = order.getAmount();
+            long loginUser = UserContext.getUser();
+            double quantity = amount*order.getPrice();
+
+            // TODO 支付订单和更新订单状态是一个分布式事务
+            // 支付订单
+            Result result = userWalletService.pay(loginUser, quantity, sellerId);
+            if (result.getCode() == ResultStatus.SUCCESS.getCode()) {
+                // 更新订单状态
+                int status = OrderStatus.toConfirm.getCode();
+                orderMapper.updateOrderStatus(orderId, status);
+            }
+
+            return  result;
+        }
+
+        return Result.fail("订单支付失败");
+    }
+
+    public void deliveryOrder(long orderId) {
+        int status = OrderStatus.toConfirm.getCode();
+        orderMapper.updateOrderStatus(orderId, status);
+    }
+
+    public void receiveOrder(long orderId) {
+        int status = OrderStatus.completed.getCode();
+        orderMapper.updateOrderStatus(orderId, status);
+    }
+
+    public List<OrderDetail> getOrders(int pageNumber) {
+        long loginUser = UserContext.getUser();
+        int pageSize = 100;
+        List<Order> list = orderMapper.findByOwner(pageSize, loginUser);
+        return list.stream().map(order -> {
+            long orderId = order.getOrderId();
+            return getOrderDetail(orderId);
+        }).collect(Collectors.toList());
+    }
+
+    public Order getOrder(long orderId) {
+        return orderMapper.findByOrderId(orderId);
+    }
+
+    public OrderDetail getOrderDetail(long orderId) {
+        ItemSnapshot itemSnapshot = mallService.getItemSnapshot(orderId);
+        Order order = orderMapper.findByOrderId(orderId);
+        return new OrderDetail(order, itemSnapshot);
+    }
+}

+ 3 - 3
content/content-service/src/main/resources/mapper/mall/OrderMapper.xml → file/file-service/src/main/resources/mapper/OrderMapper.xml

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 
-<mapper namespace="cn.reghao.tnb.content.app.mall.db.mapper.OrderMapper">
+<mapper namespace="cn.reghao.tnb.file.app.db.mapper.OrderMapper">
     <insert id="save" useGeneratedKeys="true" keyProperty="id">
         insert into mall_order
         (`order_id`,`product_id`,`delivery_id`,`price`,`amount`,`owner`,`status`)
@@ -23,14 +23,14 @@
         where `order_id`=#{orderId}
     </update>
     
-    <select id="findByOwner" resultType="cn.reghao.tnb.content.app.mall.model.po.Order">
+    <select id="findByOwner" resultType="cn.reghao.tnb.file.app.model.po.Order">
         select *
         from mall_order
         where `owner`=#{owner}
         order by create_time desc
         limit #{pageSize}
     </select>
-    <select id="findByOrderId" resultType="cn.reghao.tnb.content.app.mall.model.po.Order">
+    <select id="findByOrderId" resultType="cn.reghao.tnb.file.app.model.po.Order">
         select *
         from mall_order
         where `order_id`=#{orderId}