预订单任务表 定时任务逻辑,redis 逻辑

This commit is contained in:
Jack 2025-10-24 00:22:36 +08:00
parent b21f526797
commit 20fe8535d0
15 changed files with 452 additions and 95 deletions

View File

@ -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;
}
}

View File

@ -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);
}
/**
* 生成的随机数类型
*/

View File

@ -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<String> 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<String> 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<String> 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<String> 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);
}
}
}

View File

@ -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<Boolean, String> 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);

View File

@ -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<Boolean, String> 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);
}

View File

@ -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<ShopOrderBooking> {
/**
* 查询有效的(未到下单时机执行时间未过期订单状态正确)预约订单任务
*
* @param limit_count
* @return
*/
List<ShopOrderBooking> selectValidBookingList(@Param("limit_count") Integer limit_count);
}

View File

@ -47,6 +47,21 @@ public interface ShopOrderBookingService extends IBaseService<ShopOrderBooking>
* @param updateWrapper 更新条件包装器
* @return 是否更新成功
*/
boolean update(UpdateWrapper<ShopOrderBooking> updateWrapper);
Boolean update(UpdateWrapper<ShopOrderBooking> updateWrapper);
/**
* 更改预约订单任务下单完成状态同时删除 redis 的定时任务
*
* @param orderId 订单ID
* @return 是否完成同城下单
*/
Boolean updateOrderBookingFinishState(String orderId);
/**
* 同步预约订单任务到 redis 定时任务
*
* @return 同步数量
*/
Integer syncOrderBooking2RedisTask();
}

View File

@ -116,4 +116,21 @@ public interface ShopOrderInfoService extends IBaseService<ShopOrderInfo> {
* @return
*/
Pair<Boolean, String> checkBookingOrderArgs(Integer storeId, Integer bookingState, String bookingBeginTime, String bookingEndTime);
/**
* 判断订单是否为预约订单
*
* @param orderInfo 订单信息
* @return 是否为预约订单
*/
Boolean isBookingOrder(ShopOrderInfo orderInfo);
/**
* 预约订单到点后生成顺丰订单
*
* @param orderId 订单ID
* @return
*/
Pair<Boolean, String> runBookingOrder2CreateSfExpressOrder(String orderId);
}

View File

