|
@@ -26,7 +26,9 @@ import shop.alien.store.vo.ReservationOrderDetailVo;
|
|
|
import shop.alien.store.vo.ReservationOrderPageVo;
|
|
import shop.alien.store.vo.ReservationOrderPageVo;
|
|
|
import shop.alien.mapper.UserReservationMapper;
|
|
import shop.alien.mapper.UserReservationMapper;
|
|
|
import shop.alien.mapper.UserReservationTableMapper;
|
|
import shop.alien.mapper.UserReservationTableMapper;
|
|
|
|
|
+import shop.alien.store.config.BaseRedisService;
|
|
|
import shop.alien.store.service.*;
|
|
import shop.alien.store.service.*;
|
|
|
|
|
+import shop.alien.store.util.ali.AliSms;
|
|
|
import shop.alien.util.common.UniqueRandomNumGenerator;
|
|
import shop.alien.util.common.UniqueRandomNumGenerator;
|
|
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
import java.math.BigDecimal;
|
|
@@ -67,6 +69,9 @@ public class UserReservationServiceImpl extends ServiceImpl<UserReservationMappe
|
|
|
|
|
|
|
|
private final EssentialHolidayComparisonMapper essentialHolidayComparisonMapper;
|
|
private final EssentialHolidayComparisonMapper essentialHolidayComparisonMapper;
|
|
|
|
|
|
|
|
|
|
+ private final BaseRedisService baseRedisService;
|
|
|
|
|
+ private final AliSms aliSms;
|
|
|
|
|
+
|
|
|
private ReservationOrderPageService reservationOrderPageService;
|
|
private ReservationOrderPageService reservationOrderPageService;
|
|
|
|
|
|
|
|
@Autowired
|
|
@Autowired
|
|
@@ -90,6 +95,10 @@ public class UserReservationServiceImpl extends ServiceImpl<UserReservationMappe
|
|
|
private static final int MAX_DAYS_TO_CHECK = 366;
|
|
private static final int MAX_DAYS_TO_CHECK = 366;
|
|
|
/** 全天预订时使用的结束分钟数(24*60,即到次日0点) */
|
|
/** 全天预订时使用的结束分钟数(24*60,即到次日0点) */
|
|
|
private static final int MINUTES_DAY_END = 24 * 60;
|
|
private static final int MINUTES_DAY_END = 24 * 60;
|
|
|
|
|
+ /** 到店提醒 Redis key 前缀,用于防重复发送 */
|
|
|
|
|
+ private static final String REDIS_KEY_ARRIVAL_REMINDER_PREFIX = "reservation:arrival:reminder:order:";
|
|
|
|
|
+ /** 到店提醒已发送标记的 Redis 过期时间(25 分钟) */
|
|
|
|
|
+ private static final long ARRIVAL_REMINDER_SENT_TTL_SECONDS = 30 * 60;
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
public Integer add(UserReservationDTO dto) {
|
|
public Integer add(UserReservationDTO dto) {
|
|
@@ -1115,6 +1124,86 @@ public class UserReservationServiceImpl extends ServiceImpl<UserReservationMappe
|
|
|
return toUpdateReservationIds.size();
|
|
return toUpdateReservationIds.size();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public int sendArrivalReminderSms() {
|
|
|
|
|
+ List<Integer> orderIds = userReservationOrderMapper.listOrderIdsForArrivalReminder();
|
|
|
|
|
+ if (orderIds == null || orderIds.isEmpty()) {
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ int sentCount = 0;
|
|
|
|
|
+ for (Integer orderId : orderIds) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ String redisKey = REDIS_KEY_ARRIVAL_REMINDER_PREFIX + orderId;
|
|
|
|
|
+ if (baseRedisService.hasKey(redisKey)) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (sendArrivalReminderSmsForOrder(orderId)) {
|
|
|
|
|
+ baseRedisService.setString(redisKey, "1", Long.valueOf(ARRIVAL_REMINDER_SENT_TTL_SECONDS));
|
|
|
|
|
+ sentCount++;
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("到店提醒短信处理异常,orderId={}", orderId, e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ log.info("到店提醒定时任务结束,符合条件订单数={}, 发送短信数={}", orderIds.size(), sentCount);
|
|
|
|
|
+ return sentCount;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public boolean sendArrivalReminderSmsForOrder(Integer orderId) {
|
|
|
|
|
+ if (orderId == null) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ UserReservationOrder order = userReservationOrderService.getById(orderId);
|
|
|
|
|
+ if (order == null || order.getReservationId() == null) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ UserReservation reservation = this.getById(order.getReservationId());
|
|
|
|
|
+ if (reservation == null) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (order.getOrderStatus() == null || order.getOrderStatus() != ORDER_STATUS_TO_USE) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ String phone = reservation.getReservationUserPhone();
|
|
|
|
|
+ if (phone == null || phone.trim().isEmpty()) {
|
|
|
|
|
+ log.warn("到店提醒跳过:预约人电话为空,orderId={}", orderId);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ String startTimeStr = reservation.getStartTime() != null ? reservation.getStartTime().trim() : "";
|
|
|
|
|
+ if (startTimeStr.isEmpty()) {
|
|
|
|
|
+ startTimeStr = "未知时间";
|
|
|
|
|
+ }
|
|
|
|
|
+ StoreInfo storeInfo = storeInfoService.getById(reservation.getStoreId());
|
|
|
|
|
+ String storeName = storeInfo != null && storeInfo.getStoreName() != null ? storeInfo.getStoreName() : "未知店铺";
|
|
|
|
|
+
|
|
|
|
|
+ LambdaQueryWrapper<UserReservationTable> tableWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
+ tableWrapper.eq(UserReservationTable::getReservationId, reservation.getId())
|
|
|
|
|
+ .eq(UserReservationTable::getDeleteFlag, 0)
|
|
|
|
|
+ .orderByAsc(UserReservationTable::getSort);
|
|
|
|
|
+ List<UserReservationTable> reservationTables = userReservationTableMapper.selectList(tableWrapper);
|
|
|
|
|
+ String tableNumber = "未知桌号";
|
|
|
|
|
+ if (reservationTables != null && !reservationTables.isEmpty()) {
|
|
|
|
|
+ List<String> tableNumbers = reservationTables.stream()
|
|
|
|
|
+ .map(rt -> {
|
|
|
|
|
+ StoreBookingTable table = storeBookingTableService.getById(rt.getTableId());
|
|
|
|
|
+ return table != null && table.getTableNumber() != null ? table.getTableNumber() : null;
|
|
|
|
|
+ })
|
|
|
|
|
+ .filter(tn -> tn != null && !tn.trim().isEmpty())
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+ if (!tableNumbers.isEmpty()) {
|
|
|
|
|
+ tableNumber = String.join(",", tableNumbers);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Integer smsResult = aliSms.sendArrivalReminderSms(phone, startTimeStr, storeName, tableNumber);
|
|
|
|
|
+ if (smsResult != null && smsResult == 1) {
|
|
|
|
|
+ log.info("到店提醒短信发送成功,orderId={}, orderSn={}, phone={}", orderId, order.getOrderSn(), phone);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/** 将页面 VO 字段复制到详情 VO,供前端订单详情页与 page 接口一致展示 */
|
|
/** 将页面 VO 字段复制到详情 VO,供前端订单详情页与 page 接口一致展示 */
|
|
|
private void copyPageVoToDetailVo(ReservationOrderPageVo page, ReservationOrderDetailVo detail) {
|
|
private void copyPageVoToDetailVo(ReservationOrderPageVo page, ReservationOrderDetailVo detail) {
|
|
|
detail.setOrderId(page.getOrderId());
|
|
detail.setOrderId(page.getOrderId());
|