Explorar o código

订单详情添加菜品信息

lutong hai 2 meses
pai
achega
8c7809e68e

+ 12 - 32
alien-dining/src/main/java/shop/alien/dining/controller/StoreOrderController.java

@@ -22,10 +22,10 @@ import shop.alien.entity.store.dto.ChangeTableDTO;
 import shop.alien.entity.store.dto.CreateOrderDTO;
 import shop.alien.entity.store.vo.OrderChangeLogBatchVO;
 import shop.alien.entity.store.vo.OrderInfoVO;
+import shop.alien.entity.store.vo.OrderDetailWithChangeLogVO;
 import shop.alien.entity.store.vo.StoreOrderPageVO;
 import shop.alien.mapper.StoreInfoMapper;
 import shop.alien.mapper.StoreOrderDetailMapper;
-import shop.alien.mapper.StoreTableLogMapper;
 import shop.alien.mapper.StoreTableMapper;
 
 import javax.validation.Valid;
@@ -49,7 +49,6 @@ public class StoreOrderController {
     private final CartService cartService;
     private final SseService sseService;
     private final StoreOrderDetailMapper orderDetailMapper;
-    private final StoreTableLogMapper storeTableLogMapper;
     private final StoreTableMapper storeTableMapper;
     private final StoreInfoMapper storeInfoMapper;
 
@@ -338,20 +337,17 @@ public class StoreOrderController {
         }
     }
 
-    @ApiOperation(value = "查询订单详情", notes = "根据订单ID查询订单详情")
+    @ApiOperation(value = "查询订单详情", notes = "根据订单ID查询订单详情,包含订单基本信息和按批次分组的变更记录,用于展示每次下单/加餐都加了什么商品")
     @GetMapping("/detail/{orderId}")
