|
@@ -0,0 +1,2020 @@
|
|
|
|
|
+package shop.alien.store.dining.service.impl;
|
|
|
|
|
+
|
|
|
|
|
+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.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
|
|
+import shop.alien.store.config.BaseRedisService;
|
|
|
|
|
+import shop.alien.store.dining.service.CartService;
|
|
|
|
|
+import shop.alien.store.dining.service.StoreOrderService;
|
|
|
|
|
+import shop.alien.store.dining.util.TokenUtil;
|
|
|
|
|
+import shop.alien.entity.store.*;
|
|
|
|
|
+
|
|
|
|
|
+import shop.alien.entity.store.dto.CartDTO;
|
|
|
|
|
+import shop.alien.entity.store.dto.CartItemDTO;
|
|
|
|
|
+import shop.alien.entity.store.dto.CreateOrderDTO;
|
|
|
|
|
+import shop.alien.entity.store.vo.OrderChangeLogBatchVO;
|
|
|
|
|
+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;
|
|
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 订单服务实现类
|
|
|
|
|
+ *
|
|
|
|
|
+ * @author system
|
|
|
|
|
+ * @since 2025-01-XX
|
|
|
|
|
+ */
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+@Service
|
|
|
|
|
+@RequiredArgsConstructor
|
|
|
|
|
+@Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOrder> implements StoreOrderService {
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ private final StoreOrderDetailMapper orderDetailMapper;
|
|
|
|
|
+ private final StoreTableMapper storeTableMapper;
|
|
|
|
|
+ private final StoreTableLogMapper storeTableLogMapper;
|
|
|
|
|
+ private final StoreCuisineMapper storeCuisineMapper;
|
|
|
|
|
+ private final LifeDiscountCouponMapper lifeDiscountCouponMapper;
|
|
|
|
|
+ private final CartService cartService;
|
|
|
|
|
+ private final StoreOrderLockMapper storeOrderLockMapper;
|
|
|
|
|
+ private final StoreCouponUsageMapper storeCouponUsageMapper;
|
|
|
|
|
+ private final StoreCartMapper storeCartMapper;
|
|
|
|
|
+ private final BaseRedisService baseRedisService;
|
|
|
|
|
+ private final StoreInfoMapper storeInfoMapper;
|
|
|
|
|
+ private final StoreOrderChangeLogMapper orderChangeLogMapper;
|
|
|
|
|
+ private final shop.alien.store.dining.service.SseService sseService;
|
|
|
|
|
+ private final shop.alien.store.dining.service.OrderLockService orderLockService;
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public StoreOrder createOrder(CreateOrderDTO dto) {
|
|
|
|
|
+ log.info("创建订单, dto={}", dto);
|
|
|
|
|
+
|
|
|
|
|
+ // 获取当前用户信息
|
|
|
|
|
+ Object[] userInfo = getCurrentUserInfo();
|
|
|
|
|
+ Integer userId = (Integer) userInfo[0];
|
|
|
|
|
+ String userPhone = (String) userInfo[1];
|
|
|
|
|
+
|
|
|
|
|
+ // 检查订单锁定状态
|
|
|
|
|
+ Integer lockUserId = orderLockService.checkOrderLock(dto.getTableId());
|
|
|
|
|
+ if (lockUserId != null && !lockUserId.equals(userId)) {
|
|
|
|
|
+ throw new RuntimeException("订单已被其他用户锁定,无法下单");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 验证桌号
|
|
|
|
|
+ StoreTable table = storeTableMapper.selectById(dto.getTableId());
|
|
|
|
|
+ if (table == null) {
|
|
|
|
|
+ throw new RuntimeException("桌号不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 查询门店信息(用于获取餐具费)
|
|
|
|
|
+ StoreInfo storeInfo = storeInfoMapper.selectById(table.getStoreId());
|
|
|
|
|
+ if (storeInfo == null) {
|
|
|
|
|
+ throw new RuntimeException("门店不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取购物车
|
|
|
|
|
+ CartDTO cart = cartService.getCart(dto.getTableId());
|
|
|
|
|
+ if (cart.getItems() == null || cart.getItems().isEmpty()) {
|
|
|
|
|
+ throw new RuntimeException("购物车为空");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检查是否有新增商品或商品数量增加
|
|
|
|
|
+ boolean hasNewItems = false; // 是否有新增商品(未下单过的商品)
|
|
|
|
|
+ boolean hasQuantityIncrease = false; // 是否有商品数量增加
|
|
|
|
|
+
|
|
|
|
|
+ for (shop.alien.entity.store.dto.CartItemDTO item : cart.getItems()) {
|
|
|
|
|
+ Integer lockedQuantity = item.getLockedQuantity();
|
|
|
|
|
+ Integer currentQuantity = item.getQuantity();
|
|
|
|
|
+
|
|
|
|
|
+ if (lockedQuantity == null || lockedQuantity == 0) {
|
|
|
|
|
+ // 有新增商品(未下单过的商品)
|
|
|
|
|
+ hasNewItems = true;
|
|
|
|
|
+ log.debug("发现新增商品, cuisineId={}, quantity={}", item.getCuisineId(), currentQuantity);
|
|
|
|
|
+ } else if (currentQuantity != null && currentQuantity > lockedQuantity) {
|
|
|
|
|
+ // 有商品数量增加(当前数量大于已下单数量)
|
|
|
|
|
+ hasQuantityIncrease = true;
|
|
|
|
|
+ log.debug("发现商品数量增加, cuisineId={}, currentQuantity={}, orderedQuantity={}",
|
|
|
|
|
+ item.getCuisineId(), currentQuantity, lockedQuantity);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果没有新增商品且没有商品数量增加,不允许创建/更新订单(下单后只能增不能减,不支持减量同步)
|
|
|
|
|
+ if (!hasNewItems && !hasQuantityIncrease) {
|
|
|
|
|
+ throw new RuntimeException("购物车中没有新增商品或商品数量未增加,无法创建订单");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 直接使用前端传入的值
|
|
|
|
|
+ BigDecimal totalAmount = dto.getTotalAmount() != null ? dto.getTotalAmount() : BigDecimal.ZERO;
|
|
|
|
|
+ BigDecimal tablewareFee = dto.getTablewareFee() != null ? dto.getTablewareFee() : BigDecimal.ZERO;
|
|
|
|
|
+ BigDecimal discountAmount = dto.getDiscountAmount() != null ? dto.getDiscountAmount() : BigDecimal.ZERO;
|
|
|
|
|
+ BigDecimal payAmount = dto.getPayAmount() != null ? dto.getPayAmount() : BigDecimal.ZERO;
|
|
|
|
|
+
|
|
|
|
|
+ // 优惠券仅校验是否存在及是否属于该门店,不做金额校验;订单总金额、餐具费、优惠金额、实付金额完全采用前端传参
|
|
|
|
|
+ LifeDiscountCoupon coupon = null;
|
|
|
|
|
+
|
|
|
|
|
+ if (dto.getCouponId() != null) {
|
|
|
|
|
+ // 检查桌号是否已使用优惠券,如果已使用则替换为新优惠券
|
|
|
|
|
+ if (cartService.hasUsedCoupon(dto.getTableId())) {
|
|
|
|
|
+ // 获取旧的优惠券使用记录
|
|
|
|
|
+ LambdaQueryWrapper<StoreCouponUsage> oldUsageWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ oldUsageWrapper.eq(StoreCouponUsage::getTableId, dto.getTableId());
|
|
|
|
|
+ oldUsageWrapper.eq(StoreCouponUsage::getDeleteFlag, 0);
|
|
|
|
|
+ oldUsageWrapper.in(StoreCouponUsage::getUsageStatus, 0, 1); // 已标记使用、已下单
|
|
|
|
|
+ oldUsageWrapper.orderByDesc(StoreCouponUsage::getCreatedTime);
|
|
|
|
|
+ oldUsageWrapper.last("LIMIT 1");
|
|
|
|
|
+ StoreCouponUsage oldUsage = storeCouponUsageMapper.selectOne(oldUsageWrapper);
|
|
|
|
|
+
|
|
|
|
|
+ if (oldUsage != null) {
|
|
|
|
|
+ // 如果旧优惠券已经关联到订单(已下单状态),需要将旧优惠券使用记录状态改为"已取消"
|
|
|
|
|
+ if (oldUsage.getUsageStatus() == 1 && oldUsage.getOrderId() != null) {
|
|
|
|
|
+ oldUsage.setUsageStatus(3); // 已取消
|
|
|
|
|
+ oldUsage.setUpdatedTime(new Date());
|
|
|
|
|
+ storeCouponUsageMapper.updateById(oldUsage);
|
|
|
|
|
+ log.info("替换优惠券:取消旧优惠券使用记录, tableId={}, oldCouponId={}, newCouponId={}",
|
|
|
|
|
+ dto.getTableId(), oldUsage.getCouponId(), dto.getCouponId());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 如果只是已标记使用但未下单,直接逻辑删除
|
|
|
|
|
+ storeCouponUsageMapper.deleteById(oldUsage.getId());
|
|
|
|
|
+ log.info("替换优惠券:删除未下单的旧优惠券使用记录, tableId={}, oldCouponId={}, newCouponId={}",
|
|
|
|
|
+ dto.getTableId(), oldUsage.getCouponId(), dto.getCouponId());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 清除旧的优惠券使用标记
|
|
|
|
|
+ cartService.clearCouponUsed(dto.getTableId());
|
|
|
|
|
+ log.info("替换优惠券:清除旧优惠券标记, tableId={}, newCouponId={}", dto.getTableId(), dto.getCouponId());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 验证优惠券(只查询一次)
|
|
|
|
|
+ coupon = lifeDiscountCouponMapper.selectById(dto.getCouponId());
|
|
|
|
|
+ if (coupon == null) {
|
|
|
|
|
+ throw new RuntimeException("优惠券不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 验证优惠券是否属于该门店(空安全:coupon.storeId 为 String,table.storeId 为 Integer)
|
|
|
|
|
+ String couponStoreId = coupon.getStoreId();
|
|
|
|
|
+ Integer tableStoreId = table.getStoreId();
|
|
|
|
|
+ if (couponStoreId == null || tableStoreId == null
|
|
|
|
|
+ || !couponStoreId.equals(String.valueOf(tableStoreId))) {
|
|
|
|
|
+ throw new RuntimeException("优惠券不属于该门店");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 标记桌号已使用新优惠券
|
|
|
|
|
+ cartService.markCouponUsed(dto.getTableId(), dto.getCouponId());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 直接使用前端传入的值,如果为null则设为0
|
|
|
|
|
+ if (discountAmount == null) {
|
|
|
|
|
+ discountAmount = BigDecimal.ZERO;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ StoreOrder order = null;
|
|
|
|
|
+ String orderNo = null;
|
|
|
|
|
+ boolean isUpdate = false; // 是否是更新订单
|
|
|
|
|
+
|
|
|
|
|
+ // 检查桌号是否已绑定订单
|
|
|
|
|
+ if (table.getCurrentOrderId() != null) {
|
|
|
|
|
+ // 查询已存在的订单
|
|
|
|
|
+ StoreOrder existingOrder = this.getById(table.getCurrentOrderId());
|
|
|
|
|
+ if (existingOrder != null && existingOrder.getOrderStatus() == 0 && existingOrder.getPayStatus() == 0) {
|
|
|
|
|
+ // 订单存在且是待支付状态,更新订单
|
|
|
|
|
+ isUpdate = true;
|
|
|
|
|
+ order = existingOrder;
|
|
|
|
|
+ orderNo = order.getOrderNo(); // 使用原订单号
|
|
|
|
|
+ log.info("桌号已绑定订单,更新订单信息, orderId={}, orderNo={}", order.getId(), orderNo);
|
|
|
|
|
+
|
|
|
|
|
+ // 更新订单信息(完全采用前端传参,不做金额校验)
|
|
|
|
|
+ order.setDinerCount(dto.getDinerCount());
|
|
|
|
|
+ order.setContactPhone(dto.getContactPhone());
|
|
|
|
|
+ order.setTablewareFee(tablewareFee);
|
|
|
|
|
+ order.setTotalAmount(totalAmount);
|
|
|
|
|
+ order.setCouponId(dto.getCouponId());
|
|
|
|
|
+ order.setCurrentCouponId(dto.getCouponId());
|
|
|
|
|
+ order.setDiscountAmount(discountAmount);
|
|
|
|
|
+ order.setPayAmount(payAmount);
|
|
|
|
|
+ order.setRemark(dto.getRemark());
|
|
|
|
|
+ order.setUpdatedUserId(userId);
|
|
|
|
|
+ order.setUpdatedTime(now);
|
|
|
|
|
+ this.updateById(order);
|
|
|
|
|
+
|
|
|
|
|
+ // 逻辑删除旧的订单明细
|
|
|
|
|
+ LambdaQueryWrapper<StoreOrderDetail> detailWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ detailWrapper.eq(StoreOrderDetail::getOrderId, order.getId());
|
|
|
|
|
+ detailWrapper.eq(StoreOrderDetail::getDeleteFlag, 0);
|
|
|
|
|
+ List<StoreOrderDetail> oldDetails = orderDetailMapper.selectList(detailWrapper);
|
|
|
|
|
+ if (oldDetails != null && !oldDetails.isEmpty()) {
|
|
|
|
|
+ List<Integer> oldDetailIds = oldDetails.stream()
|
|
|
|
|
+ .map(StoreOrderDetail::getId)
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+ orderDetailMapper.deleteBatchIds(oldDetailIds);
|
|
|
|
|
+ log.info("删除旧订单明细, orderId={}, count={}", order.getId(), oldDetailIds.size());
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ log.info("桌号绑定的订单不存在或已支付/已取消,将创建新订单");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果没有订单需要更新,创建新订单
|
|
|
|
|
+ if (!isUpdate) {
|
|
|
|
|
+ // 生成订单号
|
|
|
|
|
+ orderNo = generateOrderNo();
|
|
|
|
|
+
|
|
|
|
|
+ // 创建订单
|
|
|
|
|
+ order = new StoreOrder();
|
|
|
|
|
+ order.setOrderNo(orderNo);
|
|
|
|
|
+ order.setStoreId(table.getStoreId());
|
|
|
|
|
+ order.setTableId(table.getId());
|
|
|
|
|
+ order.setTableNumber(table.getTableNumber());
|
|
|
|
|
+ order.setDinerCount(dto.getDinerCount());
|
|
|
|
|
+ order.setContactPhone(dto.getContactPhone());
|
|
|
|
|
+ order.setTablewareFee(tablewareFee);
|
|
|
|
|
+ order.setPayUserId(userId);
|
|
|
|
|
+ order.setPayUserPhone(userPhone);
|
|
|
|
|
+ order.setOrderStatus(0); // 待支付
|
|
|
|
|
+ order.setTotalAmount(totalAmount);
|
|
|
|
|
+ order.setCouponId(dto.getCouponId());
|
|
|
|
|
+ order.setCurrentCouponId(dto.getCouponId());
|
|
|
|
|
+ order.setDiscountAmount(discountAmount);
|
|
|
|
|
+ order.setPayAmount(payAmount);
|
|
|
|
|
+
|
|
|
|
|
+ // 如果immediatePay为0,只创建订单不支付;为1则创建订单并支付
|
|
|
|
|
+ // 暂时不实现立即支付,由前端调用支付接口
|
|
|
|
|
+ // payType在支付时设置
|
|
|
|
|
+ order.setPayStatus(0); // 未支付
|
|
|
|
|
+ order.setRemark(dto.getRemark());
|
|
|
|
|
+ order.setCreatedUserId(userId);
|
|
|
|
|
+ order.setUpdatedUserId(userId);
|
|
|
|
|
+ // 手动设置创建时间和更新时间(临时方案,避免自动填充未生效导致 created_time 为 null)
|
|
|
|
|
+ order.setCreatedTime(now);
|
|
|
|
|
+ order.setUpdatedTime(now);
|
|
|
|
|
+ this.save(order);
|
|
|
|
|
+ log.info("创建新订单, orderId={}, orderNo={}", order.getId(), orderNo);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 确保 order 和 orderNo 已初始化(用于后续代码)
|
|
|
|
|
+ if (order == null || orderNo == null) {
|
|
|
|
|
+ throw new RuntimeException("订单创建失败,系统错误");
|
|
|
|
|
+ }
|
|
|
|
|
+ final StoreOrder finalOrder = order;
|
|
|
|
|
+ final String finalOrderNo = orderNo;
|
|
|
|
|
+ final boolean finalIsUpdate = isUpdate; // 用于 lambda 表达式
|
|
|
|
|
+
|
|
|
|
|
+ // 更新优惠券使用记录状态为已下单
|
|
|
|
|
+ if (dto.getCouponId() != null) {
|
|
|
|
|
+ LambdaQueryWrapper<StoreCouponUsage> usageWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ usageWrapper.eq(StoreCouponUsage::getTableId, dto.getTableId());
|
|
|
|
|
+ usageWrapper.eq(StoreCouponUsage::getCouponId, dto.getCouponId());
|
|
|
|
|
+ usageWrapper.eq(StoreCouponUsage::getDeleteFlag, 0);
|
|
|
|
|
+ usageWrapper.orderByDesc(StoreCouponUsage::getCreatedTime);
|
|
|
|
|
+ usageWrapper.last("LIMIT 1");
|
|
|
|
|
+ StoreCouponUsage usage = storeCouponUsageMapper.selectOne(usageWrapper);
|
|
|
|
|
+ if (usage != null) {
|
|
|
|
|
+ usage.setOrderId(finalOrder.getId());
|
|
|
|
|
+ usage.setUsageStatus(1); // 已下单
|
|
|
|
|
+ usage.setUpdatedTime(new Date());
|
|
|
|
|
+ storeCouponUsageMapper.updateById(usage);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果是更新订单,且优惠券发生变化,需要处理旧优惠券的使用记录
|
|
|
|
|
+ if (isUpdate && finalOrder.getCouponId() != null &&
|
|
|
|
|
+ (dto.getCouponId() == null || !finalOrder.getCouponId().equals(dto.getCouponId()))) {
|
|
|
|
|
+ // 订单的优惠券被替换或取消,需要将旧优惠券使用记录状态改为"已取消"
|
|
|
|
|
+ LambdaQueryWrapper<StoreCouponUsage> oldUsageWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ oldUsageWrapper.eq(StoreCouponUsage::getOrderId, finalOrder.getId());
|
|
|
|
|
+ oldUsageWrapper.eq(StoreCouponUsage::getCouponId, finalOrder.getCouponId());
|
|
|
|
|
+ oldUsageWrapper.eq(StoreCouponUsage::getDeleteFlag, 0);
|
|
|
|
|
+ oldUsageWrapper.orderByDesc(StoreCouponUsage::getCreatedTime);
|
|
|
|
|
+ oldUsageWrapper.last("LIMIT 1");
|
|
|
|
|
+ StoreCouponUsage oldUsage = storeCouponUsageMapper.selectOne(oldUsageWrapper);
|
|
|
|
|
+ if (oldUsage != null) {
|
|
|
|
|
+ oldUsage.setUsageStatus(3); // 已取消
|
|
|
|
|
+ oldUsage.setUpdatedTime(new Date());
|
|
|
|
|
+ storeCouponUsageMapper.updateById(oldUsage);
|
|
|
|
|
+ log.info("更新订单时替换优惠券:取消旧优惠券使用记录, orderId={}, oldCouponId={}, newCouponId={}",
|
|
|
|
|
+ finalOrder.getId(), finalOrder.getCouponId(), dto.getCouponId());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 创建订单明细(单价、小计来自购物车)
|
|
|
|
|
+ // 如果是更新订单,需要判断哪些商品是新增的或数量增加的,标记为加餐
|
|
|
|
|
+ // 餐具的特殊ID(用于标识餐具项)
|
|
|
|
|
+ final Integer TABLEWARE_CUISINE_ID = -1;
|
|
|
|
|
+
|
|
|
|
|
+ List<StoreOrderDetail> orderDetails = cart.getItems().stream()
|
|
|
|
|
+ .map(item -> {
|
|
|
|
|
+ StoreOrderDetail detail = new StoreOrderDetail();
|
|
|
|
|
+ detail.setOrderId(finalOrder.getId());
|
|
|
|
|
+ detail.setOrderNo(finalOrderNo);
|
|
|
|
|
+ detail.setCuisineId(item.getCuisineId());
|
|
|
|
|
+ detail.setCuisineName(item.getCuisineName());
|
|
|
|
|
+
|
|
|
|
|
+ // 设置菜品类型:如果为null,根据是否为餐具设置默认值
|
|
|
|
|
+ Integer cuisineType = item.getCuisineType();
|
|
|
|
|
+ if (cuisineType == null) {
|
|
|
|
|
+ // 如果是餐具,设置为0;否则设置为1(默认单品)
|
|
|
|
|
+ if (TABLEWARE_CUISINE_ID.equals(item.getCuisineId())) {
|
|
|
|
|
+ cuisineType = 0; // 0表示餐具
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 尝试从菜品信息中获取,如果获取不到则默认为1(单品)
|
|
|
|
|
+ try {
|
|
|
|
|
+ StoreCuisine cuisine = storeCuisineMapper.selectById(item.getCuisineId());
|
|
|
|
|
+ if (cuisine != null && cuisine.getCuisineType() != null) {
|
|
|
|
|
+ cuisineType = cuisine.getCuisineType();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ cuisineType = 1; // 默认为单品
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("获取菜品类型失败, cuisineId={}, 使用默认值1", item.getCuisineId(), e);
|
|
|
|
|
+ cuisineType = 1; // 默认为单品
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ detail.setCuisineType(cuisineType);
|
|
|
|
|
+
|
|
|
|
|
+ detail.setCuisineImage(item.getCuisineImage());
|
|
|
|
|
+ detail.setUnitPrice(item.getUnitPrice());
|
|
|
|
|
+ detail.setQuantity(item.getQuantity());
|
|
|
|
|
+ detail.setSubtotalAmount(item.getSubtotalAmount());
|
|
|
|
|
+ detail.setAddUserId(item.getAddUserId());
|
|
|
|
|
+ detail.setAddUserPhone(item.getAddUserPhone());
|
|
|
|
|
+ detail.setRemark(item.getRemark());
|
|
|
|
|
+ detail.setCreatedUserId(userId);
|
|
|
|
|
+ detail.setCreatedTime(now);
|
|
|
|
|
+ detail.setUpdatedTime(now);
|
|
|
|
|
+
|
|
|
|
|
+ // 如果是更新订单,判断是否为新增商品或数量增加的商品
|
|
|
|
|
+ if (finalIsUpdate) {
|
|
|
|
|
+ Integer lockedQuantity = item.getLockedQuantity();
|
|
|
|
|
+ // 如果是新增商品(lockedQuantity 为 null 或 0)或数量增加的商品,标记为加餐
|
|
|
|
|
+ if (lockedQuantity == null || lockedQuantity == 0 ||
|
|
|
|
|
+ (item.getQuantity() != null && item.getQuantity() > lockedQuantity)) {
|
|
|
|
|
+ detail.setIsAddDish(1); // 标记为加餐
|
|
|
|
|
+ detail.setAddDishTime(now);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return detail;
|
|
|
|
|
+ })
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ for (StoreOrderDetail detail : orderDetails) {
|
|
|
|
|
+ orderDetailMapper.insert(detail);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 记录订单变更日志
|
|
|
|
|
+ recordOrderChangeLog(finalOrder.getId(), finalOrderNo, cart.getItems(), finalIsUpdate ? 3 : 1, now, userId, userPhone);
|
|
|
|
|
+
|
|
|
|
|
+ // 更新桌号的当前订单ID和状态(显式用 LambdaUpdateWrapper 写入,避免 updateById 忽略更新)
|
|
|
|
|
+ // 约定:首次下单 → 餐桌状态=就餐中(1);后续下单(更新订单/加餐) → 餐桌状态=加餐(3)
|
|
|
|
|
+ Integer tableId = table.getId();
|
|
|
|
|
+ if (!finalIsUpdate) {
|
|
|
|
|
+ // 首次下单:设置订单ID,餐桌状态为就餐中(1)
|
|
|
|
|
+ LambdaUpdateWrapper<StoreTable> newTableWrapper = new LambdaUpdateWrapper<>();
|
|
|
|
|
+ newTableWrapper.eq(StoreTable::getId, tableId)
|
|
|
|
|
+ .set(StoreTable::getCurrentOrderId, finalOrder.getId())
|
|
|
|
|
+ .set(StoreTable::getStatus, 1)
|
|
|
|
|
+ .set(StoreTable::getUpdatedTime, now);
|
|
|
|
|
+ if (userId != null) {
|
|
|
|
|
+ newTableWrapper.set(StoreTable::getUpdatedUserId, userId);
|
|
|
|
|
+ }
|
|
|
|
|
+ storeTableMapper.update(null, newTableWrapper);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 后续下单(更新订单/加餐):餐桌状态设为加餐(3)
|
|
|
|
|
+ LambdaUpdateWrapper<StoreTable> addDishTableWrapper = new LambdaUpdateWrapper<>();
|
|
|
|
|
+ addDishTableWrapper.eq(StoreTable::getId, tableId)
|
|
|
|
|
+ .set(StoreTable::getStatus, 3)
|
|
|
|
|
+ .set(StoreTable::getUpdatedTime, now);
|
|
|
|
|
+ if (userId != null) {
|
|
|
|
|
+ addDishTableWrapper.set(StoreTable::getUpdatedUserId, userId);
|
|
|
|
|
+ }
|
|
|
|
|
+ storeTableMapper.update(null, addDishTableWrapper);
|
|
|
|
|
+ log.info("更新订单(加餐),设置餐桌状态为加餐, tableId={}, orderId={}", tableId, finalOrder.getId());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 锁定购物车商品数量(禁止减少或删除已下单的商品)
|
|
|
|
|
+ cartService.lockCartItems(dto.getTableId());
|
|
|
|
|
+
|
|
|
|
|
+ // 下单后不清空购物车,允许加餐(加餐时添加到同一订单)
|
|
|
|
|
+ // 只有在支付完成后才清空购物车
|
|
|
|
|
+
|
|
|
|
|
+ // 创建订单成功后,自动解锁订单锁定
|
|
|
|
|
+ try {
|
|
|
|
|
+ orderLockService.unlockOrder(dto.getTableId(), userId);
|
|
|
|
|
+ log.info("订单创建成功后自动解锁订单锁定, tableId={}, userId={}", dto.getTableId(), userId);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("自动解锁订单锁定失败, tableId={}, userId={}, error={}", dto.getTableId(), userId, e.getMessage());
|
|
|
|
|
+ // 解锁失败不影响订单创建,只记录警告日志
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (isUpdate) {
|
|
|
|
|
+ log.info("订单更新成功, orderId={}, orderNo={}", finalOrder.getId(), finalOrderNo);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ log.info("订单创建成功, orderId={}, orderNo={}", finalOrder.getId(), finalOrderNo);
|
|
|
|
|
+ }
|
|
|
|
|
+ return finalOrder;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public StoreOrder payOrder(Integer orderId, Integer payType) {
|
|
|
|
|
+ log.info("支付订单, orderId={}, payType={}", orderId, payType);
|
|
|
|
|
+
|
|
|
|
|
+ // 获取当前用户信息
|
|
|
|
|
+ Object[] userInfo = getCurrentUserInfo();
|
|
|
|
|
+ Integer userId = (Integer) userInfo[0];
|
|
|
|
|
+
|
|
|
|
|
+ // 检查结算锁定状态
|
|
|
|
|
+ Integer lockUserId = orderLockService.checkSettlementLock(orderId);
|
|
|
|
|
+ if (lockUserId != null && !lockUserId.equals(userId)) {
|
|
|
|
|
+ throw new RuntimeException("订单结算已被其他用户锁定,无法支付");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 验证订单状态
|
|
|
|
|
+ StoreOrder order = validateOrderForOperation(orderId, 0, "支付");
|
|
|
|
|
+
|
|
|
|
|
+ // 支付时重新计算优惠券金额和实付金额(此时订单金额已确定,不会再变化)
|
|
|
|
|
+ BigDecimal discountAmount = BigDecimal.ZERO;
|
|
|
|
|
+ BigDecimal tablewareFee = order.getTablewareFee() != null ? order.getTablewareFee() : BigDecimal.ZERO;
|
|
|
|
|
+ BigDecimal totalAmount = order.getTotalAmount() != null ? order.getTotalAmount() : BigDecimal.ZERO;
|
|
|
|
|
+ BigDecimal totalWithTableware = totalAmount.add(tablewareFee);
|
|
|
|
|
+
|
|
|
|
|
+ if (order.getCouponId() != null) {
|
|
|
|
|
+ // 查询优惠券信息
|
|
|
|
|
+ LifeDiscountCoupon coupon = lifeDiscountCouponMapper.selectById(order.getCouponId());
|
|
|
|
|
+ if (coupon == null) {
|
|
|
|
|
+ throw new RuntimeException("优惠券不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 验证最低消费(菜品总价 + 餐具费)
|
|
|
|
|
+ if (coupon.getMinimumSpendingAmount() != null
|
|
|
|
|
+ && totalWithTableware.compareTo(coupon.getMinimumSpendingAmount()) < 0) {
|
|
|
|
|
+ throw new RuntimeException("订单金额未达到优惠券最低消费要求(" + coupon.getMinimumSpendingAmount() + "元)");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 计算优惠金额:根据优惠券类型(满减券或折扣券)计算
|
|
|
|
|
+ discountAmount = calculateDiscountAmount(coupon, totalWithTableware);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 计算实付金额(菜品总价 + 餐具费 - 优惠金额)
|
|
|
|
|
+ BigDecimal payAmount = totalWithTableware.subtract(discountAmount);
|
|
|
|
|
+
|
|
|
|
|
+ // 更新订单信息(包括优惠金额和实付金额)
|
|
|
|
|
+ order.setDiscountAmount(discountAmount);
|
|
|
|
|
+ order.setPayAmount(payAmount);
|
|
|
|
|
+ order.setOrderStatus(1); // 已支付
|
|
|
|
|
+ order.setPayStatus(1); // 已支付
|
|
|
|
|
+ order.setPayType(payType);
|
|
|
|
|
+ order.setPayTime(new Date());
|
|
|
|
|
+ order.setPayTradeNo("TRADE_" + System.currentTimeMillis()); // 模拟交易号
|
|
|
|
|
+ order.setUpdatedTime(new Date());
|
|
|
|
|
+ order.setUpdatedUserId(userId);
|
|
|
|
|
+
|
|
|
|
|
+ this.updateById(order);
|
|
|
|
|
+
|
|
|
|
|
+ // 更新优惠券使用记录状态为已支付
|
|
|
|
|
+ if (order.getCouponId() != null) {
|
|
|
|
|
+ LambdaQueryWrapper<StoreCouponUsage> usageWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ usageWrapper.eq(StoreCouponUsage::getOrderId, orderId);
|
|
|
|
|
+ usageWrapper.eq(StoreCouponUsage::getCouponId, order.getCouponId());
|
|
|
|
|
+ usageWrapper.eq(StoreCouponUsage::getDeleteFlag, 0);
|
|
|
|
|
+ usageWrapper.orderByDesc(StoreCouponUsage::getCreatedTime);
|
|
|
|
|
+ usageWrapper.last("LIMIT 1");
|
|
|
|
|
+ StoreCouponUsage usage = storeCouponUsageMapper.selectOne(usageWrapper);
|
|
|
|
|
+ if (usage != null) {
|
|
|
|
|
+ usage.setUsageStatus(2); // 已支付
|
|
|
|
|
+ usage.setUpdatedTime(new Date());
|
|
|
|
|
+ storeCouponUsageMapper.updateById(usage);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 支付完成后,清空购物车和重置餐桌状态(保留订单数据,不删除订单)
|
|
|
|
|
+ // resetTableAfterPayment 方法会清空购物车和重置餐桌状态,但不会删除订单数据
|
|
|
|
|
+ resetTableAfterPayment(order.getTableId());
|
|
|
|
|
+
|
|
|
|
|
+ // 支付订单成功后,自动解锁结算锁定
|
|
|
|
|
+ try {
|
|
|
|
|
+ orderLockService.unlockSettlement(orderId, userId);
|
|
|
|
|
+ log.info("订单支付成功后自动解锁结算锁定, orderId={}, userId={}", orderId, userId);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("自动解锁结算锁定失败, orderId={}, userId={}, error={}", orderId, userId, e.getMessage());
|
|
|
|
|
+ // 解锁失败不影响订单支付,只记录警告日志
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ log.info("订单支付成功, orderId={}", orderId);
|
|
|
|
|
+ return order;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean cancelOrder(Integer orderId) {
|
|
|
|
|
+ log.info("取消订单, orderId={}", orderId);
|
|
|
|
|
+
|
|
|
|
|
+ // 验证订单状态
|
|
|
|
|
+ StoreOrder order = validateOrderForOperation(orderId, 0, "取消");
|
|
|
|
|
+
|
|
|
|
|
+ order.setOrderStatus(2); // 已取消
|
|
|
|
|
+ order.setUpdatedTime(new Date());
|
|
|
|
|
+
|
|
|
|
|
+ // 获取当前用户信息
|
|
|
|
|
+ Integer userId = TokenUtil.getCurrentUserId();
|
|
|
|
|
+ if (userId != null) {
|
|
|
|
|
+ order.setUpdatedUserId(userId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.updateById(order);
|
|
|
|
|
+
|
|
|
|
|
+ // 恢复购物车的已下单数量(允许重新下单)
|
|
|
|
|
+ cartService.unlockCartItems(order.getTableId());
|
|
|
|
|
+
|
|
|
|
|
+ // 清除优惠券使用标记
|
|
|
|
|
+ cartService.clearCouponUsed(order.getTableId());
|
|
|
|
|
+
|
|
|
|
|
+ // 更新桌号状态:显式清空 currentOrderId(updateById 会忽略 null,必须用 LambdaUpdateWrapper)
|
|
|
|
|
+ Integer tableId = order.getTableId();
|
|
|
|
|
+ StoreTable table = storeTableMapper.selectById(tableId);
|
|
|
|
|
+ if (table != null) {
|
|
|
|
|
+ LambdaUpdateWrapper<StoreTable> tableWrapper = new LambdaUpdateWrapper<>();
|
|
|
|
|
+ tableWrapper.eq(StoreTable::getId, tableId)
|
|
|
|
|
+ .set(StoreTable::getCurrentOrderId, null)
|
|
|
|
|
+ .set(StoreTable::getUpdatedTime, new Date());
|
|
|
|
|
+ CartDTO cart = cartService.getCart(tableId);
|
|
|
|
|
+ if (cart.getItems() == null || cart.getItems().isEmpty()) {
|
|
|
|
|
+ tableWrapper.set(StoreTable::getStatus, 0).set(StoreTable::getDinerCount, null);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ tableWrapper.set(StoreTable::getStatus, 1); // 就餐中
|
|
|
|
|
+ }
|
|
|
|
|
+ if (userId != null) {
|
|
|
|
|
+ tableWrapper.set(StoreTable::getUpdatedUserId, userId);
|
|
|
|
|
+ }
|
|
|
|
|
+ storeTableMapper.update(null, tableWrapper);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ log.info("订单取消成功, orderId={}", orderId);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public StoreOrder getOrderByOrderNo(String orderNo) {
|
|
|
|
|
+ LambdaQueryWrapper<StoreOrder> wrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ wrapper.eq(StoreOrder::getOrderNo, orderNo);
|
|
|
|
|
+ wrapper.eq(StoreOrder::getDeleteFlag, 0);
|
|
|
|
|
+ return this.getOne(wrapper);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public StoreOrder getOrderById(Integer orderId) {
|
|
|
|
|
+ return this.getById(orderId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 前端只传 0=进行中、3=已完成。进行中对应库表 0待支付+1已支付,已完成对应 3。 */
|
|
|
|
|
+ private List<Integer> resolveOrderStatusFilter(Integer orderStatus) {
|
|
|
|
|
+ if (orderStatus == null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (orderStatus == 0) {
|
|
|
|
|
+ return Arrays.asList(0, 1); // 进行中 = 待支付 + 已支付
|
|
|
|
|
+ }
|
|
|
|
|
+ if (orderStatus == 3) {
|
|
|
|
|
+ return Collections.singletonList(3); // 已完成
|
|
|
|
|
+ }
|
|
|
|
|
+ return null; // 其他值不参与筛选
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public IPage<StoreOrder> getOrderPage(Page<StoreOrder> page, Integer storeId, Integer tableId, Integer orderStatus, String keyword) {
|
|
|
|
|
+ // 前端只传 0=进行中、3=已完成。进行中=待支付(0)+已支付(1),已完成=3
|
|
|
|
|
+ List<Integer> orderStatusList = resolveOrderStatusFilter(orderStatus);
|
|
|
|
|
+ LambdaQueryWrapper<StoreOrder> wrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ wrapper.eq(StoreOrder::getDeleteFlag, 0);
|
|
|
|
|
+ if (storeId != null) {
|
|
|
|
|
+ wrapper.eq(StoreOrder::getStoreId, storeId);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (tableId != null) {
|
|
|
|
|
+ wrapper.eq(StoreOrder::getTableId, tableId);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (orderStatusList != null && !orderStatusList.isEmpty()) {
|
|
|
|
|
+ wrapper.in(StoreOrder::getOrderStatus, orderStatusList);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 搜索功能:按订单编号模糊搜索
|
|
|
|
|
+ if (StringUtils.hasText(keyword)) {
|
|
|
|
|
+ // 限制搜索关键词长度(15字)
|
|
|
|
|
+ String searchKeyword = keyword.length() > 15 ? keyword.substring(0, 15) : keyword;
|
|
|
|
|
+ wrapper.like(StoreOrder::getOrderNo, searchKeyword);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ wrapper.orderByDesc(StoreOrder::getCreatedTime);
|
|
|
|
|
+ return this.page(page, wrapper);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public IPage<StoreOrderPageVO> getOrderPageWithCuisines(Page<StoreOrder> page, Integer storeId, Integer tableId, Integer orderStatus, String keyword) {
|
|
|
|
|
+ // 前端只传 0=进行中、3=已完成。进行中=待支付(0)+已支付(1),已完成=3
|
|
|
|
|
+ List<Integer> orderStatusList = resolveOrderStatusFilter(orderStatus);
|
|
|
|
|
+ log.info("分页查询订单列表(包含菜品信息), storeId={}, tableId={}, orderStatus={}, keyword={}", storeId, tableId, orderStatusList, keyword);
|
|
|
|
|
+
|
|
|
|
|
+ // 限制搜索关键词长度(15字)
|
|
|
|
|
+ String searchKeyword = null;
|
|
|
|
|
+ if (StringUtils.hasText(keyword)) {
|
|
|
|
|
+ searchKeyword = keyword.length() > 15 ? keyword.substring(0, 15) : keyword;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 如果有关键词,需要同时支持按订单编号和菜品名称搜索
|
|
|
|
|
+ final Set<Integer> matchingOrderIds;
|
|
|
|
|
+ if (StringUtils.hasText(searchKeyword)) {
|
|
|
|
|
+ Set<Integer> orderIdsSet = new HashSet<>();
|
|
|
|
|
+
|
|
|
|
|
+ // 1.1 按订单编号搜索
|
|
|
|
|
+ LambdaQueryWrapper<StoreOrder> orderNoWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ orderNoWrapper.eq(StoreOrder::getDeleteFlag, 0)
|
|
|
|
|
+ .like(StoreOrder::getOrderNo, searchKeyword);
|
|
|
|
|
+ if (storeId != null) {
|
|
|
|
|
+ orderNoWrapper.eq(StoreOrder::getStoreId, storeId);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (tableId != null) {
|
|
|
|
|
+ orderNoWrapper.eq(StoreOrder::getTableId, tableId);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (orderStatusList != null && !orderStatusList.isEmpty()) {
|
|
|
|
|
+ orderNoWrapper.in(StoreOrder::getOrderStatus, orderStatusList);
|
|
|
|
|
+ }
|
|
|
|
|
+ List<StoreOrder> orderNoMatches = this.list(orderNoWrapper);
|
|
|
|
|
+ orderNoMatches.forEach(order -> orderIdsSet.add(order.getId()));
|
|
|
|
|
+
|
|
|
|
|
+ // 1.2 按菜品名称搜索
|
|
|
|
|
+ LambdaQueryWrapper<StoreOrderDetail> detailSearchWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ detailSearchWrapper.select(StoreOrderDetail::getOrderId)
|
|
|
|
|
+ .like(StoreOrderDetail::getCuisineName, searchKeyword)
|
|
|
|
|
+ .eq(StoreOrderDetail::getDeleteFlag, 0);
|
|
|
|
|
+
|
|
|
|
|
+ // 如果有限制条件,需要先查询符合条件的订单ID
|
|
|
|
|
+ if (storeId != null || tableId != null || (orderStatusList != null && !orderStatusList.isEmpty())) {
|
|
|
|
|
+ LambdaQueryWrapper<StoreOrder> filterWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ filterWrapper.eq(StoreOrder::getDeleteFlag, 0)
|
|
|
|
|
+ .select(StoreOrder::getId);
|
|
|
|
|
+ if (storeId != null) {
|
|
|
|
|
+ filterWrapper.eq(StoreOrder::getStoreId, storeId);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (tableId != null) {
|
|
|
|
|
+ filterWrapper.eq(StoreOrder::getTableId, tableId);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (orderStatusList != null && !orderStatusList.isEmpty()) {
|
|
|
|
|
+ filterWrapper.in(StoreOrder::getOrderStatus, orderStatusList);
|
|
|
|
|
+ }
|
|
|
|
|
+ List<StoreOrder> filteredOrders = this.list(filterWrapper);
|
|
|
|
|
+ Set<Integer> filteredOrderIds = filteredOrders.stream()
|
|
|
|
|
+ .map(StoreOrder::getId)
|
|
|
|
|
+ .collect(Collectors.toSet());
|
|
|
|
|
+ if (!filteredOrderIds.isEmpty()) {
|
|
|
|
|
+ detailSearchWrapper.in(StoreOrderDetail::getOrderId, filteredOrderIds);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 如果没有符合条件的订单,直接返回空结果
|
|
|
|
|
+ Page<StoreOrderPageVO> emptyPage = new Page<>(page.getCurrent(), page.getSize());
|
|
|
|
|
+ emptyPage.setTotal(0);
|
|
|
|
|
+ emptyPage.setPages(0);
|
|
|
|
|
+ return emptyPage;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ List<StoreOrderDetail> matchingDetails = orderDetailMapper.selectList(detailSearchWrapper);
|
|
|
|
|
+ matchingDetails.forEach(detail -> orderIdsSet.add(detail.getOrderId()));
|
|
|
|
|
+
|
|
|
|
|
+ matchingOrderIds = orderIdsSet;
|
|
|
|
|
+
|
|
|
|
|
+ // 如果没有任何匹配的订单,返回空结果
|
|
|
|
|
+ if (matchingOrderIds.isEmpty()) {
|
|
|
|
|
+ Page<StoreOrderPageVO> emptyPage = new Page<>(page.getCurrent(), page.getSize());
|
|
|
|
|
+ emptyPage.setTotal(0);
|
|
|
|
|
+ emptyPage.setPages(0);
|
|
|
|
|
+ return emptyPage;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ matchingOrderIds = null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 查询订单列表
|
|
|
|
|
+ LambdaQueryWrapper<StoreOrder> wrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ wrapper.eq(StoreOrder::getDeleteFlag, 0);
|
|
|
|
|
+ if (storeId != null) {
|
|
|
|
|
+ wrapper.eq(StoreOrder::getStoreId, storeId);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (tableId != null) {
|
|
|
|
|
+ wrapper.eq(StoreOrder::getTableId, tableId);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (orderStatusList != null && !orderStatusList.isEmpty()) {
|
|
|
|
|
+ wrapper.in(StoreOrder::getOrderStatus, orderStatusList);
|
|
|
|
|
+ }
|
|
|
|
|
+ // 如果有关键词搜索,只查询匹配的订单ID
|
|
|
|
|
+ if (matchingOrderIds != null && !matchingOrderIds.isEmpty()) {
|
|
|
|
|
+ wrapper.in(StoreOrder::getId, matchingOrderIds);
|
|
|
|
|
+ }
|
|
|
|
|
+ wrapper.orderByDesc(StoreOrder::getCreatedTime);
|
|
|
|
|
+
|
|
|
|
|
+ IPage<StoreOrder> orderPage = this.page(page, wrapper);
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 获取所有订单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));
|
|
|
|
|
+
|
|
|
|
|
+ // 4.1 批量查询菜品标签(用于分页列表展示)
|
|
|
|
|
+ Set<Integer> cuisineIds = allDetails.stream()
|
|
|
|
|
+ .map(StoreOrderDetail::getCuisineId)
|
|
|
|
|
+ .filter(Objects::nonNull)
|
|
|
|
|
+ .collect(Collectors.toSet());
|
|
|
|
|
+ Map<Integer, String> cuisineIdToTags = new HashMap<>();
|
|
|
|
|
+ if (!cuisineIds.isEmpty()) {
|
|
|
|
|
+ List<StoreCuisine> cuisines = storeCuisineMapper.selectBatchIds(new ArrayList<>(cuisineIds));
|
|
|
|
|
+ if (cuisines != null) {
|
|
|
|
|
+ for (StoreCuisine c : cuisines) {
|
|
|
|
|
+ if (c.getTags() != null) {
|
|
|
|
|
+ cuisineIdToTags.put(c.getId(), c.getTags());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ Map<Integer, String> finalCuisineIdToTags = cuisineIdToTags;
|
|
|
|
|
+
|
|
|
|
|
+ // 5. 批量查询门店名称
|
|
|
|
|
+ Set<Integer> storeIds = orders.stream().map(StoreOrder::getStoreId).filter(Objects::nonNull).collect(Collectors.toSet());
|
|
|
|
|
+ Map<Integer, String> storeNameMap = new HashMap<>();
|
|
|
|
|
+ if (!storeIds.isEmpty()) {
|
|
|
|
|
+ List<StoreInfo> storeList = storeInfoMapper.selectBatchIds(storeIds);
|
|
|
|
|
+ if (storeList != null) {
|
|
|
|
|
+ for (StoreInfo s : storeList) {
|
|
|
|
|
+ storeNameMap.put(s.getId(), s.getStoreName());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 6. 构建返回结果
|
|
|
|
|
+ List<StoreOrderPageVO> voList = orders.stream().map(order -> {
|
|
|
|
|
+ StoreOrderPageVO vo = new StoreOrderPageVO();
|
|
|
|
|
+ vo.setOrder(order);
|
|
|
|
|
+ vo.setStoreName(storeNameMap.getOrDefault(order.getStoreId(), ""));
|
|
|
|
|
+
|
|
|
|
|
+ // 获取该订单的菜品列表(含菜品标签)
|
|
|
|
|
+ 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());
|
|
|
|
|
+ item.setUnitPrice(detail.getUnitPrice());
|
|
|
|
|
+ item.setTags(detail.getCuisineId() != null ? finalCuisineIdToTags.get(detail.getCuisineId()) : null);
|
|
|
|
|
+ return item;
|
|
|
|
|
+ })
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+ vo.setCuisineItems(cuisineItems);
|
|
|
|
|
+
|
|
|
|
|
+ return vo;
|
|
|
|
|
+ }).collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ // 7. 构建分页结果
|
|
|
|
|
+ Page<StoreOrderPageVO> resultPage = new Page<>(page.getCurrent(), page.getSize());
|
|
|
|
|
+ resultPage.setRecords(voList);
|
|
|
|
|
+ resultPage.setTotal(orderPage.getTotal());
|
|
|
|
|
+ resultPage.setPages(orderPage.getPages());
|
|
|
|
|
+
|
|
|
|
|
+ return resultPage;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean completeOrder(Integer orderId) {
|
|
|
|
|
+ log.info("完成订单, orderId={}", orderId);
|
|
|
|
|
+
|
|
|
|
|
+ StoreOrder order = this.getById(orderId);
|
|
|
|
|
+ if (order == null) {
|
|
|
|
|
+ throw new RuntimeException("订单不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检查订单状态:只有已支付状态(1)的订单才能完成
|
|
|
|
|
+ if (order.getOrderStatus() != 1) {
|
|
|
|
|
+ throw new RuntimeException("订单状态不正确,只有已支付状态的订单才能完成");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检查支付状态:确保订单已支付
|
|
|
|
|
+ if (order.getPayStatus() != 1) {
|
|
|
|
|
+ throw new RuntimeException("订单未支付,无法完成");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ order.setOrderStatus(3); // 已完成
|
|
|
|
|
+ order.setUpdatedTime(new Date());
|
|
|
|
|
+
|
|
|
|
|
+ // 从 token 获取用户信息
|
|
|
|
|
+ Integer userId = TokenUtil.getCurrentUserId();
|
|
|
|
|
+ if (userId != null) {
|
|
|
|
|
+ order.setUpdatedUserId(userId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.updateById(order);
|
|
|
|
|
+
|
|
|
|
|
+ // 更新桌号状态:显式清空 currentOrderId(updateById 会忽略 null,必须用 LambdaUpdateWrapper)
|
|
|
|
|
+ Integer tableId = order.getTableId();
|
|
|
|
|
+ StoreTable table = storeTableMapper.selectById(tableId);
|
|
|
|
|
+ if (table != null) {
|
|
|
|
|
+ LambdaUpdateWrapper<StoreTable> tableWrapper = new LambdaUpdateWrapper<>();
|
|
|
|
|
+ tableWrapper.eq(StoreTable::getId, tableId)
|
|
|
|
|
+ .set(StoreTable::getCurrentOrderId, null)
|
|
|
|
|
+ .set(StoreTable::getStatus, 0)
|
|
|
|
|
+ .set(StoreTable::getDinerCount, null)
|
|
|
|
|
+ .set(StoreTable::getUpdatedTime, new Date());
|
|
|
|
|
+ if (userId != null) {
|
|
|
|
|
+ tableWrapper.set(StoreTable::getUpdatedUserId, userId);
|
|
|
|
|
+ }
|
|
|
|
|
+ storeTableMapper.update(null, tableWrapper);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ log.info("订单完成成功, orderId={}", orderId);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean completeOrderByMerchant(Integer orderId) {
|
|
|
|
|
+ log.info("商家手动完成订单, orderId={}", orderId);
|
|
|
|
|
+
|
|
|
|
|
+ StoreOrder order = this.getById(orderId);
|
|
|
|
|
+ if (order == null) {
|
|
|
|
|
+ throw new RuntimeException("订单不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 商家手动完成订单,不校验支付状态,直接设置为已完成
|
|
|
|
|
+ order.setOrderStatus(3); // 已完成
|
|
|
|
|
+ order.setUpdatedTime(new Date());
|
|
|
|
|
+
|
|
|
|
|
+ // 从 token 获取用户信息
|
|
|
|
|
+ Integer userId = TokenUtil.getCurrentUserId();
|
|
|
|
|
+ if (userId != null) {
|
|
|
|
|
+ order.setUpdatedUserId(userId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.updateById(order);
|
|
|
|
|
+
|
|
|
|
|
+ // 更新桌号状态:显式清空 currentOrderId(updateById 会忽略 null,必须用 LambdaUpdateWrapper)
|
|
|
|
|
+ Integer tableId = order.getTableId();
|
|
|
|
|
+ StoreTable table = storeTableMapper.selectById(tableId);
|
|
|
|
|
+ if (table != null) {
|
|
|
|
|
+ LambdaUpdateWrapper<StoreTable> tableWrapper = new LambdaUpdateWrapper<>();
|
|
|
|
|
+ tableWrapper.eq(StoreTable::getId, tableId)
|
|
|
|
|
+ .set(StoreTable::getCurrentOrderId, null)
|
|
|
|
|
+ .set(StoreTable::getStatus, 0)
|
|
|
|
|
+ .set(StoreTable::getDinerCount, null)
|
|
|
|
|
+ .set(StoreTable::getUpdatedTime, new Date());
|
|
|
|
|
+ if (userId != null) {
|
|
|
|
|
+ tableWrapper.set(StoreTable::getUpdatedUserId, userId);
|
|
|
|
|
+ }
|
|
|
|
|
+ storeTableMapper.update(null, tableWrapper);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ log.info("商家手动完成订单成功, orderId={}", orderId);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public StoreOrder updateOrderCoupon(Integer orderId, Integer couponId) {
|
|
|
|
|
+ log.info("更新订单优惠券, orderId={}, couponId={}", orderId, couponId);
|
|
|
|
|
+
|
|
|
|
|
+ // 验证订单状态
|
|
|
|
|
+ StoreOrder order = validateOrderForOperation(orderId, 0, "修改优惠券");
|
|
|
|
|
+
|
|
|
|
|
+ BigDecimal discountAmount = BigDecimal.ZERO;
|
|
|
|
|
+
|
|
|
|
|
+ if (couponId != null) {
|
|
|
|
|
+ // 验证优惠券
|
|
|
|
|
+ LifeDiscountCoupon coupon = lifeDiscountCouponMapper.selectById(couponId);
|
|
|
|
|
+ if (coupon == null) {
|
|
|
|
|
+ throw new RuntimeException("优惠券不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 验证优惠券是否属于该门店
|
|
|
|
|
+ if (!coupon.getStoreId().equals(String.valueOf(order.getStoreId()))) {
|
|
|
|
|
+ throw new RuntimeException("优惠券不属于该门店");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 验证最低消费(菜品总价 + 餐具费)
|
|
|
|
|
+ BigDecimal totalWithTableware = order.getTotalAmount().add(order.getTablewareFee() != null ? order.getTablewareFee() : BigDecimal.ZERO);
|
|
|
|
|
+ if (coupon.getMinimumSpendingAmount() != null
|
|
|
|
|
+ && totalWithTableware.compareTo(coupon.getMinimumSpendingAmount()) < 0) {
|
|
|
|
|
+ throw new RuntimeException("订单金额未达到优惠券最低消费要求");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 计算优惠金额:根据优惠券类型(满减券或折扣券)计算
|
|
|
|
|
+ discountAmount = calculateDiscountAmount(coupon, totalWithTableware);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果之前有优惠券,且优惠券发生变化(包括取消优惠券),需要处理旧优惠券使用记录
|
|
|
|
|
+ if (order.getCouponId() != null && (couponId == null || !order.getCouponId().equals(couponId))) {
|
|
|
|
|
+ // 清除旧优惠券的使用标记(如果取消优惠券或更换优惠券)
|
|
|
|
|
+ cartService.clearCouponUsed(order.getTableId());
|
|
|
|
|
+
|
|
|
|
|
+ LambdaQueryWrapper<StoreCouponUsage> oldUsageWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ oldUsageWrapper.eq(StoreCouponUsage::getOrderId, orderId);
|
|
|
|
|
+ oldUsageWrapper.eq(StoreCouponUsage::getCouponId, order.getCouponId());
|
|
|
|
|
+ oldUsageWrapper.eq(StoreCouponUsage::getDeleteFlag, 0);
|
|
|
|
|
+ oldUsageWrapper.orderByDesc(StoreCouponUsage::getCreatedTime);
|
|
|
|
|
+ oldUsageWrapper.last("LIMIT 1");
|
|
|
|
|
+ StoreCouponUsage oldUsage = storeCouponUsageMapper.selectOne(oldUsageWrapper);
|
|
|
|
|
+ if (oldUsage != null) {
|
|
|
|
|
+ oldUsage.setUsageStatus(3); // 已取消
|
|
|
|
|
+ oldUsage.setUpdatedTime(new Date());
|
|
|
|
|
+ storeCouponUsageMapper.updateById(oldUsage);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果新设置了优惠券,需要标记桌号已使用优惠券,并更新或创建使用记录
|
|
|
|
|
+ if (couponId != null) {
|
|
|
|
|
+ // 检查桌号是否已使用优惠券(如果更换优惠券,之前的标记已清除)
|
|
|
|
|
+ if (!cartService.hasUsedCoupon(order.getTableId())) {
|
|
|
|
|
+ cartService.markCouponUsed(order.getTableId(), couponId);
|
|
|
|
|
+ }
|
|
|
|
|
+ LambdaQueryWrapper<StoreCouponUsage> usageWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ usageWrapper.eq(StoreCouponUsage::getTableId, order.getTableId());
|
|
|
|
|
+ usageWrapper.eq(StoreCouponUsage::getCouponId, couponId);
|
|
|
|
|
+ usageWrapper.eq(StoreCouponUsage::getDeleteFlag, 0);
|
|
|
|
|
+ usageWrapper.orderByDesc(StoreCouponUsage::getCreatedTime);
|
|
|
|
|
+ usageWrapper.last("LIMIT 1");
|
|
|
|
|
+ StoreCouponUsage usage = storeCouponUsageMapper.selectOne(usageWrapper);
|
|
|
|
|
+ if (usage != null) {
|
|
|
|
|
+ usage.setOrderId(orderId);
|
|
|
|
|
+ usage.setUsageStatus(1); // 已下单
|
|
|
|
|
+ usage.setDiscountAmount(discountAmount);
|
|
|
|
|
+ usage.setUpdatedTime(new Date());
|
|
|
|
|
+ storeCouponUsageMapper.updateById(usage);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 创建新的使用记录
|
|
|
|
|
+ StoreTable table = storeTableMapper.selectById(order.getTableId());
|
|
|
|
|
+ if (table != null) {
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ StoreCouponUsage newUsage = new StoreCouponUsage();
|
|
|
|
|
+ newUsage.setTableId(order.getTableId());
|
|
|
|
|
+ newUsage.setStoreId(order.getStoreId());
|
|
|
|
|
+ newUsage.setOrderId(orderId);
|
|
|
|
|
+ newUsage.setCouponId(couponId);
|
|
|
|
|
+ newUsage.setDiscountAmount(discountAmount);
|
|
|
|
|
+ newUsage.setUsageStatus(1); // 已下单
|
|
|
|
|
+ newUsage.setCreatedTime(now);
|
|
|
|
|
+ newUsage.setUpdatedTime(now); // 设置更新时间,避免数据库约束错误
|
|
|
|
|
+ Integer userId = TokenUtil.getCurrentUserId();
|
|
|
|
|
+ if (userId != null) {
|
|
|
|
|
+ newUsage.setCreatedUserId(userId);
|
|
|
|
|
+ }
|
|
|
|
|
+ storeCouponUsageMapper.insert(newUsage);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 重新计算实付金额(菜品总价 + 餐具费 - 优惠金额)
|
|
|
|
|
+ BigDecimal tablewareFee = order.getTablewareFee() != null ? order.getTablewareFee() : BigDecimal.ZERO;
|
|
|
|
|
+ BigDecimal payAmount = order.getTotalAmount().add(tablewareFee).subtract(discountAmount);
|
|
|
|
|
+
|
|
|
|
|
+ order.setCouponId(couponId);
|
|
|
|
|
+ order.setCurrentCouponId(couponId);
|
|
|
|
|
+ order.setDiscountAmount(discountAmount);
|
|
|
|
|
+ order.setPayAmount(payAmount);
|
|
|
|
|
+ order.setUpdatedTime(new Date());
|
|
|
|
|
+
|
|
|
|
|
+ // 从 token 获取用户信息
|
|
|
|
|
+ Integer userId = TokenUtil.getCurrentUserId();
|
|
|
|
|
+ if (userId != null) {
|
|
|
|
|
+ order.setUpdatedUserId(userId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.updateById(order);
|
|
|
|
|
+
|
|
|
|
|
+ log.info("更新订单优惠券成功, orderId={}", orderId);
|
|
|
|
|
+ return order;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean resetTable(Integer tableId) {
|
|
|
|
|
+ log.info("管理员重置餐桌, tableId={}", tableId);
|
|
|
|
|
+
|
|
|
|
|
+ // 验证餐桌是否存在
|
|
|
|
|
+ StoreTable table = storeTableMapper.selectById(tableId);
|
|
|
|
|
+ if (table == null) {
|
|
|
|
|
+ throw new RuntimeException("餐桌不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 删除购物车数据(逻辑删除,使用 MyBatis-Plus 的 removeBatchIds)
|
|
|
|
|
+ LambdaQueryWrapper<StoreCart> cartWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ cartWrapper.eq(StoreCart::getTableId, tableId);
|
|
|
|
|
+ cartWrapper.eq(StoreCart::getDeleteFlag, 0);
|
|
|
|
|
+ List<StoreCart> cartList = storeCartMapper.selectList(cartWrapper);
|
|
|
|
|
+ if (cartList != null && !cartList.isEmpty()) {
|
|
|
|
|
+ List<Integer> cartIds = cartList.stream()
|
|
|
|
|
+ .map(StoreCart::getId)
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+ // 使用 removeBatchIds 进行逻辑删除(MyBatis-Plus 会自动处理 @TableLogic)
|
|
|
|
|
+ storeCartMapper.deleteBatchIds(cartIds);
|
|
|
|
|
+ log.info("删除购物车数据, tableId={}, count={}", tableId, cartList.size());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 只删除未支付或已取消的订单(保留已支付/已完成的订单,避免数据丢失)
|
|
|
|
|
+ // 订单状态:0-待支付,1-已支付,2-已取消,3-已完成
|
|
|
|
|
+ LambdaQueryWrapper<StoreOrder> orderWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ orderWrapper.eq(StoreOrder::getTableId, tableId);
|
|
|
|
|
+ orderWrapper.eq(StoreOrder::getDeleteFlag, 0);
|
|
|
|
|
+ // 只查询未支付(0)或已取消(2)的订单
|
|
|
|
|
+ orderWrapper.in(StoreOrder::getOrderStatus, 0, 2);
|
|
|
|
|
+ List<StoreOrder> orderList = this.list(orderWrapper);
|
|
|
|
|
+ List<Integer> orderIds = new ArrayList<>();
|
|
|
|
|
+ if (orderList != null && !orderList.isEmpty()) {
|
|
|
|
|
+ orderIds = orderList.stream()
|
|
|
|
|
+ .map(StoreOrder::getId)
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ // 删除订单明细(逻辑删除,使用 MyBatis-Plus 的 deleteBatchIds)
|
|
|
|
|
+ for (Integer orderId : orderIds) {
|
|
|
|
|
+ LambdaQueryWrapper<StoreOrderDetail> detailWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ detailWrapper.eq(StoreOrderDetail::getOrderId, orderId);
|
|
|
|
|
+ detailWrapper.eq(StoreOrderDetail::getDeleteFlag, 0);
|
|
|
|
|
+ List<StoreOrderDetail> detailList = orderDetailMapper.selectList(detailWrapper);
|
|
|
|
|
+ if (detailList != null && !detailList.isEmpty()) {
|
|
|
|
|
+ List<Integer> detailIds = detailList.stream()
|
|
|
|
|
+ .map(StoreOrderDetail::getId)
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+ // 使用 deleteBatchIds 进行逻辑删除(MyBatis-Plus 会自动处理 @TableLogic)
|
|
|
|
|
+ orderDetailMapper.deleteBatchIds(detailIds);
|
|
|
|
|
+ log.info("删除订单明细, orderId={}, count={}", orderId, detailList.size());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 删除订单变更记录(逻辑删除,使用 MyBatis-Plus 的 deleteBatchIds)
|
|
|
|
|
+ for (Integer orderId : orderIds) {
|
|
|
|
|
+ LambdaQueryWrapper<StoreOrderChangeLog> changeLogWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ changeLogWrapper.eq(StoreOrderChangeLog::getOrderId, orderId);
|
|
|
|
|
+ changeLogWrapper.eq(StoreOrderChangeLog::getDeleteFlag, 0);
|
|
|
|
|
+ List<StoreOrderChangeLog> changeLogList = orderChangeLogMapper.selectList(changeLogWrapper);
|
|
|
|
|
+ if (changeLogList != null && !changeLogList.isEmpty()) {
|
|
|
|
|
+ List<Integer> changeLogIds = changeLogList.stream()
|
|
|
|
|
+ .map(StoreOrderChangeLog::getId)
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+ // 使用 deleteBatchIds 进行逻辑删除(MyBatis-Plus 会自动处理 @TableLogic)
|
|
|
|
|
+ orderChangeLogMapper.deleteBatchIds(changeLogIds);
|
|
|
|
|
+ log.info("删除订单变更记录, orderId={}, count={}", orderId, changeLogList.size());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 删除订单(逻辑删除,使用 MyBatis-Plus 的 removeByIds)
|
|
|
|
|
+ // 使用 removeByIds 进行逻辑删除(MyBatis-Plus 会自动处理 @TableLogic)
|
|
|
|
|
+ this.removeByIds(orderIds);
|
|
|
|
|
+ log.info("删除未支付/已取消订单数据, tableId={}, count={}", tableId, orderList.size());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 查询该桌号的所有订单(包括已支付/已完成的),用于后续处理锁定记录
|
|
|
|
|
+ LambdaQueryWrapper<StoreOrder> allOrderWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ allOrderWrapper.eq(StoreOrder::getTableId, tableId);
|
|
|
|
|
+ allOrderWrapper.eq(StoreOrder::getDeleteFlag, 0);
|
|
|
|
|
+ List<StoreOrder> allOrderList = this.list(allOrderWrapper);
|
|
|
|
|
+ List<Integer> allOrderIds = new ArrayList<>();
|
|
|
|
|
+ if (allOrderList != null && !allOrderList.isEmpty()) {
|
|
|
|
|
+ allOrderIds = allOrderList.stream()
|
|
|
|
|
+ .map(StoreOrder::getId)
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 只删除未下单的优惠券使用记录(保留已下单/已支付的记录,避免数据丢失)
|
|
|
|
|
+ // usageStatus: 0-已标记使用, 1-已下单, 2-已支付, 3-已取消
|
|
|
|
|
+ LambdaQueryWrapper<StoreCouponUsage> couponUsageWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ couponUsageWrapper.eq(StoreCouponUsage::getTableId, tableId);
|
|
|
|
|
+ couponUsageWrapper.eq(StoreCouponUsage::getDeleteFlag, 0);
|
|
|
|
|
+ // 只删除已标记使用但未下单的记录(usageStatus=0)
|
|
|
|
|
+ couponUsageWrapper.eq(StoreCouponUsage::getUsageStatus, 0);
|
|
|
|
|
+ List<StoreCouponUsage> couponUsageList = storeCouponUsageMapper.selectList(couponUsageWrapper);
|
|
|
|
|
+ if (couponUsageList != null && !couponUsageList.isEmpty()) {
|
|
|
|
|
+ List<Integer> couponUsageIds = couponUsageList.stream()
|
|
|
|
|
+ .map(StoreCouponUsage::getId)
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+ // 使用 deleteBatchIds 进行逻辑删除(MyBatis-Plus 会自动处理 @TableLogic)
|
|
|
|
|
+ storeCouponUsageMapper.deleteBatchIds(couponUsageIds);
|
|
|
|
|
+ log.info("删除未下单的优惠券使用记录, tableId={}, count={}", tableId, couponUsageList.size());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 4. 删除该桌号的所有订单锁定记录(逻辑删除)
|
|
|
|
|
+ // 包括下单锁定(lock_type=1,通过 tableId 查找)和结算锁定(lock_type=2,通过订单ID关联)
|
|
|
|
|
+
|
|
|
|
|
+ // 删除下单锁定(通过 tableId 查找)
|
|
|
|
|
+ LambdaQueryWrapper<StoreOrderLock> tableLockWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ tableLockWrapper.eq(StoreOrderLock::getTableId, tableId);
|
|
|
|
|
+ tableLockWrapper.eq(StoreOrderLock::getDeleteFlag, 0);
|
|
|
|
|
+ List<StoreOrderLock> tableLockList = storeOrderLockMapper.selectList(tableLockWrapper);
|
|
|
|
|
+ if (tableLockList != null && !tableLockList.isEmpty()) {
|
|
|
|
|
+ List<Integer> tableLockIds = tableLockList.stream()
|
|
|
|
|
+ .map(StoreOrderLock::getId)
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+ storeOrderLockMapper.deleteBatchIds(tableLockIds);
|
|
|
|
|
+ log.info("删除下单锁定记录, tableId={}, count={}", tableId, tableLockList.size());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 删除结算锁定(通过 orderId 查找,如果有订单的话)
|
|
|
|
|
+ // 注意:这里使用 allOrderIds,包括所有订单(已删除和未删除的),因为锁定记录可能关联已支付的订单
|
|
|
|
|
+ if (!allOrderIds.isEmpty()) {
|
|
|
|
|
+ LambdaQueryWrapper<StoreOrderLock> orderLockWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ orderLockWrapper.in(StoreOrderLock::getOrderId, allOrderIds);
|
|
|
|
|
+ orderLockWrapper.eq(StoreOrderLock::getDeleteFlag, 0);
|
|
|
|
|
+ List<StoreOrderLock> orderLockList = storeOrderLockMapper.selectList(orderLockWrapper);
|
|
|
|
|
+ if (orderLockList != null && !orderLockList.isEmpty()) {
|
|
|
|
|
+ List<Integer> orderLockIds = orderLockList.stream()
|
|
|
|
|
+ .map(StoreOrderLock::getId)
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+ storeOrderLockMapper.deleteBatchIds(orderLockIds);
|
|
|
|
|
+ log.info("删除结算锁定记录, orderIds={}, count={}", allOrderIds, orderLockList.size());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 5. 清空Redis中的购物车缓存
|
|
|
|
|
+ String cartKey = "cart:table:" + tableId;
|
|
|
|
|
+ baseRedisService.delete(cartKey);
|
|
|
|
|
+ log.info("清空Redis购物车缓存, tableId={}", tableId);
|
|
|
|
|
+
|
|
|
|
|
+ // 6. 清除优惠券使用标记(Redis中的标记)
|
|
|
|
|
+ cartService.clearCouponUsed(tableId);
|
|
|
|
|
+ log.info("清除优惠券使用标记, tableId={}", tableId);
|
|
|
|
|
+
|
|
|
|
|
+ // 7. 重置餐桌表(使用 LambdaUpdateWrapper 来显式设置 null 值)
|
|
|
|
|
+ LambdaUpdateWrapper<StoreTable> updateWrapper = new LambdaUpdateWrapper<>();
|
|
|
|
|
+ updateWrapper.eq(StoreTable::getId, tableId)
|
|
|
|
|
+ .set(StoreTable::getCurrentOrderId, null) // 显式设置 null
|
|
|
|
|
+ .set(StoreTable::getCurrentCouponId, null) // 显式设置 null
|
|
|
|
|
+ .set(StoreTable::getCartItemCount, 0)
|
|
|
|
|
+ .set(StoreTable::getCartTotalAmount, BigDecimal.ZERO)
|
|
|
|
|
+ .set(StoreTable::getStatus, 0) // 空闲
|
|
|
|
|
+ .set(StoreTable::getDinerCount, null) // 清空就餐人数
|
|
|
|
|
+ .set(StoreTable::getUpdatedTime, new Date());
|
|
|
|
|
+ Integer userId = TokenUtil.getCurrentUserId();
|
|
|
|
|
+ if (userId != null) {
|
|
|
|
|
+ updateWrapper.set(StoreTable::getUpdatedUserId, userId);
|
|
|
|
|
+ }
|
|
|
|
|
+ storeTableMapper.update(null, updateWrapper);
|
|
|
|
|
+ log.info("重置餐桌表完成, tableId={}", tableId);
|
|
|
|
|
+
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 支付完成后重置餐桌(保留订单数据,只重置餐桌绑定关系)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param tableId 餐桌ID
|
|
|
|
|
+ */
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public void resetTableAfterPayment(Integer tableId) {
|
|
|
|
|
+ log.info("支付完成后重置餐桌, tableId={}", tableId);
|
|
|
|
|
+
|
|
|
|
|
+ // 验证餐桌是否存在
|
|
|
|
|
+ StoreTable table = storeTableMapper.selectById(tableId);
|
|
|
|
|
+ if (table == null) {
|
|
|
|
|
+ log.warn("餐桌不存在,跳过重置, tableId={}", tableId);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 完全清空购物车(包括已下单的商品,因为订单已支付完成)
|
|
|
|
|
+ // 逻辑删除所有购物车数据
|
|
|
|
|
+ LambdaQueryWrapper<StoreCart> cartWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ cartWrapper.eq(StoreCart::getTableId, tableId);
|
|
|
|
|
+ cartWrapper.eq(StoreCart::getDeleteFlag, 0);
|
|
|
|
|
+ List<StoreCart> cartList = storeCartMapper.selectList(cartWrapper);
|
|
|
|
|
+ if (cartList != null && !cartList.isEmpty()) {
|
|
|
|
|
+ List<Integer> cartIds = cartList.stream()
|
|
|
|
|
+ .map(StoreCart::getId)
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+ storeCartMapper.deleteBatchIds(cartIds);
|
|
|
|
|
+ log.info("支付完成后删除购物车数据, tableId={}, count={}", tableId, cartList.size());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 清空Redis中的购物车缓存
|
|
|
|
|
+ String cartKey = "cart:table:" + tableId;
|
|
|
|
|
+ baseRedisService.delete(cartKey);
|
|
|
|
|
+ log.info("清空Redis购物车缓存, tableId={}", tableId);
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 清除优惠券使用标记
|
|
|
|
|
+ cartService.clearCouponUsed(tableId);
|
|
|
|
|
+ log.info("清除优惠券使用标记, tableId={}", tableId);
|
|
|
|
|
+
|
|
|
|
|
+ // 4. 重置餐桌表(使用 LambdaUpdateWrapper 来显式设置 null 值)
|
|
|
|
|
+ LambdaUpdateWrapper<StoreTable> updateWrapper = new LambdaUpdateWrapper<>();
|
|
|
|
|
+ updateWrapper.eq(StoreTable::getId, tableId)
|
|
|
|
|
+ .set(StoreTable::getCurrentOrderId, null) // 显式设置 null
|
|
|
|
|
+ .set(StoreTable::getCurrentCouponId, null) // 显式设置 null
|
|
|
|
|
+ .set(StoreTable::getCartItemCount, 0)
|
|
|
|
|
+ .set(StoreTable::getCartTotalAmount, BigDecimal.ZERO)
|
|
|
|
|
+ .set(StoreTable::getStatus, 0) // 空闲
|
|
|
|
|
+ .set(StoreTable::getDinerCount, null) // 清空就餐人数
|
|
|
|
|
+ .set(StoreTable::getUpdatedTime, new Date());
|
|
|
|
|
+ Integer userId = TokenUtil.getCurrentUserId();
|
|
|
|
|
+ if (userId != null) {
|
|
|
|
|
+ updateWrapper.set(StoreTable::getUpdatedUserId, userId);
|
|
|
|
|
+ }
|
|
|
|
|
+ storeTableMapper.update(null, updateWrapper);
|
|
|
|
|
+ log.info("支付完成后重置餐桌表完成, tableId={}", tableId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public IPage<StoreOrder> getMyOrders(Page<StoreOrder> page, String type) {
|
|
|
|
|
+ log.info("查询我的订单, type={}", type);
|
|
|
|
|
+
|
|
|
|
|
+ // 获取当前用户ID
|
|
|
|
|
+ Integer userId = TokenUtil.getCurrentUserId();
|
|
|
|
|
+ if (userId == null) {
|
|
|
|
|
+ throw new RuntimeException("用户未登录");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ LambdaQueryWrapper<StoreOrder> wrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ wrapper.eq(StoreOrder::getDeleteFlag, 0);
|
|
|
|
|
+ wrapper.eq(StoreOrder::getCreatedUserId, userId); // 查询当前用户创建的订单
|
|
|
|
|
+
|
|
|
|
|
+ // 根据类型过滤订单
|
|
|
|
|
+ if ("0".equals(type) || "unpaid".equalsIgnoreCase(type)) {
|
|
|
|
|
+ // 未支付订单:orderStatus=0 且 payStatus=0
|
|
|
|
|
+ wrapper.eq(StoreOrder::getOrderStatus, 0);
|
|
|
|
|
+ wrapper.eq(StoreOrder::getPayStatus, 0);
|
|
|
|
|
+ log.info("查询未支付订单, userId={}", userId);
|
|
|
|
|
+ } else if ("1".equals(type) || "history".equalsIgnoreCase(type)) {
|
|
|
|
|
+ // 历史订单:已支付或已完成的订单(orderStatus=1 或 3,payStatus=1)
|
|
|
|
|
+ wrapper.and(w -> w.and(w1 -> w1.eq(StoreOrder::getOrderStatus, 1).eq(StoreOrder::getPayStatus, 1))
|
|
|
|
|
+ .or(w2 -> w2.eq(StoreOrder::getOrderStatus, 3).eq(StoreOrder::getPayStatus, 1)));
|
|
|
|
|
+ log.info("查询历史订单, userId={}", userId);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 如果类型不明确,返回所有订单
|
|
|
|
|
+ log.warn("订单类型不明确, type={}, 返回所有订单", type);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ wrapper.orderByDesc(StoreOrder::getCreatedTime);
|
|
|
|
|
+ return this.page(page, wrapper);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @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));
|
|
|
|
|
+
|
|
|
|
|
+ // 4.1 批量查询菜品标签(用于我的订单分页展示)
|
|
|
|
|
+ Set<Integer> cuisineIdsMy = allDetails.stream()
|
|
|
|
|
+ .map(StoreOrderDetail::getCuisineId)
|
|
|
|
|
+ .filter(Objects::nonNull)
|
|
|
|
|
+ .collect(Collectors.toSet());
|
|
|
|
|
+ Map<Integer, String> cuisineIdToTagsMy = new HashMap<>();
|
|
|
|
|
+ if (!cuisineIdsMy.isEmpty()) {
|
|
|
|
|
+ List<StoreCuisine> cuisinesMy = storeCuisineMapper.selectBatchIds(new ArrayList<>(cuisineIdsMy));
|
|
|
|
|
+ if (cuisinesMy != null) {
|
|
|
|
|
+ for (StoreCuisine c : cuisinesMy) {
|
|
|
|
|
+ if (c.getTags() != null) {
|
|
|
|
|
+ cuisineIdToTagsMy.put(c.getId(), c.getTags());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ Map<Integer, String> finalCuisineIdToTagsMy = cuisineIdToTagsMy;
|
|
|
|
|
+
|
|
|
|
|
+ // 5. 批量查询门店名称
|
|
|
|
|
+ Set<Integer> storeIds = orders.stream().map(StoreOrder::getStoreId).filter(Objects::nonNull).collect(Collectors.toSet());
|
|
|
|
|
+ Map<Integer, String> storeNameMap = new HashMap<>();
|
|
|
|
|
+ if (!storeIds.isEmpty()) {
|
|
|
|
|
+ List<StoreInfo> storeList = storeInfoMapper.selectBatchIds(storeIds);
|
|
|
|
|
+ if (storeList != null) {
|
|
|
|
|
+ for (StoreInfo s : storeList) {
|
|
|
|
|
+ storeNameMap.put(s.getId(), s.getStoreName());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 6. 构建返回结果
|
|
|
|
|
+ List<StoreOrderPageVO> voList = orders.stream().map(order -> {
|
|
|
|
|
+ StoreOrderPageVO vo = new StoreOrderPageVO();
|
|
|
|
|
+ vo.setOrder(order);
|
|
|
|
|
+ vo.setStoreName(storeNameMap.getOrDefault(order.getStoreId(), ""));
|
|
|
|
|
+
|
|
|
|
|
+ // 获取该订单的菜品列表(含菜品标签)
|
|
|
|
|
+ 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());
|
|
|
|
|
+ item.setUnitPrice(detail.getUnitPrice());
|
|
|
|
|
+ item.setTags(detail.getCuisineId() != null ? finalCuisineIdToTagsMy.get(detail.getCuisineId()) : null);
|
|
|
|
|
+ return item;
|
|
|
|
|
+ })
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+ vo.setCuisineItems(cuisineItems);
|
|
|
|
|
+
|
|
|
|
|
+ return vo;
|
|
|
|
|
+ }).collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ // 7. 构建分页结果
|
|
|
|
|
+ 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);
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 查询订单基本信息
|
|
|
|
|
+ StoreOrder order = this.getById(orderId);
|
|
|
|
|
+ if (order == null || order.getDeleteFlag() == 1) {
|
|
|
|
|
+ throw new RuntimeException("订单不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 查询订单明细(菜品清单)
|
|
|
|
|
+ LambdaQueryWrapper<StoreOrderDetail> detailWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ detailWrapper.eq(StoreOrderDetail::getOrderId, orderId);
|
|
|
|
|
+ detailWrapper.eq(StoreOrderDetail::getDeleteFlag, 0);
|
|
|
|
|
+ detailWrapper.orderByDesc(StoreOrderDetail::getCreatedTime);
|
|
|
|
|
+ List<StoreOrderDetail> details = orderDetailMapper.selectList(detailWrapper);
|
|
|
|
|
+
|
|
|
|
|
+ // 批量查询菜品标签(订单明细无 tags,从价目表取)
|
|
|
|
|
+ java.util.Set<Integer> cuisineIds = details.stream()
|
|
|
|
|
+ .map(StoreOrderDetail::getCuisineId)
|
|
|
|
|
+ .filter(java.util.Objects::nonNull)
|
|
|
|
|
+ .collect(Collectors.toSet());
|
|
|
|
|
+ java.util.Map<Integer, String> cuisineIdToTags = new java.util.HashMap<>();
|
|
|
|
|
+ if (!cuisineIds.isEmpty()) {
|
|
|
|
|
+ List<StoreCuisine> cuisines = storeCuisineMapper.selectBatchIds(cuisineIds);
|
|
|
|
|
+ if (cuisines != null) {
|
|
|
|
|
+ for (StoreCuisine c : cuisines) {
|
|
|
|
|
+ cuisineIdToTags.put(c.getId(), c.getTags());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ final java.util.Map<Integer, String> tagsMap = cuisineIdToTags;
|
|
|
|
|
+
|
|
|
|
|
+ // 转换为CartItemDTO(含 tags)
|
|
|
|
|
+ List<CartItemDTO> items = details.stream().map(detail -> {
|
|
|
|
|
+ CartItemDTO item = new CartItemDTO();
|
|
|
|
|
+ item.setCuisineId(detail.getCuisineId());
|
|
|
|
|
+ item.setCuisineName(detail.getCuisineName());
|
|
|
|
|
+ item.setCuisineType(detail.getCuisineType());
|
|
|
|
|
+ item.setCuisineImage(detail.getCuisineImage());
|
|
|
|
|
+ item.setUnitPrice(detail.getUnitPrice());
|
|
|
|
|
+ item.setQuantity(detail.getQuantity());
|
|
|
|
|
+ item.setSubtotalAmount(detail.getSubtotalAmount());
|
|
|
|
|
+ item.setAddUserId(detail.getAddUserId());
|
|
|
|
|
+ item.setAddUserPhone(detail.getAddUserPhone());
|
|
|
|
|
+ item.setRemark(detail.getRemark());
|
|
|
|
|
+ item.setTags(detail.getCuisineId() != null ? tagsMap.get(detail.getCuisineId()) : null);
|
|
|
|
|
+ return item;
|
|
|
|
|
+ }).collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 查询门店信息
|
|
|
|
|
+ StoreInfo storeInfo = storeInfoMapper.selectById(order.getStoreId());
|
|
|
|
|
+ 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;
|
|
|
|
|
+ Integer couponType = null;
|
|
|
|
|
+ BigDecimal discountRate = null;
|
|
|
|
|
+ BigDecimal nominalValue = null;
|
|
|
|
|
+ BigDecimal minimumSpendingAmount = null;
|
|
|
|
|
+ if (order.getCouponId() != null) {
|
|
|
|
|
+ LifeDiscountCoupon coupon = lifeDiscountCouponMapper.selectById(order.getCouponId());
|
|
|
|
|
+ if (coupon != null) {
|
|
|
|
|
+ couponName = coupon.getName();
|
|
|
|
|
+ couponType = coupon.getCouponType();
|
|
|
|
|
+ discountRate = coupon.getDiscountRate();
|
|
|
|
|
+ nominalValue = coupon.getNominalValue();
|
|
|
|
|
+ minimumSpendingAmount = coupon.getMinimumSpendingAmount();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 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());
|
|
|
|
|
+ vo.setRemark(order.getRemark());
|
|
|
|
|
+ vo.setItems(items);
|
|
|
|
|
+ vo.setTotalAmount(order.getTotalAmount());
|
|
|
|
|
+ vo.setTablewareFee(order.getTablewareFee());
|
|
|
|
|
+ vo.setCouponId(order.getCouponId());
|
|
|
|
|
+ vo.setCouponName(couponName);
|
|
|
|
|
+ vo.setCouponType(couponType);
|
|
|
|
|
+ vo.setDiscountRate(discountRate);
|
|
|
|
|
+ vo.setNominalValue(nominalValue);
|
|
|
|
|
+ vo.setMinimumSpendingAmount(minimumSpendingAmount);
|
|
|
|
|
+ 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());
|
|
|
|
|
+
|
|
|
|
|
+ log.info("查询订单信息完成, orderId={}, itemCount={}", orderId, items.size());
|
|
|
|
|
+ return vo;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public List<OrderChangeLogBatchVO> getOrderChangeLogs(Integer orderId) {
|
|
|
|
|
+ log.info("查询订单变更记录, orderId={}", orderId);
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 查询订单的所有变更记录
|
|
|
|
|
+ LambdaQueryWrapper<StoreOrderChangeLog> wrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ wrapper.eq(StoreOrderChangeLog::getOrderId, orderId);
|
|
|
|
|
+ wrapper.eq(StoreOrderChangeLog::getDeleteFlag, 0);
|
|
|
|
|
+ wrapper.orderByAsc(StoreOrderChangeLog::getOperationTime);
|
|
|
|
|
+ wrapper.orderByAsc(StoreOrderChangeLog::getBatchNo);
|
|
|
|
|
+ List<StoreOrderChangeLog> logs = orderChangeLogMapper.selectList(wrapper);
|
|
|
|
|
+
|
|
|
|
|
+ if (logs == null || logs.isEmpty()) {
|
|
|
|
|
+ log.info("订单没有变更记录, orderId={}", orderId);
|
|
|
|
|
+ return new ArrayList<>();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 按批次号分组
|
|
|
|
|
+ Map<String, List<StoreOrderChangeLog>> batchMap = logs.stream()
|
|
|
|
|
+ .collect(Collectors.groupingBy(StoreOrderChangeLog::getBatchNo));
|
|
|
|
|
+
|
|
|
|
|
+ // 2.1 批量查询菜品标签(用于订单详情展示)
|
|
|
|
|
+ Set<Integer> cuisineIds = logs.stream()
|
|
|
|
|
+ .map(StoreOrderChangeLog::getCuisineId)
|
|
|
|
|
+ .filter(Objects::nonNull)
|
|
|
|
|
+ .collect(Collectors.toSet());
|
|
|
|
|
+ Map<Integer, String> cuisineIdToTags = new HashMap<>();
|
|
|
|
|
+ if (!cuisineIds.isEmpty()) {
|
|
|
|
|
+ List<StoreCuisine> cuisines = storeCuisineMapper.selectBatchIds(new ArrayList<>(cuisineIds));
|
|
|
|
|
+ if (cuisines != null) {
|
|
|
|
|
+ for (StoreCuisine c : cuisines) {
|
|
|
|
|
+ if (c.getTags() != null) {
|
|
|
|
|
+ cuisineIdToTags.put(c.getId(), c.getTags());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 转换为批次VO列表
|
|
|
|
|
+ List<OrderChangeLogBatchVO> batchList = new ArrayList<>();
|
|
|
|
|
+ for (Map.Entry<String, List<StoreOrderChangeLog>> entry : batchMap.entrySet()) {
|
|
|
|
|
+ String batchNo = entry.getKey();
|
|
|
|
|
+ List<StoreOrderChangeLog> batchLogs = entry.getValue();
|
|
|
|
|
+
|
|
|
|
|
+ if (batchLogs.isEmpty()) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 取第一条记录作为批次信息(同一批次的操作类型、时间等信息相同)
|
|
|
|
|
+ StoreOrderChangeLog firstLog = batchLogs.get(0);
|
|
|
|
|
+
|
|
|
|
|
+ OrderChangeLogBatchVO batchVO = new OrderChangeLogBatchVO();
|
|
|
|
|
+ batchVO.setBatchNo(batchNo);
|
|
|
|
|
+ batchVO.setOperationType(firstLog.getOperationType());
|
|
|
|
|
+ batchVO.setOperationTypeText(getOperationTypeText(firstLog.getOperationType()));
|
|
|
|
|
+ batchVO.setOperationTime(firstLog.getOperationTime());
|
|
|
|
|
+ batchVO.setOperatorUserId(firstLog.getOperatorUserId());
|
|
|
|
|
+ batchVO.setOperatorUserPhone(firstLog.getOperatorUserPhone());
|
|
|
|
|
+ batchVO.setRemark(firstLog.getRemark());
|
|
|
|
|
+
|
|
|
|
|
+ // 计算批次统计信息
|
|
|
|
|
+ Integer totalQuantityChange = batchLogs.stream()
|
|
|
|
|
+ .mapToInt(log -> log.getQuantityChange() != null ? log.getQuantityChange() : 0)
|
|
|
|
|
+ .sum();
|
|
|
|
|
+ BigDecimal totalAmountChange = batchLogs.stream()
|
|
|
|
|
+ .map(log -> log.getAmountChange() != null ? log.getAmountChange() : BigDecimal.ZERO)
|
|
|
|
|
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
|
|
+
|
|
|
|
|
+ batchVO.setTotalQuantityChange(totalQuantityChange);
|
|
|
|
|
+ batchVO.setTotalAmountChange(totalAmountChange);
|
|
|
|
|
+ batchVO.setItemCount(batchLogs.size());
|
|
|
|
|
+
|
|
|
|
|
+ // 转换为商品项VO列表(含菜品标签)
|
|
|
|
|
+ Map<Integer, String> finalCuisineIdToTags = cuisineIdToTags;
|
|
|
|
|
+ List<OrderChangeLogItemVO> items = batchLogs.stream().map(log -> {
|
|
|
|
|
+ OrderChangeLogItemVO itemVO = new OrderChangeLogItemVO();
|
|
|
|
|
+ itemVO.setCuisineId(log.getCuisineId());
|
|
|
|
|
+ itemVO.setCuisineName(log.getCuisineName());
|
|
|
|
|
+ itemVO.setCuisineType(log.getCuisineType());
|
|
|
|
|
+ itemVO.setCuisineImage(log.getCuisineImage());
|
|
|
|
|
+ itemVO.setUnitPrice(log.getUnitPrice());
|
|
|
|
|
+ itemVO.setQuantityChange(log.getQuantityChange());
|
|
|
|
|
+ itemVO.setQuantityBefore(log.getQuantityBefore());
|
|
|
|
|
+ itemVO.setQuantityAfter(log.getQuantityAfter());
|
|
|
|
|
+ itemVO.setAmountChange(log.getAmountChange());
|
|
|
|
|
+ itemVO.setRemark(log.getRemark());
|
|
|
|
|
+ itemVO.setTags(log.getCuisineId() != null ? finalCuisineIdToTags.get(log.getCuisineId()) : null);
|
|
|
|
|
+ return itemVO;
|
|
|
|
|
+ }).collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ batchVO.setItems(items);
|
|
|
|
|
+ batchList.add(batchVO);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 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;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取操作类型文本
|
|
|
|
|
+ */
|
|
|
|
|
+ private String getOperationTypeText(Integer operationType) {
|
|
|
|
|
+ if (operationType == null) {
|
|
|
|
|
+ return "未知";
|
|
|
|
|
+ }
|
|
|
|
|
+ switch (operationType) {
|
|
|
|
|
+ case 1:
|
|
|
|
|
+ return "首次下单";
|
|
|
|
|
+ case 3:
|
|
|
|
|
+ return "更新订单";
|
|
|
|
|
+ default:
|
|
|
|
|
+ return "未知";
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 记录订单变更日志
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param orderId 订单ID
|
|
|
|
|
+ * @param orderNo 订单号
|
|
|
|
|
+ * @param items 商品列表
|
|
|
|
|
+ * @param operationType 操作类型(1:首次下单, 3:更新订单)
|
|
|
|
|
+ * @param operationTime 操作时间
|
|
|
|
|
+ * @param userId 操作人ID
|
|
|
|
|
+ * @param userPhone 操作人手机号
|
|
|
|
|
+ */
|
|
|
|
|
+ private void recordOrderChangeLog(Integer orderId, String orderNo, List<shop.alien.entity.store.dto.CartItemDTO> items,
|
|
|
|
|
+ Integer operationType, Date operationTime, Integer userId, String userPhone) {
|
|
|
|
|
+ if (items == null || items.isEmpty()) {
|
|
|
|
|
+ log.warn("商品列表为空,不记录变更日志, orderId={}", orderId);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 生成批次号:ORDER{orderId}_{yyyyMMddHHmmss}
|
|
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
|
|
|
|
|
+ String timestamp = sdf.format(operationTime);
|
|
|
|
|
+ String batchNo = "ORDER" + orderId + "_" + timestamp;
|
|
|
|
|
+
|
|
|
|
|
+ List<StoreOrderChangeLog> changeLogs = new ArrayList<>();
|
|
|
|
|
+
|
|
|
|
|
+ for (shop.alien.entity.store.dto.CartItemDTO item : items) {
|
|
|
|
|
+ Integer lockedQuantity = item.getLockedQuantity(); // 已下单数量
|
|
|
|
|
+ Integer currentQuantity = item.getQuantity(); // 当前数量
|
|
|
|
|
+ Integer quantityChange = 0; // 数量变化
|
|
|
|
|
+ Integer quantityBefore = 0; // 变化前数量
|
|
|
|
|
+ Integer quantityAfter = 0; // 变化后数量
|
|
|
|
|
+
|
|
|
|
|
+ if (operationType == 1) {
|
|
|
|
|
+ // 首次下单:记录所有商品
|
|
|
|
|
+ quantityBefore = 0;
|
|
|
|
|
+ quantityAfter = currentQuantity != null ? currentQuantity : 0;
|
|
|
|
|
+ quantityChange = quantityAfter;
|
|
|
|
|
+ } else if (operationType == 3) {
|
|
|
|
|
+ // 更新订单:只记录新增的商品或数量增加的商品
|
|
|
|
|
+ if (lockedQuantity == null || lockedQuantity == 0) {
|
|
|
|
|
+ // 新增商品
|
|
|
|
|
+ quantityBefore = 0;
|
|
|
|
|
+ quantityAfter = currentQuantity != null ? currentQuantity : 0;
|
|
|
|
|
+ quantityChange = quantityAfter;
|
|
|
|
|
+ } else if (currentQuantity != null && currentQuantity > lockedQuantity) {
|
|
|
|
|
+ // 数量增加
|
|
|
|
|
+ quantityBefore = lockedQuantity;
|
|
|
|
|
+ quantityAfter = currentQuantity;
|
|
|
|
|
+ quantityChange = quantityAfter - quantityBefore;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 没有变化,跳过
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 只记录有数量变化的商品
|
|
|
|
|
+ if (quantityChange == null || quantityChange == 0) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 计算金额变化
|
|
|
|
|
+ BigDecimal amountChange = item.getUnitPrice() != null
|
|
|
|
|
+ ? item.getUnitPrice().multiply(BigDecimal.valueOf(quantityChange))
|
|
|
|
|
+ : BigDecimal.ZERO;
|
|
|
|
|
+
|
|
|
|
|
+ StoreOrderChangeLog changeLog = new StoreOrderChangeLog();
|
|
|
|
|
+ changeLog.setOrderId(orderId);
|
|
|
|
|
+ changeLog.setOrderNo(orderNo);
|
|
|
|
|
+ changeLog.setBatchNo(batchNo);
|
|
|
|
|
+ changeLog.setOperationType(operationType);
|
|
|
|
|
+ changeLog.setCuisineId(item.getCuisineId());
|
|
|
|
|
+ changeLog.setCuisineName(item.getCuisineName());
|
|
|
|
|
+ changeLog.setCuisineType(item.getCuisineType());
|
|
|
|
|
+ changeLog.setCuisineImage(item.getCuisineImage());
|
|
|
|
|
+ changeLog.setUnitPrice(item.getUnitPrice());
|
|
|
|
|
+ changeLog.setQuantityChange(quantityChange);
|
|
|
|
|
+ changeLog.setQuantityBefore(quantityBefore);
|
|
|
|
|
+ changeLog.setQuantityAfter(quantityAfter);
|
|
|
|
|
+ changeLog.setAmountChange(amountChange);
|
|
|
|
|
+ changeLog.setOperationTime(operationTime);
|
|
|
|
|
+ changeLog.setOperatorUserId(userId);
|
|
|
|
|
+ changeLog.setOperatorUserPhone(userPhone);
|
|
|
|
|
+ changeLog.setRemark(item.getRemark());
|
|
|
|
|
+ changeLog.setCreatedUserId(userId);
|
|
|
|
|
+ changeLog.setCreatedTime(operationTime);
|
|
|
|
|
+ changeLog.setUpdatedTime(operationTime);
|
|
|
|
|
+
|
|
|
|
|
+ changeLogs.add(changeLog);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 批量插入变更记录(使用 MyBatis-Plus 的批量插入)
|
|
|
|
|
+ if (!changeLogs.isEmpty()) {
|
|
|
|
|
+ // 使用循环插入(MyBatis-Plus 的 BaseMapper 没有批量插入方法,需要手动实现或使用 ServiceImpl 的 saveBatch)
|
|
|
|
|
+ // 注意:如果变更记录数量很大,可以考虑使用 MyBatis 的批量插入
|
|
|
|
|
+ for (StoreOrderChangeLog log : changeLogs) {
|
|
|
|
|
+ orderChangeLogMapper.insert(log);
|
|
|
|
|
+ }
|
|
|
|
|
+ log.info("记录订单变更日志完成, orderId={}, batchNo={}, operationType={}, itemCount={}",
|
|
|
|
|
+ orderId, batchNo, operationType, changeLogs.size());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ log.warn("没有需要记录的变更, orderId={}, operationType={}", orderId, operationType);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 生成订单号
|
|
|
|
|
+ */
|
|
|
|
|
+ private String generateOrderNo() {
|
|
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
|
|
|
|
|
+ String timestamp = sdf.format(new Date());
|
|
|
|
|
+ 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);
|
|
|
|
|
+
|
|
|
|
|
+ // 0. 校验:目标桌只能是空桌(空闲且无当前订单)
|
|
|
|
|
+ if (fromTableId.equals(toTableId)) {
|
|
|
|
|
+ throw new RuntimeException("原桌号与目标桌号不能相同");
|
|
|
|
|
+ }
|
|
|
|
|
+ StoreTable fromTable = storeTableMapper.selectById(fromTableId);
|
|
|
|
|
+ if (fromTable == null) {
|
|
|
|
|
+ throw new RuntimeException("原桌号不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ StoreTable toTable = storeTableMapper.selectById(toTableId);
|
|
|
|
|
+ if (toTable == null) {
|
|
|
|
|
+ throw new RuntimeException("目标桌号不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!fromTable.getStoreId().equals(toTable.getStoreId())) {
|
|
|
|
|
+ throw new RuntimeException("原桌号与目标桌号须在同一门店");
|
|
|
|
|
+ }
|
|
|
|
|
+ // 空桌:状态为空闲(0)且无当前订单
|
|
|
|
|
+ boolean emptyStatus = (toTable.getStatus() == null || toTable.getStatus() == 0);
|
|
|
|
|
+ boolean noOrder = (toTable.getCurrentOrderId() == null);
|
|
|
|
|
+ if (!emptyStatus || !noOrder) {
|
|
|
|
|
+ throw new RuntimeException("只能换到空桌,请选择空闲且无订单的桌号");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 迁移购物车
|
|
|
|
|
+ shop.alien.entity.store.dto.CartDTO cart = cartService.migrateCart(fromTableId, toTableId);
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 迁移所有关联数据(订单、订单变更记录、优惠券使用记录等)
|
|
|
|
|
+ migrateTableData(fromTableId, toTableId, userId);
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 记录换桌日志(fromTable、toTable 已在步骤0中查询)
|
|
|
|
|
+ Date now = new Date();
|
|
|
|
|
+ StoreTableLog tableLog = new StoreTableLog();
|
|
|
|
|
+ tableLog.setStoreId(cart.getStoreId());
|
|
|
|
|
+ tableLog.setOrderId(fromTable.getCurrentOrderId()); // 有订单则记录,仅购物车换桌时为 null
|
|
|
|
|
+ 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);
|
|
|
|
|
+ tableLog.setCreatedTime(now);
|
|
|
|
|
+ tableLog.setUpdatedTime(now);
|
|
|
|
|
+ tableLog.setUpdatedUserId(userId);
|
|
|
|
|
+ storeTableLogMapper.insert(tableLog);
|
|
|
|
|
+
|
|
|
|
|
+ // 4. 推送购物车更新消息到新桌号
|
|
|
|
|
+ sseService.pushCartUpdate(toTableId, cart);
|
|
|
|
|
+
|
|
|
|
|
+ log.info("换桌完成, fromTableId={}, toTableId={}", fromTableId, toTableId);
|
|
|
|
|
+ return cart;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 验证订单是否存在且状态正确(用于支付、取消、加餐、更新优惠券等操作)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param orderId 订单ID
|
|
|
|
|
+ * @param expectedStatus 期望的订单状态(null表示不校验状态)
|
|
|
|
|
+ * @param operation 操作名称(用于错误提示)
|
|
|
|
|
+ * @return 订单对象
|
|
|
|
|
+ */
|
|
|
|
|
+ private StoreOrder validateOrderForOperation(Integer orderId, Integer expectedStatus, String operation) {
|
|
|
|
|
+ StoreOrder order = this.getById(orderId);
|
|
|
|
|
+ if (order == null) {
|
|
|
|
|
+ throw new RuntimeException("订单不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (expectedStatus != null && order.getOrderStatus() != expectedStatus) {
|
|
|
|
|
+ throw new RuntimeException("订单状态不正确,无法" + operation);
|
|
|
|
|
+ }
|
|
|
|
|
+ return order;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取当前登录用户信息
|
|
|
|
|
+ *
|
|
|
|
|
+ * @return 用户ID和手机号的数组 [userId, userPhone]
|
|
|
|
|
+ */
|
|
|
|
|
+ private Object[] getCurrentUserInfo() {
|
|
|
|
|
+ Integer userId = TokenUtil.getCurrentUserId();
|
|
|
|
|
+ String userPhone = TokenUtil.getCurrentUserPhone();
|
|
|
|
|
+ if (userId == null) {
|
|
|
|
|
+ throw new RuntimeException("用户未登录");
|
|
|
|
|
+ }
|
|
|
|
|
+ return new Object[]{userId, userPhone};
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 计算优惠金额:根据优惠券类型(满减券或折扣券)计算
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param coupon 优惠券对象
|
|
|
|
|
+ * @param totalWithTableware 订单总金额(含餐具费)
|
|
|
|
|
+ * @return 优惠金额
|
|
|
|
|
+ */
|
|
|
|
|
+ private BigDecimal calculateDiscountAmount(LifeDiscountCoupon coupon, BigDecimal totalWithTableware) {
|
|
|
|
|
+ if (coupon == null || totalWithTableware == null || totalWithTableware.compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
|
|
+ return BigDecimal.ZERO;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Integer couponType = coupon.getCouponType();
|
|
|
|
|
+ BigDecimal discountAmount = BigDecimal.ZERO;
|
|
|
|
|
+
|
|
|
|
|
+ if (couponType != null && couponType == 2) {
|
|
|
|
|
+ // 折扣券:根据折扣率计算优惠金额
|
|
|
|
|
+ // discountRate: 0-100,例如80表示8折,优惠金额 = 订单金额 * (100 - discountRate) / 100
|
|
|
|
|
+ BigDecimal discountRate = coupon.getDiscountRate();
|
|
|
|
|
+ if (discountRate != null && discountRate.compareTo(BigDecimal.ZERO) > 0 && discountRate.compareTo(new BigDecimal(100)) <= 0) {
|
|
|
|
|
+ // 计算折扣后的金额
|
|
|
|
|
+ BigDecimal discountedAmount = totalWithTableware.multiply(discountRate).divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP);
|
|
|
|
|
+ // 优惠金额 = 原价 - 折扣后价格
|
|
|
|
|
+ discountAmount = totalWithTableware.subtract(discountedAmount);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 满减券(默认或couponType=1):使用nominalValue
|
|
|
|
|
+ discountAmount = coupon.getNominalValue();
|
|
|
|
|
+ if (discountAmount == null) {
|
|
|
|
|
+ discountAmount = BigDecimal.ZERO;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 优惠金额不能超过订单总金额
|
|
|
|
|
+ if (discountAmount.compareTo(totalWithTableware) > 0) {
|
|
|
|
|
+ discountAmount = totalWithTableware;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return discountAmount;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+}
|