@ -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<ShopOrderBaseMappe
orderItems.add(ent.rebuild());
}
// 7位数取单号位数不够向左补0 同城配送的取单号String.format("%07d", m.get("order_pickup_num"))
m.put("order_pickup_num_str", fmtPickNum(Convert.toLong(m.get("order_pickup_num"))));
// 是否预约订单
Integer booking_state = Convert.toInt(m.getOrDefault("booking_state", 1));
boolean isBookingOrder = booking_state.equals(CommonConstant.Order_Booking_State_YY) && m.get("booking_begin_time") != null;
// 7位数取单号位数不够向左补0 同城配送的取单号
m.put("order_pickup_num_str", StringUtils.fmtPickNum(Convert.toLong(m.get("order_pickup_num"))));
m.put("seller_message", "");//卖家留言
m.put("order_items", orderItems);//订单商品列表
m.put("order_items_count", orderItems.size());//商品数量
@ -8716,7 +8719,14 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
// 配送时间= 支付时间+35分钟
Long mchOrderExpireSeconds = shopOrderBaseService.sameCityOrderExpireSeconds(2100L);
m.put("delivery_time", DateUtil.offsetSecond((Date) m.get("payment_time"), mchOrderExpireSeconds.intValue()));
// 配送时间
Date paymentTime = Convert.toDate(m.get("payment_time"));
if (isBookingOrder && paymentTime != null) {
// 预约送达时间
paymentTime = Convert.toDate(m.get("booking_begin_time"));
}
m.put("delivery_time", DateUtil.offsetSecond(paymentTime, mchOrderExpireSeconds.intValue()));
// 配送方式
String deliveryTypeName = StateCode.DELIVERY_TYPE_MAP.getOrDefault(m.getOrDefault("delivery_type_id", StateCode.DELIVERY_TYPE_SAME_CITY), "普通快递");
m.put("delivery_type_name", deliveryTypeName);
@ -8728,6 +8738,8 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
m.put("yajin", 0.00); // 押金
m.put("cashier", m.getOrDefault("store_name", "店长")); // 收银员
m.put("order_source", "微信小程序"); // 订单来源
m.put("is_booking_order", isBookingOrder);
m.put("booking_state", booking_state); //订单配送预约状态1-立即配送2-预约配送
return m;
}
@ -8751,6 +8763,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
ShopOrderBase shopOrderBase = shopOrderBaseService.getById(shopOrderId);
ShopOrderData shopOrderData = shopOrderDataService.get(shopOrderId);
QueryWrapper<ShopOrderItem> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_id", shopOrderId);
List<ShopOrderItem> shopOrderItemList = shopOrderItemService.list(queryWrapper);
@ -8774,6 +8787,11 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
return null;
}
// if (CheckUtil.isEmpty(orderPickupNum) && CheckUtil.isEmpty(shopOrderInfo.getOrder_pickup_num())) {
// logger.error("构建顺丰订单失败无法获取取单号订单ID={}storeId={}", shopOrderId, storeId);
// return null;
// }
// 顺丰同城给的测试店铺id
String shopId = "3243279847393";
//3269768224353 到时启用这个正式店铺 type:便利店
@ -8798,9 +8816,22 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
sfCreateOrderReq.setOrder_source(shopStoreBase.getStore_name()); //订单来源
sfCreateOrderReq.setRemark(shopOrderData.getOrder_message()); // 订单留言
// 7位数取单号位数不够向左补0
String orderPickupNumStr = String.format("%07d", orderPickupNum);
sfCreateOrderReq.setOrder_sequence(orderPickupNumStr); //拣货编号
// 获取并格式化取单号
String orderPickupNumStr = "";
// 如果传入取单号为空从订单信息中获取
if (CheckUtil.isEmpty(orderPickupNum)) {
ShopOrderInfo shopOrderInfo = shopOrderInfoService.get(shopOrderId);
orderPickupNum = shopOrderInfo != null ? shopOrderInfo.getOrder_pickup_num() : 0L;
}
// 格式化取单号并设置到顺丰订单
if (CheckUtil.isNotEmpty(orderPickupNum)) {
orderPickupNumStr = StringUtils.fmtPickNum(orderPickupNum);
sfCreateOrderReq.setOrder_sequence(orderPickupNumStr);
} else {
logger.error("构建顺丰订单失败取单号错误订单ID={}storeId={}", shopOrderId, storeId);
}
sfCreateOrderReq.setVersion(19);
sfCreateOrderReq.setReturn_flag(511);
@ -8977,7 +9008,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
}
// 格式化取货号
order.setOrder_pickup_num_str(fmtPickNum(order.getOrder_pickup_num()));
order.setOrder_pickup_num_str(StringUtils.fmtPickNum(order.getOrder_pickup_num()));
// 订单是否禁止退款
int isOrderDenyReturn = shopOrderReturnService.isOrderDenyReturn(order.getOrder_id()) ? 1 : 2;
@ -9083,7 +9114,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
}
// 格式化取货号
orderDetail.setOrder_pickup_num_str(fmtPickNum(orderDetail.getOrder_pickup_num()));
orderDetail.setOrder_pickup_num_str(StringUtils.fmtPickNum(orderDetail.getOrder_pickup_num()));
// 订单是否禁止退款
int isOrderDenyReturn = shopOrderReturnService.isOrderDenyReturn(orderId) ? 1 : 2;
@ -9311,17 +9342,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
* @param pickNum 一般是 Long 类型
* @return
*/
protected String fmtPickNum(Long pickNum) {
if (pickNum == null || pickNum <= 0) {
return "";
}
if (pickNum > 9999999) {
return String.valueOf(pickNum);
}
return String.format("%07d", pickNum);
}
/**
* 计算平台与代理商的总分账金额

View File

@ -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<ShopOrderBookin
private static final Long MINUTES_BEFORE_BOOKING = 35L;
@Resource
private ShopOrderBookingMapper shopOrderBookingMapper;
@Lazy
@Autowired
private RedisService redisService;
@ -70,7 +79,7 @@ public class ShopOrderBookingServiceImpl extends BaseServiceImpl<ShopOrderBookin
String redisKey = RedisConstant.Order_Booking_Task_Key + orderId;
// 设置过期时间为runAt时间点相对于当前时间的秒数
if (runAt > 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<ShopOrderBookin
Page<ShopOrderBooking> page = new Page<>(pageNum, pageSize);
QueryWrapper<ShopOrderBooking> 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<ShopOrderBooking> updateWrapper) {
public Boolean update(UpdateWrapper<ShopOrderBooking> updateWrapper) {
log.debug("更新预约订单任务");
if (updateWrapper == null) {
log.warn("更新条件不能为空");
@ -172,4 +182,77 @@ public class ShopOrderBookingServiceImpl extends BaseServiceImpl<ShopOrderBookin
}
}
/**
* 更改预约订单任务下单完成状态同时删除 redis 的定时任务
*
* @param orderId 订单ID
* @return 是否完成同城下单
*/
@Override
public Boolean updateOrderBookingFinishState(String orderId) {
log.debug("完成预约订单任务的同城配送下单: orderId={}", orderId);
// 参数校验
if (StrUtil.isBlank(orderId)) {
log.warn("订单ID不能为空");
return false;
}
try {
// 构建更新条件
UpdateWrapper<ShopOrderBooking> 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<ShopOrderBooking> 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;
}
}

