diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/IdUtil.java b/mall-common/src/main/java/com/suisung/mall/common/utils/IdUtil.java index 60adc578..d9da2e16 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/IdUtil.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/IdUtil.java @@ -21,8 +21,8 @@ public class IdUtil { private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS; private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS; private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS); - private long workerId; - private long dataCenterId; + private final long workerId; + private final long dataCenterId; private long sequence = 0L; private long lastTimestamp = -1L; @@ -116,4 +116,5 @@ public class IdUtil { return timestamp; } + } diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/StringUtils.java b/mall-common/src/main/java/com/suisung/mall/common/utils/StringUtils.java index d3d2ef85..e2d10b10 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/StringUtils.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/StringUtils.java @@ -750,6 +750,24 @@ public final class StringUtils extends org.apache.commons.lang3.StringUtils { } } + /** + * 取货单号格式化 + * + * @param pickNum 一般是 Long 类型 + * @return + */ + public static String fmtPickNum(Long pickNum) { + if (pickNum == null || pickNum <= 0) { + return ""; + } + + if (pickNum > 9999999) { + return String.valueOf(pickNum); + } + + return String.format("%07d", pickNum); + } + /** * 生成的随机数类型 */ diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/UpdateOrderStatusJob.java b/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/UpdateOrderStatusJob.java index 9cf3c70c..6174b61e 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/UpdateOrderStatusJob.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/UpdateOrderStatusJob.java @@ -6,6 +6,7 @@ import com.suisung.mall.common.utils.LogUtil; import com.suisung.mall.shop.config.SpringUtil; import com.suisung.mall.shop.distribution.service.ShopDistributionUserOrderService; import com.suisung.mall.shop.order.service.ShopOrderBaseService; +import com.suisung.mall.shop.order.service.ShopOrderBookingService; import com.suisung.mall.shop.order.service.ShopOrderInfoService; import com.suisung.mall.shop.sixun.utils.CommonUtil; import org.quartz.JobExecutionContext; @@ -31,6 +32,7 @@ public class UpdateOrderStatusJob extends QuartzJobBean { ShopOrderBaseService shopOrderBaseService = SpringUtil.getBean(ShopOrderBaseService.class); ShopOrderInfoService shopOrderInfoService = SpringUtil.getBean(ShopOrderInfoService.class); ShopDistributionUserOrderService shopDistributionUserOrderService = SpringUtil.getBean(ShopDistributionUserOrderService.class); + ShopOrderBookingService shopOrderBookingService = SpringUtil.getBean(ShopOrderBookingService.class); // 在UpdateOrderStatusJob.execute方法开始添加日志 logger.info("UpdateOrderStatusJob 方法开始执行"); @@ -56,35 +58,45 @@ public class UpdateOrderStatusJob extends QuartzJobBean { } } } - // 自动确认收货 - //shopOrderBaseService.autoReceive(); + // 自动确认收货 + //shopOrderBaseService.autoReceive(); - // 2. 更新为确认收货 - List order_id_receipt = shopOrderInfoService.getAutoFinishOrderId(); - if (CollUtil.isNotEmpty(order_id_receipt)) { - for (String order_id : order_id_receipt) { - try { - if (!shopOrderBaseService.receive(order_id, null)) { - LogUtil.error(String.format(I18nUtil._("order_id : %s 确认收货出错"), order_id)); - } - } catch (Exception e) { - LogUtil.error(String.format(I18nUtil._("order_id : %s 确认收货出错"), order_id), e); + // 2. 更新为确认收货 + List order_id_receipt = shopOrderInfoService.getAutoFinishOrderId(); + if (CollUtil.isNotEmpty(order_id_receipt)) { + for (String order_id : order_id_receipt) { + try { + if (!shopOrderBaseService.receive(order_id, null)) { + LogUtil.error(String.format(I18nUtil._("order_id : %s 确认收货出错"), order_id)); } + } catch (Exception e) { + LogUtil.error(String.format(I18nUtil._("order_id : %s 确认收货出错"), order_id), e); } } + } - // 3.分销结算 - List order_id_settle_commission = shopOrderBaseService.getWaitingSettleCommissionOrder(null, null); - if (CollUtil.isNotEmpty(order_id_settle_commission)) { - for (String order_id : order_id_settle_commission) { - try { - if (!shopDistributionUserOrderService.settleDistributionUserOrder(order_id)) { - LogUtil.error(String.format(I18nUtil._("order_id : %s 确认佣金结算出错"), order_id)); - } - } catch (Exception e) { - LogUtil.error(String.format(I18nUtil._("order_id : %s 确认佣金结算出错"), order_id), e); - } + // 3.分销结算 + List order_id_settle_commission = shopOrderBaseService.getWaitingSettleCommissionOrder(null, null); + if (CollUtil.isNotEmpty(order_id_settle_commission)) { + for (String order_id : order_id_settle_commission) { + try { + if (!shopDistributionUserOrderService.settleDistributionUserOrder(order_id)) { + LogUtil.error(String.format(I18nUtil._("order_id : %s 确认佣金结算出错"), order_id)); } + } catch (Exception e) { + LogUtil.error(String.format(I18nUtil._("order_id : %s 确认佣金结算出错"), order_id), e); } } + } + + // 4. 预约订单定时任务,同步到 redis + try { + int count = shopOrderBookingService.syncOrderBooking2RedisTask(); + logger.info("同步预约订单任务到 redis 定时任务成功,成功数量:" + count); + + } catch (Exception e) { + LogUtil.error(String.format(I18nUtil._("同步预约订单任务到 redis 定时任务失败")), e); + } + + } } \ No newline at end of file diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/OrderPayedListener.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/OrderPayedListener.java index 149a70a8..9787b2a1 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/OrderPayedListener.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/OrderPayedListener.java @@ -149,15 +149,20 @@ public class OrderPayedListener { if (order_state_id == StateCode.ORDER_STATE_WAIT_PAY || order_state_id == StateCode.ORDER_STATE_WAIT_REVIEW || order_state_id == StateCode.ORDER_STATE_WAIT_FINANCE_REVIEW) { + + // 是预约订单吗? + boolean isBookingOrder = shopOrderInfoService.isBookingOrder(orderInfoOld); + // 已支付的订单,生成取单号,打票机并打印订单 Long orderPickupNum = shopOrderInfoService.isPaidOrderGenPickNumAndPrint(orderInfoOld.getStore_id(), orderId); - // 如果配送方式是 顺丰同城下单 + // 如果配送方式是 顺丰同城, 且立即下单,则触发顺丰同城下单 if (CheckUtil.isNotEmpty(orderInfoOld.getDelivery_type_id()) && orderInfoOld.getDelivery_type_id().equals(StateCode.DELIVERY_TYPE_SAME_CITY) - && CheckUtil.isNotEmpty(orderPickupNum)) { + && CheckUtil.isNotEmpty(orderPickupNum) + && !isBookingOrder) { - // 顺丰同城下单 + // 如果是立即下单,将触发顺丰同城下单,否则(预约下单) redis+cron 定时触发顺丰同城下单 Pair pairCreateSfOrder = sfExpressApiService.innerCreateSfExpressOrder(orderId, orderPickupNum); if (pairCreateSfOrder == null) { logger.error("[订单支付监听] 顺丰同城下单失败,无返回值 订单ID: {}", orderId); @@ -183,24 +188,28 @@ public class OrderPayedListener { } // 同城配送或普通快递,都发送 unipush 推送:您有一个新的订单,请查收! - String orderType = orderInfoOld.getDelivery_type_id() == StateCode.DELIVERY_TYPE_SAME_CITY ? "同城" : ""; + String bookingTip = isBookingOrder ? "预约" : ""; + + String orderType = orderInfoOld.getDelivery_type_id() == StateCode.DELIVERY_TYPE_SAME_CITY ? "同城" + bookingTip : bookingTip; String title = String.format("您有一笔新的%s订单,请注意查收。", orderType); - String content = String.format("这笔新%s订单:%s,用户于%s下的单,请注意查收。", orderType, orderId, DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss")); + String content = String.format("这笔新的%s订单:%s,用户于%s下的单,请注意查收。", orderType, orderId, DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss")); JSONObject payload = new JSONObject(); payload.put("category", CommonConstant.PUSH_MSG_CATE_MCH_ONLINE_ORDER_LIST); payload.put("orderId", orderId); pushMessageService.noticeMerchantEmployeeOrderAction(orderInfoOld.getStore_id(), orderId, title, content, payload); - // 发送延迟消息 25分钟拣货配送时间(提前5分钟,下单20分钟后,提醒商家及时拣货) - Long mchOrderExpireSeconds = shopOrderBaseService.sameCityOrderExpireSeconds(1500L) - 300; - redisService.set(RedisConstant.SF_Order_Proc_WillExpire_Key + String.format("%d&%s", orderInfoOld.getStore_id(), orderId), orderId, mchOrderExpireSeconds); + if (!isBookingOrder) { // 非预约下单的情况 + // 发送延迟消息 25分钟拣货配送时间(提前5分钟,下单20分钟后,提醒商家及时拣货) + Long mchOrderExpireSeconds = shopOrderBaseService.sameCityOrderExpireSeconds(1500L) - 300; + redisService.set(RedisConstant.SF_Order_Proc_WillExpire_Key + String.format("%d&%s", orderInfoOld.getStore_id(), orderId), orderId, mchOrderExpireSeconds); - // 发送延迟消息 25分钟拣货配送时间(下单25分钟后,提醒商家及时拣货) - mchOrderExpireSeconds = shopOrderBaseService.sameCityOrderExpireSeconds(1500L); - redisService.set(RedisConstant.SF_Order_Proc_Expire_Key + String.format("%d&%s", orderInfoOld.getStore_id(), orderId), orderId, mchOrderExpireSeconds); + // 发送延迟消息 25分钟拣货配送时间(下单25分钟后,提醒商家及时拣货) + mchOrderExpireSeconds = shopOrderBaseService.sameCityOrderExpireSeconds(1500L); + redisService.set(RedisConstant.SF_Order_Proc_Expire_Key + String.format("%d&%s", orderInfoOld.getStore_id(), orderId), orderId, mchOrderExpireSeconds); - // 记录推送已发送状态,避免重复推送 - redisService.set(pushFlagKey, "1", 24 * 3600); // 24小时过期 + // 记录推送已发送状态,避免重复推送 + redisService.set(pushFlagKey, "1", 24 * 3600); // 24小时过期 + } } catch (Exception e) { log.error("[订单支付监听] 发送推送消息失败. 订单ID: {}", orderId, e); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/RedisKeyExpiredListener.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/RedisKeyExpiredListener.java index 099633eb..caa742f4 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/RedisKeyExpiredListener.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/listener/RedisKeyExpiredListener.java @@ -15,9 +15,12 @@ import cn.hutool.json.JSONObject; import com.suisung.mall.common.constant.CommonConstant; import com.suisung.mall.common.constant.RedisConstant; import com.suisung.mall.shop.message.service.PushMessageService; +import com.suisung.mall.shop.order.service.ShopOrderInfoService; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; +import org.springframework.data.util.Pair; import javax.annotation.Resource; @@ -27,6 +30,10 @@ public class RedisKeyExpiredListener implements MessageListener { @Resource private PushMessageService pushMessageService; + @Lazy + @Resource + private ShopOrderInfoService shopOrderInfoService; + /** * Callback for processing received objects through Redis. * @@ -83,6 +90,24 @@ public class RedisKeyExpiredListener implements MessageListener { } else { log.error("[Redis过期监听] 订单即将超时事件处理失败. 店铺ID: {}, 订单号: {}", args[0], args[1]); } + } else if (expiredKey.startsWith(RedisConstant.Order_Booking_Task_Key)) { + // 预约订单向顺丰同城下单 + log.debug("[预约订单顺丰同城下单Redis过期监听] 开始处理预约订单顺丰同城下单超时事件. 过期键: {}", expiredKey); + + // orderId + String orderId = expiredKey.replace(RedisConstant.Order_Booking_Task_Key, ""); + if (StrUtil.isBlank(orderId)) { + log.error("[预约订单顺丰同城下单Redis过期监听] 键: {} 不符合预期格式,无法解析订单号", expiredKey); + return; + } + + // 重要:预约订单向顺丰同城下单 + Pair result = shopOrderInfoService.runBookingOrder2CreateSfExpressOrder(orderId); + if (result == null || !result.getFirst()) { + log.error("[预约订单顺丰同城下单Redis过期监听] 顺丰同城下单失败:{}, 订单号: {}", result != null ? result.getSecond() : "", orderId); + } else { + log.info("[预约订单顺丰同城下单Redis过期监听] 顺丰同城下单成功. 订单号: {}", orderId); + } } else { //log.debug("[Redis过期监听] 忽略非订单超时事件. 过期键: {}", expiredKey); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/mapper/ShopOrderBookingMapper.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/mapper/ShopOrderBookingMapper.java index 939bc0fc..f6786bc1 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/mapper/ShopOrderBookingMapper.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/mapper/ShopOrderBookingMapper.java @@ -10,6 +10,17 @@ package com.suisung.mall.shop.order.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.suisung.mall.common.modules.order.ShopOrderBooking; +import org.apache.ibatis.annotations.Param; + +import java.util.List; public interface ShopOrderBookingMapper extends BaseMapper { + + /** + * 查询有效的(未到下单时机,执行时间未过期,订单状态正确)预约订单任务 + * + * @param limit_count + * @return + */ + List selectValidBookingList(@Param("limit_count") Integer limit_count); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBookingService.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBookingService.java index 31c87590..8fa70126 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBookingService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBookingService.java @@ -47,6 +47,21 @@ public interface ShopOrderBookingService extends IBaseService * @param updateWrapper 更新条件包装器 * @return 是否更新成功 */ - boolean update(UpdateWrapper updateWrapper); + Boolean update(UpdateWrapper updateWrapper); + + /** + * 更改预约订单任务下单完成状态(同时删除 redis 的定时任务) + * + * @param orderId 订单ID + * @return 是否完成同城下单 + */ + Boolean updateOrderBookingFinishState(String orderId); + + /** + * 同步预约订单任务到 redis 定时任务 + * + * @return 同步数量 + */ + Integer syncOrderBooking2RedisTask(); } \ No newline at end of file diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderInfoService.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderInfoService.java index 8f79f149..c4e39042 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderInfoService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderInfoService.java @@ -116,4 +116,21 @@ public interface ShopOrderInfoService extends IBaseService { * @return */ Pair checkBookingOrderArgs(Integer storeId, Integer bookingState, String bookingBeginTime, String bookingEndTime); + + + /** + * 判断订单是否为预约订单 + * + * @param orderInfo 订单信息 + * @return 是否为预约订单 + */ + Boolean isBookingOrder(ShopOrderInfo orderInfo); + + /** + * 预约订单到点后生成顺丰订单 + * + * @param orderId 订单ID + * @return + */ + Pair runBookingOrder2CreateSfExpressOrder(String orderId); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java index d82deb67..7b46bced 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java @@ -99,7 +99,6 @@ import com.suisung.mall.shop.sfexpress.service.SFExpressApiService; import com.suisung.mall.shop.store.service.*; import com.suisung.mall.shop.user.service.*; import com.suisung.mall.shop.wechat.service.WxOrderShippingService; -import io.seata.common.util.StringUtils; import io.seata.core.context.RootContext; import io.seata.core.exception.TransactionException; import io.seata.spring.annotation.GlobalTransactional; @@ -8700,8 +8699,12 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); queryWrapper.eq("order_id", shopOrderId); List shopOrderItemList = shopOrderItemService.list(queryWrapper); @@ -8774,6 +8787,11 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl 9999999) { - return String.valueOf(pickNum); - } - - return String.format("%07d", pickNum); - } /** * 计算平台与代理商的总分账金额 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBookingServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBookingServiceImpl.java index 65c6bef4..67a7ff14 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBookingServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBookingServiceImpl.java @@ -8,6 +8,8 @@ package com.suisung.mall.shop.order.service.impl; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; @@ -22,9 +24,12 @@ import com.suisung.mall.shop.order.mapper.ShopOrderBookingMapper; import com.suisung.mall.shop.order.service.ShopOrderBookingService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; +import javax.annotation.Resource; +import java.util.List; import java.util.concurrent.TimeUnit; @Slf4j @@ -33,6 +38,10 @@ public class ShopOrderBookingServiceImpl extends BaseServiceImpl 0) { - redisService.set(redisKey, String.valueOf(runAt), runAt); + redisService.set(redisKey, Convert.toStr(shopOrderBooking.getRun_at()), runAt); log.debug("Redis键设置成功: key={}, bookingAt={}, runAt={}", redisKey, bookingAt, runAt); } else { log.warn("过期时间无效,未设置Redis键: key={}, bookingAt={}, runAt={}", redisKey, bookingAt, runAt); @@ -152,12 +161,13 @@ public class ShopOrderBookingServiceImpl extends BaseServiceImpl page = new Page<>(pageNum, pageSize); QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("status", CommonConstant.Enable); - queryWrapper.orderByAsc("booking_at"); + queryWrapper.gt("run_at", System.currentTimeMillis() / 1000); + queryWrapper.orderByAsc("run_at"); return this.page(page, queryWrapper); } @Override - public boolean update(UpdateWrapper updateWrapper) { + public Boolean update(UpdateWrapper updateWrapper) { log.debug("更新预约订单任务"); if (updateWrapper == null) { log.warn("更新条件不能为空"); @@ -172,4 +182,77 @@ public class ShopOrderBookingServiceImpl extends BaseServiceImpl updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("order_id", orderId); + updateWrapper.set("status", CommonConstant.Disable2); + + // 执行更新操作 + boolean result = this.update(updateWrapper); + + if (result) { + log.debug("成功完成预约订单任务的同城配送下单: orderId={}", orderId); + + // 同时删除对应的Redis键 + String redisKey = RedisConstant.Order_Booking_Task_Key + orderId; + redisService.del(redisKey); + log.debug("已清理对应的Redis定时任务键: {}", redisKey); + } else { + log.warn("未找到对应的预约订单任务,无法完成同城配送下单: orderId={}", orderId); + } + + return result; + } catch (Exception e) { + log.error("完成预约订单任务的同城配送下单时发生异常: orderId={}", orderId, e); + return false; + } + } + + /** + * 同步预约订单任务到 redis 定时任务 + * + * @return 同步数量 + */ + @Override + public Integer syncOrderBooking2RedisTask() { + List list = shopOrderBookingMapper.selectValidBookingList(200); + if (CollUtil.isEmpty(list)) { + log.debug("未找到有效的预约订单任务"); + return 0; + } + + int count = 0; + for (ShopOrderBooking shopOrderBooking : list) { + String redisKey = RedisConstant.Order_Booking_Task_Key + shopOrderBooking.getOrder_id(); + if (redisService.get(redisKey) != null) { + log.debug("已存在对应的Redis定时任务键: {},不需要同步", redisKey); + continue; + } + + redisService.set(redisKey, Convert.toStr(shopOrderBooking.getRun_at()), shopOrderBooking.getRun_at()); + + count++; + } + + return count; + } + + } \ No newline at end of file diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java index ff6f2335..64e415ec 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java @@ -35,6 +35,7 @@ import com.suisung.mall.shop.base.service.AccountBaseConfigService; import com.suisung.mall.shop.base.service.ShopBaseStateCodeService; import com.suisung.mall.shop.distribution.service.ShopDistributionUserCommissionService; import com.suisung.mall.shop.distribution.service.ShopDistributionUserWithdrawService; +import com.suisung.mall.shop.message.service.PushMessageService; import com.suisung.mall.shop.order.mapper.ShopOrderInfoMapper; import com.suisung.mall.shop.order.service.*; import com.suisung.mall.shop.plantform.service.ShopPlantformActivityItemService; @@ -57,6 +58,7 @@ import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import java.math.BigDecimal; +import java.time.LocalDateTime; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ThreadPoolExecutor; @@ -120,10 +122,18 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl 0) { - // 已打印过,无需打印 - logger.info("####以前生成的订单{}的取单号####", orderInfoOld.getOrder_pickup_num()); - return orderInfoOld.getOrder_pickup_num(); - } + logger.info("####开始处理订单{}的取单号生成和打印####", orderId); - ShopOrderInfo orderInfo = new ShopOrderInfo(); - orderInfo.setOrder_id(orderId); - // 生成取单号,写入order_info - Long orderPickupNum = genTodayPickupNum(storeId); - orderInfo.setOrder_pickup_num(orderPickupNum); - orderInfo.setOrder_express_print(CommonConstant.Enable); // 打印标记 1-已打印 - if (!edit(orderInfo)) { + // 参数校验 + if (storeId == null || orderId == null) { + logger.warn("参数校验失败: storeId或orderId为空"); return 0L; } - // 订单状态处理成功之后,打印小票 - shopStorePrinterService.printShopStoreOrder(storeId, orderId); - logger.info("####新生成订单{}的取单号####", orderPickupNum); - return orderPickupNum; + ShopOrderInfo orderInfoOld = get(orderId); + if (orderInfoOld == null) { + logger.warn("未找到订单信息: orderId={}", orderId); + return 0L; + } + + // 检查是否已经生成过取单号并打印 + if (orderInfoOld.getOrder_express_print() != null + && orderInfoOld.getOrder_express_print().equals(CommonConstant.Enable) + && orderInfoOld.getOrder_pickup_num() != null + && orderInfoOld.getOrder_pickup_num() > 0) { + logger.info("订单{}已处理过,取单号: {}", orderId, orderInfoOld.getOrder_pickup_num()); + return orderInfoOld.getOrder_pickup_num(); + } + + try { + // 生成取单号 + Long orderPickupNum = genTodayPickupNum(storeId); + if (orderPickupNum == null || orderPickupNum <= 0) { + logger.error("生成取单号失败: storeId={}", storeId); + return 0L; + } + + // 更新订单信息 + ShopOrderInfo orderInfo = new ShopOrderInfo(); + orderInfo.setOrder_id(orderId); + orderInfo.setOrder_pickup_num(orderPickupNum); + orderInfo.setOrder_express_print(CommonConstant.Enable); // 打印标记 1-已打印 + + if (!edit(orderInfo)) { + logger.error("更新订单取单号失败: orderId={}", orderId); + return 0L; + } + + // 打印小票 + shopStorePrinterService.printShopStoreOrder(storeId, orderId); + logger.info("####成功生成并打印订单{}的取单号: {}####", orderId, orderPickupNum); + return orderPickupNum; + + } catch (Exception e) { + logger.error("处理订单取单号时发生异常: orderId={}", orderId, e); + return 0L; + } } /** @@ -872,14 +909,99 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl= System.currentTimeMillis() / 1000; + } + + /** + * 执行预约订单到点后向顺丰同城订单任务 + * + * @param orderId 订单ID + * @return 处理结果Pair,第一个元素表示是否成功,第二个元素为结果描述 + */ + @Transactional + @Override + public Pair runBookingOrder2CreateSfExpressOrder(String orderId) { + // 参数校验 + if (StrUtil.isBlank(orderId)) { + logger.warn("[预约订单顺丰下单] 订单ID为空"); + return Pair.of(false, "订单ID不能为空"); + } + + try { + // 获取订单信息 + ShopOrderInfo shopOrderInfo = get(orderId); + if (shopOrderInfo == null) { + logger.warn("[预约订单顺丰下单] 订单不存在: {}", orderId); + return Pair.of(false, "订单不存在"); + } + + Integer orderState = shopOrderInfo.getOrder_state_id(); + + // 验证订单是否为有效的预约订单 + if (!isBookingOrder(shopOrderInfo) || CheckUtil.isEmpty(orderState) + || orderState.intValue() >= StateCode.ORDER_STATE_SHIPPED) { + logger.warn("[预约订单顺丰下单] 订单不是预约订单或者状态不正确: orderId: {}, orderState: {}", orderId, orderState); + return Pair.of(false, "订单不是预约订单或者状态不正确"); + } + + // 调用顺丰接口创建订单 + Pair sfResult = sfExpressApiService.innerCreateSfExpressOrder(orderId, 0L); + if (sfResult == null) { + logger.error("[预约订单顺丰下单] 调用顺丰接口无返回值, orderId={}", orderId); + throw new RuntimeException("预约订单顺丰下单失败,接口无响应"); + } + + if (!sfResult.getFirst()) { + logger.error("[预约订单顺丰下单] 调用顺丰接口失败: {}, orderId={}", sfResult.getSecond(), orderId); + throw new RuntimeException("预约订单顺丰下单失败," + sfResult.getSecond()); + } + + // 更新订单预约状态为立即下单状态 + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("order_id", orderId) + .set("booking_state", CommonConstant.Order_Booking_State_LJ); + this.update(updateWrapper); + + // 标记预约任务完成 + shopOrderBookingService.updateOrderBookingFinishState(orderId); + + // 发送推送消息给商家 + String title = "您有一笔新的订单,请注意查收。"; + String content = String.format("这笔新的订单:%s,用户于%s下的单,请注意查收。", + orderId, DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss")); + JSONObject payload = new JSONObject(); + payload.set("category", CommonConstant.PUSH_MSG_CATE_MCH_ONLINE_ORDER_LIST); + payload.set("orderId", orderId); + pushMessageService.noticeMerchantEmployeeOrderAction(null, orderId, title, content, payload); + + logger.info("[预约订单顺丰下单] 成功, orderId={}", orderId); + return Pair.of(true, "预约订单在顺丰下单成功"); + + } catch (Exception e) { + logger.error("[预约订单顺丰下单] 异常, orderId={}", orderId, e); + throw new RuntimeException("系统异常,预约订单顺丰下单失败", e); + } + } + } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sfexpress/service/impl/SFExpressApiServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sfexpress/service/impl/SFExpressApiServiceImpl.java index db6a17e7..ea46e35d 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sfexpress/service/impl/SFExpressApiServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sfexpress/service/impl/SFExpressApiServiceImpl.java @@ -568,7 +568,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { @Override public Pair innerCreateSfExpressOrder(String shopOrderId, Long orderPickupNum) { logger.info("开始顺丰同城下单"); - if (StrUtil.isBlank(shopOrderId) || orderPickupNum == null || orderPickupNum <= 0) { + if (StrUtil.isBlank(shopOrderId) || orderPickupNum == null) { return Pair.of(false, "顺丰同城下单时,缺少必要参数!"); } @@ -1108,7 +1108,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { String order_id = shopStoreSfOrder.getShop_order_id(); itemQueryWrapper.eq("order_id", order_id); List order_item_rows = shopOrderItemService.find(itemQueryWrapper); - if(picking(order_item_rows)){ + if (picking(order_item_rows)) { logger.info("顺丰发货商品扣减库存成功"); } if (!order_item_rows.isEmpty()) { @@ -1266,7 +1266,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { } Pair pair = wxOrderShippingService.uploadShippingInfoToWx(2, shopOrderBase.getOrder_id()); if (pair.getFirst()) { - Integer orderStatus = StateCode.ORDER_STATE_SHIPPED;//已发货 + Integer orderStatus = StateCode.ORDER_STATE_SHIPPED;//已发货 Integer orderIsOutStatus = StateCode.ORDER_PICKING_STATE_YES; // 已出库 shopOrderInfoService.changeOrderStatus(orderId, orderStatus, orderIsOutStatus, 0); //完成订单后修改订单异常操作流程进入完成订单环节 @@ -1277,7 +1277,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { QueryWrapper itemQueryWrapper = new QueryWrapper<>(); itemQueryWrapper.eq("order_id", orderId); List order_item_rows = shopOrderItemService.find(itemQueryWrapper); - if(picking(order_item_rows)){ + if (picking(order_item_rows)) { logger.info("自行发货商品扣减库存成功"); } if (!order_item_rows.isEmpty()) { @@ -1292,7 +1292,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { return CommonResult.success("操作成功"); //出库扣减思迅库存end } - return CommonResult.failed("操作失败:"+pair.getSecond()); + return CommonResult.failed("操作失败:" + pair.getSecond()); } /** @@ -1309,18 +1309,20 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { // 送达后,发出催促微信用户确认收货通知 (同城配送不能调用微信的确认收货) shopOrderInfoService.changeOrderStatus(shopOrderId, orderStatus, 0, 0); //完成订单后修改订单异常操作流程 - ShopOrderBase updateShopOrderBase=new ShopOrderBase(); + ShopOrderBase updateShopOrderBase = new ShopOrderBase(); updateShopOrderBase.setOrder_id(shopOrderId); updateShopOrderBase.setOperate_flag("2"); shopOrderBaseService.updateById(updateShopOrderBase); return CommonResult.success("操作成功"); } + /** * 根据订单列表扣除商品库存 + * * @param order_item_rows * @return */ - public boolean picking(List order_item_rows){ + public boolean picking(List order_item_rows) { logger.info("出库商品扣除库存--开始"); List updateProductItems = new ArrayList<>(); for (ShopOrderItem shopOrderItem : order_item_rows) { @@ -1329,7 +1331,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { if (productItem == null) { throw new ApiException(String.format(_("SKU【%s】不存在!"), item_id)); } - Integer item_change_quantity=shopOrderItem.getOrder_item_quantity()*-1; + Integer item_change_quantity = shopOrderItem.getOrder_item_quantity() * -1; Integer quantity = productItem.getItem_quantity() + item_change_quantity; if (quantity.compareTo(0) < 0) { quantity = 0; @@ -1343,7 +1345,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { productItem.setItem_quantity_frozen(tmp_quantity); updateProductItems.add(productItem); } - return shopProductItemService.updateBatchById(updateProductItems,updateProductItems.size()); + return shopProductItemService.updateBatchById(updateProductItems, updateProductItems.size()); } // 私有方法 diff --git a/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml b/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml index 4c7e43f6..19e12d17 100644 --- a/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml +++ b/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml @@ -535,6 +535,7 @@ a.order_id,a.store_id,a.store_name,a.buyer_user_id, a.buyer_user_name,a.order_time,a.order_payment_amount,a.order_product_amount, b.order_title, b.delivery_type_id, b.payment_type_id, b.payment_time, b.order_pickup_num, + CASE WHEN b.booking_state = 2 THEN 2 ELSE 1 END AS booking_state, b.booking_begin_time, c.order_message, c.order_item_amount, c.order_shipping_fee, c.order_shipping_fee_amount, c.delivery_time, (c.order_discount_amount + c.voucher_price + c.order_points_fee + c.order_adjust_fee) as total_discount_amount, c.packing_fee, diff --git a/mall-shop/src/main/resources/mapper/order/ShopOrderBookingMapper.xml b/mall-shop/src/main/resources/mapper/order/ShopOrderBookingMapper.xml index 74d140ce..86b216db 100644 --- a/mall-shop/src/main/resources/mapper/order/ShopOrderBookingMapper.xml +++ b/mall-shop/src/main/resources/mapper/order/ShopOrderBookingMapper.xml @@ -5,4 +5,20 @@ * + + diff --git a/mall-shop/src/main/resources/templates/order_printer.txt b/mall-shop/src/main/resources/templates/order_printer.txt index 1e248a80..9014e58f 100644 --- a/mall-shop/src/main/resources/templates/order_printer.txt +++ b/mall-shop/src/main/resources/templates/order_printer.txt @@ -3,6 +3,7 @@ 示例格式化后的: 小发同城
+预约订单
--------------------------------
#00019232
买家备注:不用敲门,放在门口旁边的外卖箱,打个电话告知送达就行,谢谢!!!
@@ -57,4 +58,7 @@ ${store_name}
--------------------------------
#${order_pickup_num_str}
买家备注:${order_message!'-'}
配送时间:${payment_time?string('MM-dd HH:mm')}~${delivery_time?string('HH:mm')}
--------------------------------
订单编号:${order_id}
订单来源:微信小程序
支付方式:微信支付
配送来源:顺丰同城
付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}
--------------------------------
商品名称 数量 金额
--------------------------------
<#list order_items as item>${item.s_name}${item.s_quantity}${item.s_amount}
<#if item.s_name_segs??><#list item.s_name_segs as seg>${seg}
<#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}
--------------------------------
商品总件数:${order_items_count!0}
商品总额:¥${order_product_amount?string('0.00')}
押金:¥${(yajin!0)?string('0.00')}
运费:¥${order_shipping_fee?string('0.00')}
会员权益:-¥${(quanyi!0)?string('0.00')}
秒杀:-¥${(miaosha!0)?string('0.00')}
实付金额:¥${order_payment_amount?string('0.00')}
<#if seller_message?default("")?trim?length gt 1>--------------------------------
商家备注:${seller_message!'-'}
--------------------------------
收货人:${buyer_user_name!''}
收货人手机:${da_mobile!'-'}
收货地址:${da_province!'-'}${da_city!'-'}${da_address!'-'}
--------------------------------
门店:${store_name}
门店电话:${store_tel!'-'}
收银员:${cashier!'店长'}
第二版带变量的模版: -${store_name}
--------------------------------
#${order_pickup_num_str}
买家备注:${order_message!'-'}
配送时间:${payment_time?string('MM-dd HH:mm')}~${delivery_time?string('HH:mm')}
--------------------------------
订单编号:${order_id}
订单来源:微信小程序
支付方式:微信支付
配送来源:顺丰同城
付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}
--------------------------------
商品名称 数量 金额
--------------------------------
<#list order_items as item>${item.s_name}${item.s_quantity}${item.s_amount}
<#if item.s_name_segs??><#list item.s_name_segs as seg>${seg}
<#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}
--------------------------------
商品总件数:${order_items_count!0}
商品总额:¥${order_product_amount?string('0.00')}
运费:¥${order_shipping_fee?string('0.00')}
<#if packing_fee?? && (packing_fee > 0)>打包费:¥${packing_fee?string('0.00')}
优惠金额:-¥${(quanyi!0)?string('0.00')}
实付金额:¥${order_payment_amount?string('0.00')}
<#if seller_message?default("")?trim?length gt 1>--------------------------------
商家备注:${seller_message!'-'}
--------------------------------
收货人:${buyer_user_name!''}
收货人手机:${da_mobile!'-'}
收货地址:${da_province!'-'}${da_city!'-'}${da_address!'-'}
--------------------------------
门店:${store_name}
门店电话:${store_tel!'-'}
收银员:${cashier!'店长'}
\ No newline at end of file +${store_name}
--------------------------------
#${order_pickup_num_str}
买家备注:${order_message!'-'}
配送时间:${payment_time?string('MM-dd HH:mm')}~${delivery_time?string('HH:mm')}
--------------------------------
订单编号:${order_id}
订单来源:微信小程序
支付方式:微信支付
配送来源:顺丰同城
付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}
--------------------------------
商品名称 数量 金额
--------------------------------
<#list order_items as item>${item.s_name}${item.s_quantity}${item.s_amount}
<#if item.s_name_segs??><#list item.s_name_segs as seg>${seg}
<#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}
--------------------------------
商品总件数:${order_items_count!0}
商品总额:¥${order_product_amount?string('0.00')}
运费:¥${order_shipping_fee?string('0.00')}
<#if packing_fee?? && (packing_fee > 0)>打包费:¥${packing_fee?string('0.00')}
优惠金额:-¥${(quanyi!0)?string('0.00')}
实付金额:¥${order_payment_amount?string('0.00')}
<#if seller_message?default("")?trim?length gt 1>--------------------------------
商家备注:${seller_message!'-'}
--------------------------------
收货人:${buyer_user_name!''}
收货人手机:${da_mobile!'-'}
收货地址:${da_province!'-'}${da_city!'-'}${da_address!'-'}
--------------------------------
门店:${store_name}
门店电话:${store_tel!'-'}
收银员:${cashier!'店长'}
+ +第三版带变量的模版(有预约订单) +${store_name}
<#if is_booking_order>预约订单
--------------------------------
#${order_pickup_num_str}
买家备注:${order_message!'-'}
配送时间:${payment_time?string('MM-dd HH:mm')}~${delivery_time?string('HH:mm')}
--------------------------------
订单编号:${order_id}
订单来源:微信小程序
支付方式:微信支付
配送来源:顺丰同城
付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}
--------------------------------
商品名称 数量 金额
--------------------------------
<#list order_items as item>${item.s_name}${item.s_quantity}${item.s_amount}
<#if item.s_name_segs??><#list item.s_name_segs as seg>${seg}
<#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}
--------------------------------
商品总件数:${order_items_count!0}
商品总额:¥${order_product_amount?string('0.00')}
运费:¥${order_shipping_fee?string('0.00')}
<#if packing_fee?? && (packing_fee > 0)>打包费:¥${packing_fee?string('0.00')}
优惠金额:-¥${(quanyi!0)?string('0.00')}
实付金额:¥${order_payment_amount?string('0.00')}
<#if seller_message?default("")?trim?length gt 1>--------------------------------
商家备注:${seller_message!'-'}
--------------------------------
收货人:${buyer_user_name!''}
收货人手机:${da_mobile!'-'}
收货地址:${da_province!'-'}${da_city!'-'}${da_address!'-'}
--------------------------------
门店:${store_name}
门店电话:${store_tel!'-'}
收银员:${cashier!'店长'}
\ No newline at end of file