-    public R<StoreOrder> getOrderDetail(@ApiParam(value = "订单ID", required = true) @PathVariable Integer orderId) {
+    public R<OrderDetailWithChangeLogVO> getOrderDetail(@ApiParam(value = "订单ID", required = true) @PathVariable Integer orderId) {
         try {
             // 从 token 获取用户信息
             Integer userId = TokenUtil.getCurrentUserId();
             if (userId == null) {
                 return R.fail("用户未登录");
             }
-            StoreOrder order = orderService.getOrderById(orderId);
-            if (order == null) {
-                return R.fail("订单不存在");
-            }
-            return R.data(order);
+            OrderDetailWithChangeLogVO orderDetail = orderService.getOrderDetailWithChangeLog(orderId);
+            return R.data(orderDetail);
         } catch (Exception e) {
             log.error("查询订单详情失败: {}", e.getMessage(), e);
             return R.fail("查询订单详情失败: " + e.getMessage());
@@ -414,6 +410,7 @@ public class StoreOrderController {
         }
     }
 
+
     @ApiOperation(value = "分页查询订单列表", notes = "分页查询订单列表,包含订单中的菜品数量、菜品名称、菜品图片")
     @GetMapping("/page")
     public R<IPage<StoreOrderPageVO>> getOrderPage(
@@ -437,9 +434,9 @@ public class StoreOrderController {
         }
     }
 
-    @ApiOperation(value = "查询我的订单", notes = "通过标识查询我的未支付订单或历史订单。type: 0或unpaid-未支付订单, 1或history-历史订单")
+    @ApiOperation(value = "查询我的订单", notes = "通过标识查询我的未支付订单或历史订单,包含订单中的菜品数量、菜品名称、菜品图片。type: 0或unpaid-未支付订单, 1或history-历史订单")
     @GetMapping("/my-orders")
-    public R<IPage<StoreOrder>> getMyOrders(
+    public R<IPage<StoreOrderPageVO>> getMyOrders(
             @ApiParam(value = "页码", required = true) @RequestParam(defaultValue = "1") Long current,
             @ApiParam(value = "每页数量", required = true) @RequestParam(defaultValue = "10") Long size,
             @ApiParam(value = "订单类型(0或unpaid:未支付订单, 1或history:历史订单)", required = true) @RequestParam String type) {
@@ -450,7 +447,7 @@ public class StoreOrderController {
                 return R.fail("用户未登录");
             }
             Page<StoreOrder> page = new Page<>(current, size);
-            IPage<StoreOrder> result = orderService.getMyOrders(page, type);
+            IPage<StoreOrderPageVO> result = orderService.getMyOrdersWithCuisines(page, type);
             return R.data(result);
         } catch (Exception e) {
             log.error("查询我的订单失败: {}", e.getMessage(), e);
@@ -458,7 +455,7 @@ public class StoreOrderController {
         }
     }
 
-    @ApiOperation(value = "换桌", notes = "换桌并迁移购物车")
+    @ApiOperation(value = "换桌", notes = "换桌并迁移购物车、未完成的订单以及其他关联表数据")
     @PostMapping("/change-table")
     public R<CartDTO> changeTable(@Valid @RequestBody ChangeTableDTO dto) {
         try {
@@ -467,26 +464,9 @@ public class StoreOrderController {
             if (userId == null) {
                 return R.fail("用户未登录");
             }
-            
-            // 迁移购物车
-            CartDTO cart = cartService.migrateCart(dto.getFromTableId(), dto.getToTableId());
 
-            // 查询桌号信息
-            StoreTable fromTable = storeTableMapper.selectById(dto.getFromTableId());
-            StoreTable toTable = storeTableMapper.selectById(dto.getToTableId());
-
-            StoreTableLog log = new StoreTableLog();
-            log.setStoreId(cart.getStoreId());
-            log.setFromTableId(dto.getFromTableId());
-            log.setFromTableNumber(fromTable != null ? fromTable.getTableNumber() : null);
-            log.setToTableId(dto.getToTableId());
-            log.setToTableNumber(toTable != null ? toTable.getTableNumber() : null);
-            log.setChangeReason(dto.getChangeReason());
-            log.setCreatedUserId(userId);
-            storeTableLogMapper.insert(log);
-
-            // 推送购物车更新消息到新桌号
-            sseService.pushCartUpdate(dto.getToTableId(), cart);
+            // 调用Service层处理换桌业务逻辑
+            CartDTO cart = orderService.changeTable(dto.getFromTableId(), dto.getToTableId(), dto.getChangeReason(), userId);
 
             return R.data(cart);
         } catch (Exception e) {

+ 37 - 0
alien-dining/src/main/java/shop/alien/dining/service/StoreOrderService.java

@@ -135,6 +135,14 @@ public interface StoreOrderService extends IService<StoreOrder> {
     List<OrderChangeLogBatchVO> getOrderChangeLogs(Integer orderId);
 
     /**
+     * 查询订单详情(包含订单基本信息和按批次分组的变更记录)
+     *
+     * @param orderId 订单ID
+     * @return 订单详情(包含变更记录)
+     */
+    shop.alien.entity.store.vo.OrderDetailWithChangeLogVO getOrderDetailWithChangeLog(Integer orderId);
+
+    /**
      * 支付完成后重置餐桌(保留订单数据,只重置餐桌绑定关系)
      *
      * @param tableId 餐桌ID
@@ -149,4 +157,33 @@ public interface StoreOrderService extends IService<StoreOrder> {
      * @return 订单分页列表
      */
     IPage<StoreOrder> getMyOrders(Page<StoreOrder> page, String type);
+
+    /**
+     * 查询我的订单(包含菜品信息)
+     *
+     * @param page 分页参数
+     * @param type 订单类型(0或"unpaid":未支付订单, 1或"history":历史订单)
+     * @return 订单分页列表(包含菜品信息)
+     */
+    IPage<StoreOrderPageVO> getMyOrdersWithCuisines(Page<StoreOrder> page, String type);
+
+    /**
+     * 换桌时迁移所有关联数据(订单、订单变更记录、优惠券使用记录等)
+     *
+     * @param fromTableId 原桌号ID
+     * @param toTableId   目标桌号ID
+     * @param userId      操作用户ID
+     */
+    void migrateTableData(Integer fromTableId, Integer toTableId, Integer userId);
+
+    /**
+     * 换桌(包含所有业务逻辑:迁移购物车、订单、优惠券等,记录日志,推送消息)
+     *
+     * @param fromTableId  原桌号ID
+     * @param toTableId    目标桌号ID
+     * @param changeReason 换桌原因
+     * @param userId       操作用户ID
+     * @return 迁移后的购物车信息
+     */
+    shop.alien.entity.store.dto.CartDTO changeTable(Integer fromTableId, Integer toTableId, String changeReason, Integer userId);
 }

+ 283 - 3
alien-dining/src/main/java/shop/alien/dining/service/impl/StoreOrderServiceImpl.java

@@ -23,6 +23,7 @@ import shop.alien.entity.store.vo.OrderChangeLogItemVO;
 import shop.alien.entity.store.vo.OrderInfoVO;
 import shop.alien.entity.store.vo.StoreOrderPageVO;
 import shop.alien.entity.store.vo.OrderCuisineItemVO;
+import shop.alien.entity.store.vo.OrderDetailWithChangeLogVO;
 import shop.alien.mapper.*;
 
 import java.math.BigDecimal;
@@ -54,6 +55,7 @@ public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOr
     private final BaseRedisService baseRedisService;
     private final StoreInfoMapper storeInfoMapper;
     private final StoreOrderChangeLogMapper orderChangeLogMapper;
+    private final shop.alien.dining.service.SseService sseService;
 
     @Override
     public StoreOrder createOrder(CreateOrderDTO dto) {
@@ -965,6 +967,70 @@ public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOr
     }
 
     @Override
+    public IPage<StoreOrderPageVO> getMyOrdersWithCuisines(Page<StoreOrder> page, String type) {
+        log.info("查询我的订单(包含菜品信息), type={}", type);
+
+        // 1. 查询订单列表
+        IPage<StoreOrder> orderPage = getMyOrders(page, type);
+
+        // 2. 获取所有订单ID
+        List<StoreOrder> orders = orderPage.getRecords();
+        if (orders == null || orders.isEmpty()) {
+            // 如果没有订单,返回空的分页结果
+            Page<StoreOrderPageVO> resultPage = new Page<>(page.getCurrent(), page.getSize());
+            resultPage.setTotal(orderPage.getTotal());
+            resultPage.setPages(orderPage.getPages());
+            return resultPage;
+        }
+
+        List<Integer> orderIds = orders.stream()
+                .map(StoreOrder::getId)
+                .collect(Collectors.toList());
+
+        // 3. 批量查询所有订单的菜品详情
+        LambdaQueryWrapper<StoreOrderDetail> detailWrapper = new LambdaQueryWrapper<>();
+        detailWrapper.in(StoreOrderDetail::getOrderId, orderIds);
+        detailWrapper.eq(StoreOrderDetail::getDeleteFlag, 0);
+        detailWrapper.orderByAsc(StoreOrderDetail::getOrderId);
+        detailWrapper.orderByAsc(StoreOrderDetail::getCreatedTime);
+        List<StoreOrderDetail> allDetails = orderDetailMapper.selectList(detailWrapper);
+
+        // 4. 按订单ID分组菜品详情
+        Map<Integer, List<StoreOrderDetail>> detailsMap = allDetails.stream()
+                .collect(Collectors.groupingBy(StoreOrderDetail::getOrderId));
+
+        // 5. 构建返回结果
+        List<StoreOrderPageVO> voList = orders.stream().map(order -> {
+            StoreOrderPageVO vo = new StoreOrderPageVO();
+            vo.setOrder(order);
+
+            // 获取该订单的菜品列表
+            List<StoreOrderDetail> orderDetails = detailsMap.getOrDefault(order.getId(), new ArrayList<>());
+            List<OrderCuisineItemVO> cuisineItems = orderDetails.stream()
+                    .map(detail -> {
+                        OrderCuisineItemVO item = new OrderCuisineItemVO();
+                        item.setCuisineId(detail.getCuisineId());
+                        item.setCuisineName(detail.getCuisineName());
+                        item.setCuisineImage(detail.getCuisineImage());
+                        item.setQuantity(detail.getQuantity());
+                        return item;
+                    })
+                    .collect(Collectors.toList());
+            vo.setCuisineItems(cuisineItems);
+
+            return vo;
+        }).collect(Collectors.toList());
+
+        // 6. 构建分页结果
+        Page<StoreOrderPageVO> resultPage = new Page<>(page.getCurrent(), page.getSize());
+        resultPage.setRecords(voList);
+        resultPage.setTotal(orderPage.getTotal());
+        resultPage.setPages(orderPage.getPages());
+
+        return resultPage;
+    }
+
+    @Override
     public OrderInfoVO getOrderInfo(Integer orderId) {
         log.info("查询订单信息, orderId={}", orderId);
         
@@ -999,8 +1065,21 @@ public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOr
         
         // 3. 查询门店信息
         StoreInfo storeInfo = storeInfoMapper.selectById(order.getStoreId());
-        String storeName = storeInfo != null ? storeInfo.getStoreName() : "";
-        
+        String storeName = "";
+        String storeTel = "";
+        String storeAddress = "";
+        String storeBlurb = "";
+        String storeType = "";
+        Integer businessStatus = null;
+        if (storeInfo != null) {
+            storeName = storeInfo.getStoreName();
+            storeTel = storeInfo.getStoreTel();
+            storeAddress = storeInfo.getStoreAddress();
+            storeBlurb = storeInfo.getStoreBlurb();
+            storeType = storeInfo.getStoreType();
+            businessStatus = storeInfo.getBusinessStatus();
+        }
+
         // 4. 查询优惠券信息(如果有)
         String couponName = null;
         if (order.getCouponId() != null) {
@@ -1009,12 +1088,18 @@ public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOr
                 couponName = coupon.getName();
             }
         }
-        
+
         // 5. 组装OrderInfoVO
         OrderInfoVO vo = new OrderInfoVO();
         vo.setOrderId(order.getId());
         vo.setOrderNo(order.getOrderNo());
+        vo.setStoreId(order.getStoreId());
         vo.setStoreName(storeName);
+        vo.setStoreTel(storeTel);
+        vo.setStoreAddress(storeAddress);
+        vo.setStoreBlurb(storeBlurb);
+        vo.setStoreType(storeType);
+        vo.setBusinessStatus(businessStatus);
         vo.setTableNumber(order.getTableNumber());
         vo.setDinerCount(order.getDinerCount());
         vo.setContactPhone(order.getContactPhone());
@@ -1113,6 +1198,58 @@ public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOr
         log.info("查询订单变更记录完成, orderId={}, batchCount={}", orderId, batchList.size());
         return batchList;
     }
+
+    @Override
+    public OrderDetailWithChangeLogVO getOrderDetailWithChangeLog(Integer orderId) {
+        log.info("查询订单详情(包含变更记录), orderId={}", orderId);
+
+        // 1. 查询订单基本信息
+        StoreOrder order = this.getById(orderId);
+        if (order == null || order.getDeleteFlag() == 1) {
+            throw new RuntimeException("订单不存在");
+        }
+
+        // 2. 查询门店信息
+        StoreInfo storeInfo = storeInfoMapper.selectById(order.getStoreId());
+        String storeName = storeInfo != null ? storeInfo.getStoreName() : "";
+
+        // 3. 查询优惠券信息(如果有)
+        String couponName = null;
+        if (order.getCouponId() != null) {
+            LifeDiscountCoupon coupon = lifeDiscountCouponMapper.selectById(order.getCouponId());
+            if (coupon != null) {
+                couponName = coupon.getName();
+            }
+        }
+
+        // 4. 查询订单变更记录(按批次分组)
+        List<OrderChangeLogBatchVO> changeLogBatches = getOrderChangeLogs(orderId);
+
+        // 5. 组装OrderDetailWithChangeLogVO
+        OrderDetailWithChangeLogVO vo = new OrderDetailWithChangeLogVO();
+        vo.setOrderId(order.getId());
+        vo.setOrderNo(order.getOrderNo());
+        vo.setStoreName(storeName);
+        vo.setTableNumber(order.getTableNumber());
+        vo.setDinerCount(order.getDinerCount());
+        vo.setContactPhone(order.getContactPhone());
+        vo.setRemark(order.getRemark());
+        vo.setTotalAmount(order.getTotalAmount());
+        vo.setTablewareFee(order.getTablewareFee());
+        vo.setCouponId(order.getCouponId());
+        vo.setCouponName(couponName);
+        vo.setDiscountAmount(order.getDiscountAmount());
+        vo.setPayAmount(order.getPayAmount());
+        vo.setOrderStatus(order.getOrderStatus());
+        vo.setPayStatus(order.getPayStatus());
+        vo.setPayType(order.getPayType());
+        vo.setCreatedTime(order.getCreatedTime());
+        vo.setPayTime(order.getPayTime());
+        vo.setChangeLogBatches(changeLogBatches);
+
+        log.info("查询订单详情(包含变更记录)完成, orderId={}, batchCount={}", orderId, changeLogBatches.size());
+        return vo;
+    }
     
     /**
      * 获取操作类型文本
@@ -1249,4 +1386,147 @@ public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOr
         String random = String.valueOf((int) (Math.random() * 10000));
         return "ORD" + timestamp + String.format("%04d", Integer.parseInt(random));
     }
+
+    @Override
+    public void migrateTableData(Integer fromTableId, Integer toTableId, Integer userId) {
+        log.info("换桌迁移数据, fromTableId={}, toTableId={}, userId={}", fromTableId, toTableId, userId);
+
+        // 1. 查询原桌号信息
+        StoreTable fromTable = storeTableMapper.selectById(fromTableId);
+        if (fromTable == null) {
+            throw new RuntimeException("原桌号不存在");
+        }
+
+        // 2. 查询目标桌号信息
+        StoreTable toTable = storeTableMapper.selectById(toTableId);
+        if (toTable == null) {
+            throw new RuntimeException("目标桌号不存在");
+        }
+
+        // 3. 迁移未完成的订单(orderStatus=0 且 payStatus=0)
+        if (fromTable.getCurrentOrderId() != null) {
+            StoreOrder pendingOrder = this.getById(fromTable.getCurrentOrderId());
+            if (pendingOrder != null && pendingOrder.getOrderStatus() == 0 && pendingOrder.getPayStatus() == 0) {
+                // 更新订单的桌号信息
+                pendingOrder.setTableId(toTableId);
+                pendingOrder.setTableNumber(toTable.getTableNumber());
+                pendingOrder.setUpdatedUserId(userId);
+                pendingOrder.setUpdatedTime(new Date());
+                this.updateById(pendingOrder);
+                log.info("迁移未完成订单, orderId={}, fromTableId={}, toTableId={}", pendingOrder.getId(), fromTableId, toTableId);
+
+                // 更新订单明细的订单号(如果需要,但通常订单号不变,所以这里只更新 table_id 关联)
+                // 订单明细通过 order_id 关联,不需要单独更新
+            }
+        }
+
+        // 4. 迁移所有未完成的订单(包括可能存在的其他未完成订单)
+        LambdaQueryWrapper<StoreOrder> orderWrapper = new LambdaQueryWrapper<>();
+        orderWrapper.eq(StoreOrder::getTableId, fromTableId);
+        orderWrapper.eq(StoreOrder::getOrderStatus, 0); // 待支付
+        orderWrapper.eq(StoreOrder::getPayStatus, 0); // 未支付
+        orderWrapper.eq(StoreOrder::getDeleteFlag, 0);
+        List<StoreOrder> pendingOrders = this.list(orderWrapper);
+        
+        if (pendingOrders != null && !pendingOrders.isEmpty()) {
+            for (StoreOrder order : pendingOrders) {
+                order.setTableId(toTableId);
+                order.setTableNumber(toTable.getTableNumber());
+                order.setUpdatedUserId(userId);
+                order.setUpdatedTime(new Date());
+            }
+            this.updateBatchById(pendingOrders);
+            log.info("迁移未完成订单, count={}, fromTableId={}, toTableId={}", pendingOrders.size(), fromTableId, toTableId);
+        }
+
+        // 5. 迁移优惠券使用记录
+        LambdaQueryWrapper<StoreCouponUsage> couponUsageWrapper = new LambdaQueryWrapper<>();
+        couponUsageWrapper.eq(StoreCouponUsage::getTableId, fromTableId);
+        couponUsageWrapper.eq(StoreCouponUsage::getDeleteFlag, 0);
+        couponUsageWrapper.in(StoreCouponUsage::getUsageStatus, 0, 1, 2); // 已标记使用、已下单、已支付
+        List<StoreCouponUsage> couponUsages = storeCouponUsageMapper.selectList(couponUsageWrapper);
+        
+        if (couponUsages != null && !couponUsages.isEmpty()) {
+            Date now = new Date();
+            for (StoreCouponUsage usage : couponUsages) {
+                usage.setFromTableId(fromTableId);
+                usage.setTableId(toTableId);
+                usage.setMigrateTime(now);
+                usage.setUpdatedUserId(userId);
+                usage.setUpdatedTime(now);
+            }
+            // 批量更新
+            for (StoreCouponUsage usage : couponUsages) {
+                storeCouponUsageMapper.updateById(usage);
+            }
+            log.info("迁移优惠券使用记录, count={}, fromTableId={}, toTableId={}", couponUsages.size(), fromTableId, toTableId);
+        }
+
+        // 6. 更新桌号表的关联信息
+        // 原桌号:清空 currentOrderId 和 currentCouponId
+        LambdaUpdateWrapper<StoreTable> fromTableWrapper = new LambdaUpdateWrapper<>();
+        fromTableWrapper.eq(StoreTable::getId, fromTableId)
+                .set(StoreTable::getCurrentOrderId, null)
+                .set(StoreTable::getCurrentCouponId, null)
+                .set(StoreTable::getUpdatedTime, new Date());
+        if (userId != null) {
+            fromTableWrapper.set(StoreTable::getUpdatedUserId, userId);
+        }
+        storeTableMapper.update(null, fromTableWrapper);
+        log.info("清空原桌号关联信息, fromTableId={}", fromTableId);
+
+        // 目标桌号:设置 currentOrderId 和 currentCouponId(如果有)
+        if (fromTable.getCurrentOrderId() != null || fromTable.getCurrentCouponId() != null) {
+            LambdaUpdateWrapper<StoreTable> toTableWrapper = new LambdaUpdateWrapper<>();
+            toTableWrapper.eq(StoreTable::getId, toTableId);
+            
+            if (fromTable.getCurrentOrderId() != null) {
+                toTableWrapper.set(StoreTable::getCurrentOrderId, fromTable.getCurrentOrderId());
+            }
+            if (fromTable.getCurrentCouponId() != null) {
+                toTableWrapper.set(StoreTable::getCurrentCouponId, fromTable.getCurrentCouponId());
+            }
+            toTableWrapper.set(StoreTable::getUpdatedTime, new Date());
+            if (userId != null) {
+                toTableWrapper.set(StoreTable::getUpdatedUserId, userId);
+            }
+            storeTableMapper.update(null, toTableWrapper);
+            log.info("设置目标桌号关联信息, toTableId={}, currentOrderId={}, currentCouponId={}", 
+                    toTableId, fromTable.getCurrentOrderId(), fromTable.getCurrentCouponId());
+        }
+
+        log.info("换桌数据迁移完成, fromTableId={}, toTableId={}", fromTableId, toTableId);
+    }
+
+    @Override
+    public shop.alien.entity.store.dto.CartDTO changeTable(Integer fromTableId, Integer toTableId, String changeReason, Integer userId) {
+        log.info("换桌, fromTableId={}, toTableId={}, changeReason={}, userId={}", fromTableId, toTableId, changeReason, userId);
+
+        // 1. 迁移购物车
+        shop.alien.entity.store.dto.CartDTO cart = cartService.migrateCart(fromTableId, toTableId);
+
+        // 2. 迁移所有关联数据(订单、订单变更记录、优惠券使用记录等)
+        migrateTableData(fromTableId, toTableId, userId);
+
+        // 3. 查询桌号信息
+        StoreTable fromTable = storeTableMapper.selectById(fromTableId);
+        StoreTable toTable = storeTableMapper.selectById(toTableId);
+
+        // 4. 记录换桌日志
+        StoreTableLog tableLog = new StoreTableLog();
+        tableLog.setStoreId(cart.getStoreId());
+        tableLog.setFromTableId(fromTableId);
+        tableLog.setFromTableNumber(fromTable != null ? fromTable.getTableNumber() : null);
+        tableLog.setToTableId(toTableId);
+        tableLog.setToTableNumber(toTable != null ? toTable.getTableNumber() : null);
+        tableLog.setChangeReason(changeReason);
+        tableLog.setCreatedUserId(userId);
+        storeTableLogMapper.insert(tableLog);
+
+        // 5. 推送购物车更新消息到新桌号
+        sseService.pushCartUpdate(toTableId, cart);
+
+        log.info("换桌完成, fromTableId={}, toTableId={}", fromTableId, toTableId);
+        return cart;
+    }
 }

+ 77 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/OrderDetailWithChangeLogVO.java

@@ -0,0 +1,77 @@
+package shop.alien.entity.store.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 订单详情VO(包含订单基本信息和按批次分组的变更记录)
+ *
+ * @author system
+ * @since 2025-02-02
+ */
+@Data
+@ApiModel(value = "OrderDetailWithChangeLogVO对象", description = "订单详情(包含变更记录)")
+public class OrderDetailWithChangeLogVO {
+
+    @ApiModelProperty(value = "订单ID")
+    private Integer orderId;
+
+    @ApiModelProperty(value = "订单号")
+    private String orderNo;
+
+    @ApiModelProperty(value = "店铺名称")
+    private String storeName;
+
+    @ApiModelProperty(value = "桌号")
+    private String tableNumber;
+
+    @ApiModelProperty(value = "就餐人数")
+    private Integer dinerCount;
+
+    @ApiModelProperty(value = "联系电话")
+    private String contactPhone;
+
+    @ApiModelProperty(value = "备注")
+    private String remark;
+
+    @ApiModelProperty(value = "菜品总价")
+    private BigDecimal totalAmount;
+
+    @ApiModelProperty(value = "餐具费")
+    private BigDecimal tablewareFee;
+
+    @ApiModelProperty(value = "优惠券ID")
+    private Integer couponId;
+
+    @ApiModelProperty(value = "优惠券名称")
+    private String couponName;
+
+    @ApiModelProperty(value = "优惠金额")
+    private BigDecimal discountAmount;
+
+    @ApiModelProperty(value = "应付金额")
+    private BigDecimal payAmount;
+
+    @ApiModelProperty(value = "订单状态(0:待支付, 1:已支付, 2:已取消, 3:已完成)")
+    private Integer orderStatus;
+
+    @ApiModelProperty(value = "支付状态(0:未支付, 1:已支付, 2:已退款)")
+    private Integer payStatus;
+
+    @ApiModelProperty(value = "支付方式(1:微信, 2:支付宝, 3:现金)")
+    private Integer payType;
+
+    @ApiModelProperty(value = "创建时间")
+    private Date createdTime;
+
+    @ApiModelProperty(value = "支付时间")
+    private Date payTime;
+
+    @ApiModelProperty(value = "订单变更记录批次列表(按批次分组,展示每次下单/加餐都加了什么)")
+    private List<OrderChangeLogBatchVO> changeLogBatches;
+}

+ 18 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/OrderInfoVO.java

@@ -28,6 +28,24 @@ public class OrderInfoVO {
     @ApiModelProperty(value = "店铺名称")
     private String storeName;
 
+    @ApiModelProperty(value = "店铺ID")
+    private Integer storeId;
+
+    @ApiModelProperty(value = "门店电话")
+    private String storeTel;
+
+    @ApiModelProperty(value = "门店地址")
+    private String storeAddress;
+
+    @ApiModelProperty(value = "门店简介")
+    private String storeBlurb;
+
+    @ApiModelProperty(value = "门店类型(1:中餐, 2:烧烤, 3:饮品, 4:甜点, 5:火锅, 6:宵夜, 7:西餐, 8:轻食, 9:水果)")
+    private String storeType;
+
+    @ApiModelProperty(value = "营业状态(-1:注销中, 0:正常营业, 1:暂停营业, 2:筹建中, 99:永久关门)")
+    private Integer businessStatus;
+
     @ApiModelProperty(value = "桌号")
     private String tableNumber;