View File

@ -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<ShopOrderInfoMappe
@Autowired
private ShopStoreSfOrderService shopStoreSfOrderService;
@Lazy
@Autowired
private ShopOrderBookingService shopOrderBookingService;
@Lazy
@Autowired
private SFExpressApiService sfExpressApiService;
@Lazy
@Autowired
private PushMessageService pushMessageService;
@Autowired
private ThreadPoolExecutor executor;
@ -367,33 +377,60 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
* @param orderId
* @return 取货单号
*/
@Transactional
// @Transactional
@Override
public Long isPaidOrderGenPickNumAndPrint(Integer storeId, String orderId) {
logger.info("####开始生成订单{}的取单号####", orderId);
ShopOrderInfo orderInfoOld = get(orderId);
if (orderInfoOld != null
&& orderInfoOld.getOrder_express_print().equals(CommonConstant.Enable)
&& orderInfoOld.getOrder_pickup_num() != null && orderInfoOld.getOrder_pickup_num() > 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<ShopOrderInfoMappe
return Pair.of(false, "[预约订单校验] 请在店铺营业时间内预约下单");
}
// 7. 预约时间范围检查 - 仅能预约45分钟后的订单
Date fortyFiveMinutesLater = DateUtil.offsetMinute(new Date(), 45);
// 7. 预约时间范围检查 - 仅能预约50分钟后的订单
Date fortyFiveMinutesLater = DateUtil.offsetMinute(new Date(), 50);
if (!bookingBeginTime.after(fortyFiveMinutesLater)) {
return Pair.of(false, "[预约订单校验] 仅能预约45分后的订单");
return Pair.of(false, "[预约订单校验] 请预约50分后的订单");
}
return Pair.of(true, "[预约订单校验] 成功");
}
/**
* 判断订单是否为预约订单
*
* @param orderInfo 订单信息
* @return 是否为预约订单
*/
@Override
public Boolean isBookingOrder(ShopOrderInfo orderInfo) {
return orderInfo != null
&& CommonConstant.Order_Booking_State_YY.equals(orderInfo.getBooking_state())
&& !CheckUtil.isEmpty(orderInfo.getBooking_at())
&& orderInfo.getBooking_at().longValue() >= System.currentTimeMillis() / 1000;
}
/**
* 执行预约订单到点后向顺丰同城订单任务
*
* @param orderId 订单ID
* @return 处理结果Pair第一个元素表示是否成功第二个元素为结果描述
*/
@Transactional
@Override
public Pair<Boolean, String> 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<Boolean, String> 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<ShopOrderInfo> 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);
}
}
}

View File

@ -568,7 +568,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService {
@Override
public Pair<Boolean, String> 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<ShopOrderItem> 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<Boolean, String> 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<ShopOrderItem> itemQueryWrapper = new QueryWrapper<>();
itemQueryWrapper.eq("order_id", orderId);
List<ShopOrderItem> 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<ShopOrderItem> order_item_rows){
public boolean picking(List<ShopOrderItem> order_item_rows) {
logger.info("出库商品扣除库存--开始");
List<ShopProductItem> 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());
}
// 私有方法

View File

@ -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,

View File

@ -5,4 +5,20 @@
<sql id="Base_Column_List">
*
</sql>
<select id="selectValidBookingList" resultType="com.suisung.mall.common.modules.order.ShopOrderBooking">
SELECT a.*
FROM shop_order_booking a
JOIN shop_order_info b ON a.order_id = b.order_id
WHERE a.status = 1
AND a.run_at > UNIX_TIMESTAMP()
AND b.booking_state = 2
AND b.booking_begin_time is not null
AND b.order_state_id &lt; 2040
ORDER BY a.run_at ASC
<if test="limit_count != null and limit_count>0">
LIMIT #{limit_count}
</if>
</select>
</mapper>

View File

