|
|
@@ -18,13 +18,14 @@ 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.mapper.*;
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
import java.text.SimpleDateFormat;
|
|
|
-import java.util.Date;
|
|
|
-import java.util.List;
|
|
|
+import java.util.*;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
/**
|
|
|
@@ -50,6 +51,7 @@ public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOr
|
|
|
private final StoreCartMapper storeCartMapper;
|
|
|
private final BaseRedisService baseRedisService;
|
|
|
private final StoreInfoMapper storeInfoMapper;
|
|
|
+ private final StoreOrderChangeLogMapper orderChangeLogMapper;
|
|
|
|
|
|
@Override
|
|
|
public StoreOrder createOrder(CreateOrderDTO dto) {
|
|
|
@@ -73,6 +75,31 @@ public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOr
|
|
|
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("购物车中没有新增商品或商品数量未增加,无法创建订单");
|
|
|
+ }
|
|
|
|
|
|
// 验证优惠券(可选,couponId 可以为 null,不选择优惠券时 discountAmount 为 0)
|
|
|
BigDecimal discountAmount = BigDecimal.ZERO;
|
|
|
@@ -127,39 +154,96 @@ public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOr
|
|
|
// 计算实付金额(菜品总价 + 餐具费 - 优惠金额)
|
|
|
BigDecimal payAmount = cart.getTotalAmount().add(tablewareFee).subtract(discountAmount);
|
|
|
|
|
|
- // 生成订单号
|
|
|
- String orderNo = generateOrderNo();
|
|
|
-
|
|
|
- // 创建订单
|
|
|
Date now = new Date();
|
|
|
- StoreOrder 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(cart.getTotalAmount());
|
|
|
- order.setCouponId(dto.getCouponId());
|
|
|
- order.setCurrentCouponId(dto.getCouponId()); // 记录当前使用的优惠券
|
|
|
- order.setDiscountAmount(discountAmount);
|
|
|
- order.setPayAmount(payAmount);
|
|
|
+ StoreOrder order = null;
|
|
|
+ String orderNo = null;
|
|
|
+ boolean isUpdate = false; // 是否是更新订单
|
|
|
|
|
|
- // 如果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);
|
|
|
+ // 检查桌号是否已绑定订单
|
|
|
+ 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(cart.getTotalAmount());
|
|
|
+ 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(cart.getTotalAmount());
|
|
|
+ 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;
|
|
|
|
|
|
// 更新优惠券使用记录状态为已下单
|
|
|
if (dto.getCouponId() != null) {
|
|
|
@@ -171,19 +255,22 @@ public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOr
|
|
|
usageWrapper.last("LIMIT 1");
|
|
|
StoreCouponUsage usage = storeCouponUsageMapper.selectOne(usageWrapper);
|
|
|
if (usage != null) {
|
|
|
- usage.setOrderId(order.getId());
|
|
|
+ usage.setOrderId(finalOrder.getId());
|
|
|
usage.setUsageStatus(1); // 已下单
|
|
|
usage.setUpdatedTime(new Date());
|
|
|
storeCouponUsageMapper.updateById(usage);
|
|
|
}
|
|
|
+ } else if (isUpdate) {
|
|
|
+ // 如果是更新订单且没有使用优惠券,需要清除之前的优惠券使用记录
|
|
|
+ // 这里暂时不处理,因为可能用户只是想更新订单信息,不想改变优惠券
|
|
|
}
|
|
|
|
|
|
// 创建订单明细
|
|
|
List<StoreOrderDetail> orderDetails = cart.getItems().stream()
|
|
|
.map(item -> {
|
|
|
StoreOrderDetail detail = new StoreOrderDetail();
|
|
|
- detail.setOrderId(order.getId());
|
|
|
- detail.setOrderNo(orderNo);
|
|
|
+ detail.setOrderId(finalOrder.getId());
|
|
|
+ detail.setOrderNo(finalOrderNo);
|
|
|
detail.setCuisineId(item.getCuisineId());
|
|
|
detail.setCuisineName(item.getCuisineName());
|
|
|
detail.setCuisineType(item.getCuisineType());
|
|
|
@@ -205,10 +292,12 @@ public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOr
|
|
|
orderDetailMapper.insert(detail);
|
|
|
}
|
|
|
|
|
|
- // 更新桌号的当前订单ID
|
|
|
- table.setCurrentOrderId(order.getId());
|
|
|
- table.setStatus(1); // 就餐中
|
|
|
- storeTableMapper.updateById(table);
|
|
|
+ // 更新桌号的当前订单ID(如果是新订单才需要更新,更新订单时已经绑定了)
|
|
|
+ if (!isUpdate) {
|
|
|
+ table.setCurrentOrderId(finalOrder.getId());
|
|
|
+ table.setStatus(1); // 就餐中
|
|
|
+ storeTableMapper.updateById(table);
|
|
|
+ }
|
|
|
|
|
|
// 锁定购物车商品数量(禁止减少或删除已下单的商品)
|
|
|
cartService.lockCartItems(dto.getTableId());
|
|
|
@@ -216,8 +305,12 @@ public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOr
|
|
|
// 下单后不清空购物车,允许加餐(加餐时添加到同一订单)
|
|
|
// 只有在支付完成后才清空购物车
|
|
|
|
|
|
- log.info("订单创建成功, orderId={}, orderNo={}", order.getId(), orderNo);
|
|
|
- return order;
|
|
|
+ if (isUpdate) {
|
|
|
+ log.info("订单更新成功, orderId={}, orderNo={}", finalOrder.getId(), finalOrderNo);
|
|
|
+ } else {
|
|
|
+ log.info("订单创建成功, orderId={}, orderNo={}", finalOrder.getId(), finalOrderNo);
|
|
|
+ }
|
|
|
+ return finalOrder;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -638,6 +731,22 @@ public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOr
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 删除订单变更记录(逻辑删除,使用 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);
|
|
|
@@ -744,6 +853,103 @@ public class StoreOrderServiceImpl extends ServiceImpl<StoreOrderMapper, StoreOr
|
|
|
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));
|
|
|
+
|
|
|
+ // 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());
|
|
|
+
|
|
|
+ // 计算批次统计信息
|
|
|
+ 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列表
|
|
|
+ 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());
|
|
|
+ return itemVO;
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+
|
|
|
+ batchVO.setItems(items);
|
|
|
+ batchList.add(batchVO);
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("查询订单变更记录完成, orderId={}, batchCount={}", orderId, batchList.size());
|
|
|
+ return batchList;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取操作类型文本
|
|
|
+ */
|
|
|
+ private String getOperationTypeText(Integer operationType) {
|
|
|
+ if (operationType == null) {
|
|
|
+ return "未知";
|
|
|
+ }
|
|
|
+ switch (operationType) {
|
|
|
+ case 1:
|
|
|
+ return "首次下单";
|
|
|
+ case 2:
|
|
|
+ return "加餐";
|
|
|
+ case 3:
|
|
|
+ return "更新订单";
|
|
|
+ default:
|
|
|
+ return "未知";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 生成订单号
|
|
|
*/
|