@ -3,6 +3,7 @@
示例格式化后的:
<CB>小发同城</CB><BR>
<CB><B>预约订单</B></CB><BR>
--------------------------------<BR>
<CB>#00019232</CB><BR>
<L>买家备注:不用敲门,放在门口旁边的外卖箱,打个电话告知送达就行,谢谢!!!</L><BR>
@ -58,3 +59,6 @@
第二版带变量的模版:
<CB>${store_name}</CB><BR>--------------------------------<BR><CB>#${order_pickup_num_str}</CB><BR><L>买家备注:${order_message!'-'}</L><BR><BOLD>配送时间:${payment_time?string('MM-dd HH:mm')}${delivery_time?string('HH:mm')}</BOLD><BR>--------------------------------<BR>订单编号:${order_id}<BR>订单来源:微信小程序<BR>支付方式:微信支付<BR>配送来源:顺丰同城<BR>付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}<BR>--------------------------------<BR><L>商品名称 数量 金额</L><BR>--------------------------------<BR><#list order_items as item><L>${item.s_name}</L><L><BOLD>${item.s_quantity}</BOLD></L><L>${item.s_amount}</L><BR><#if item.s_name_segs??><#list item.s_name_segs as seg><L>${seg}</L><BR></#list></#if><BOLD><#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}</BOLD><BR></#if></#list>--------------------------------<BR>商品总件数:<BOLD>${order_items_count!0}</BOLD><BR>商品总额:<BOLD>¥${order_product_amount?string('0.00')}</BOLD><BR>运费:<BOLD>¥${order_shipping_fee?string('0.00')}</BOLD><BR><#if packing_fee?? && (packing_fee > 0)>打包费:<BOLD>¥${packing_fee?string('0.00')}</BOLD><BR></#if>优惠金额:<BOLD>-¥${(quanyi!0)?string('0.00')}</BOLD><BR>实付金额:<BOLD>¥${order_payment_amount?string('0.00')}</BOLD><BR><#if seller_message?default("")?trim?length gt 1>--------------------------------<BR><BOLD>商家备注:${seller_message!'-'}</BOLD><BR></#if>--------------------------------<BR><BOLD>收货人:${buyer_user_name!''}</BOLD><BR><BOLD>收货人手机:${da_mobile!'-'}</BOLD><BR><BOLD>收货地址:${da_province!'-'}${da_city!'-'}${da_address!'-'}</BOLD><BR>--------------------------------<BR>门店:${store_name}<BR>门店电话:<BOLD>${store_tel!'-'}</BOLD><BR>收银员:${cashier!'店长'}<BR>
第三版带变量的模版(有预约订单)
<CB>${store_name}</CB><BR><#if is_booking_order><CB><B>预约订单</B></CB><BR></#if>--------------------------------<BR><CB>#${order_pickup_num_str}</CB><BR><L>买家备注:${order_message!'-'}</L><BR><BOLD>配送时间:${payment_time?string('MM-dd HH:mm')}${delivery_time?string('HH:mm')}</BOLD><BR>--------------------------------<BR>订单编号:${order_id}<BR>订单来源:微信小程序<BR>支付方式:微信支付<BR>配送来源:顺丰同城<BR>付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}<BR>--------------------------------<BR><L>商品名称 数量 金额</L><BR>--------------------------------<BR><#list order_items as item><L>${item.s_name}</L><L><BOLD>${item.s_quantity}</BOLD></L><L>${item.s_amount}</L><BR><#if item.s_name_segs??><#list item.s_name_segs as seg><L>${seg}</L><BR></#list></#if><BOLD><#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}</BOLD><BR></#if></#list>--------------------------------<BR>商品总件数:<BOLD>${order_items_count!0}</BOLD><BR>商品总额:<BOLD>¥${order_product_amount?string('0.00')}</BOLD><BR>运费:<BOLD>¥${order_shipping_fee?string('0.00')}</BOLD><BR><#if packing_fee?? && (packing_fee > 0)>打包费:<BOLD>¥${packing_fee?string('0.00')}</BOLD><BR></#if>优惠金额:<BOLD>-¥${(quanyi!0)?string('0.00')}</BOLD><BR>实付金额:<BOLD>¥${order_payment_amount?string('0.00')}</BOLD><BR><#if seller_message?default("")?trim?length gt 1>--------------------------------<BR><BOLD>商家备注:${seller_message!'-'}</BOLD><BR></#if>--------------------------------<BR><BOLD>收货人:${buyer_user_name!''}</BOLD><BR><BOLD>收货人手机:${da_mobile!'-'}</BOLD><BR><BOLD>收货地址:${da_province!'-'}${da_city!'-'}${da_address!'-'}</BOLD><BR>--------------------------------<BR>门店:${store_name}<BR>门店电话:<BOLD>${store_tel!'-'}</BOLD><BR>收银员:${cashier!'店长'}<BR>