zhangchen 1 месяц назад
Родитель
Сommit
2904761b98

+ 25 - 0
alien-store/src/main/java/shop/alien/store/service/ReservationNoticeAsyncService.java

@@ -0,0 +1,25 @@
+package shop.alien.store.service;
+
+/**
+ * 预约相关通知异步发送,避免阻塞主流程(查询+落库均在异步中执行)
+ */
+public interface ReservationNoticeAsyncService {
+
+    /**
+     * 异步根据订单ID查询预约/店铺/桌号并给商家发送订单取消提醒(LifeNotice)
+     *
+     * @param orderId 预订订单ID
+     */
+    void sendCancelReminderToStore(Integer orderId);
+
+    /**
+     * 异步给商家发送订单修改提醒(LifeNotice),需主线程传入原/新时间与桌位文案,异步内仅查店铺手机号并落库
+     *
+     * @param reservationId  预约ID
+     * @param oldDateTime   原预订时间,如 2026-01-01 14:00
+     * @param oldTableNumber 原桌位,如 A01
+     * @param newDateTime   修改后时间
+     * @param newTableNumber 修改后桌位,如 A02
+     */
+    void sendUpdateReminderToStore(Integer reservationId, String oldDateTime, String oldTableNumber, String newDateTime, String newTableNumber);
+}

+ 147 - 0
alien-store/src/main/java/shop/alien/store/service/impl/ReservationNoticeAsyncServiceImpl.java

@@ -0,0 +1,147 @@
+package shop.alien.store.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import shop.alien.entity.store.LifeNotice;
+import shop.alien.entity.store.StoreBookingTable;
+import shop.alien.entity.store.StoreUser;
+import shop.alien.entity.store.UserReservation;
+import shop.alien.entity.store.UserReservationOrder;
+import shop.alien.entity.store.UserReservationTable;
+import shop.alien.mapper.LifeNoticeMapper;
+import shop.alien.mapper.StoreUserMapper;
+import shop.alien.mapper.UserReservationMapper;
+import shop.alien.mapper.UserReservationOrderMapper;
+import shop.alien.mapper.UserReservationTableMapper;
+import shop.alien.store.service.ReservationNoticeAsyncService;
+import shop.alien.store.service.StoreBookingTableService;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 预约相关通知异步发送实现:查询通知参数 + 落库均在异步中执行
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ReservationNoticeAsyncServiceImpl implements ReservationNoticeAsyncService {
+
+    private final LifeNoticeMapper lifeNoticeMapper;
+    private final UserReservationOrderMapper userReservationOrderMapper;
+    private final UserReservationMapper userReservationMapper;
+    private final StoreUserMapper storeUserMapper;
+    private final UserReservationTableMapper userReservationTableMapper;
+    private final StoreBookingTableService storeBookingTableService;
+
+    @Async("taskExecutor")
+    @Override
+    public void sendCancelReminderToStore(Integer orderId) {
+        if (orderId == null) {
+            return;
+        }
+        try {
+            UserReservationOrder order = userReservationOrderMapper.selectById(orderId);
+            if (order == null || order.getReservationId() == null) {
+                return;
+            }
+            Integer reservationId = order.getReservationId();
+            UserReservation reservation = userReservationMapper.selectById(reservationId);
+            if (reservation == null) {
+                return;
+            }
+            StoreUser storeUser = storeUserMapper.selectOne(new LambdaQueryWrapper<StoreUser>()
+                    .eq(StoreUser::getStoreId, reservation.getStoreId())
+                    .eq(StoreUser::getDeleteFlag, 0)
+                    .last("LIMIT 1"));
+            if (storeUser == null || storeUser.getPhone() == null || storeUser.getPhone().trim().isEmpty()) {
+                return;
+            }
+            String storePhone = storeUser.getPhone().trim();
+            String dateTime = reservation.getStartTime() != null && !reservation.getStartTime().trim().isEmpty()
+                    ? reservation.getStartTime().trim() : "未知时间";
+            List<UserReservationTable> reservationTables = userReservationTableMapper.selectList(
+                    new LambdaQueryWrapper<UserReservationTable>()
+                            .eq(UserReservationTable::getReservationId, reservationId)
+                            .eq(UserReservationTable::getDeleteFlag, 0)
+                            .orderByAsc(UserReservationTable::getSort));
+            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);
+                }
+            }
+            String message = "用户取消了" + dateTime + "预订的" + tableNumber + "的桌位";
+
+            JSONObject contextJson = new JSONObject();
+            contextJson.put("message", message);
+            contextJson.put("reservationId", reservationId);
+            LifeNotice lifeNotice = new LifeNotice();
+            lifeNotice.setSenderId("system");
+            lifeNotice.setReceiverId("store_" + storePhone);
+            lifeNotice.setBusinessId(reservationId);
+            lifeNotice.setTitle("订单取消提醒");
+            lifeNotice.setContext(contextJson.toJSONString());
+            lifeNotice.setNoticeType(2);
+            lifeNotice.setIsRead(0);
+            lifeNoticeMapper.insert(lifeNotice);
+            log.info("用户取消预订已通知商家,reservationId={}, receiverId=store_{}", reservationId, storePhone);
+        } catch (Exception e) {
+            log.error("发送订单取消提醒通知异常,orderId={}", orderId, e);
+        }
+    }
+
+    @Async("taskExecutor")
+    @Override
+    public void sendUpdateReminderToStore(Integer reservationId, String oldDateTime, String oldTableNumber, String newDateTime, String newTableNumber) {
+        if (reservationId == null) {
+            return;
+        }
+        try {
+            UserReservation reservation = userReservationMapper.selectById(reservationId);
+            if (reservation == null) {
+                return;
+            }
+            StoreUser storeUser = storeUserMapper.selectOne(new LambdaQueryWrapper<StoreUser>()
+                    .eq(StoreUser::getStoreId, reservation.getStoreId())
+                    .eq(StoreUser::getDeleteFlag, 0)
+                    .last("LIMIT 1"));
+            if (storeUser == null || storeUser.getPhone() == null || storeUser.getPhone().trim().isEmpty()) {
+                return;
+            }
+            String storePhone = storeUser.getPhone().trim();
+            String oTime = oldDateTime != null && !oldDateTime.trim().isEmpty() ? oldDateTime.trim() : "未知时间";
+            String oTable = oldTableNumber != null && !oldTableNumber.trim().isEmpty() ? oldTableNumber.trim() : "未知桌号";
+            String nTime = newDateTime != null && !newDateTime.trim().isEmpty() ? newDateTime.trim() : "未知时间";
+            String nTable = newTableNumber != null && !newTableNumber.trim().isEmpty() ? newTableNumber.trim() : "未知桌号";
+            String message = "用户修改了预订信息,原预订信息为:" + oTime + "," + oTable + "桌位,修改后为:" + nTime + "," + nTable + "桌位";
+
+            JSONObject contextJson = new JSONObject();
+            contextJson.put("message", message);
+            contextJson.put("reservationId", reservationId);
+            LifeNotice lifeNotice = new LifeNotice();
+            lifeNotice.setSenderId("system");
+            lifeNotice.setReceiverId("store_" + storePhone);
+            lifeNotice.setBusinessId(reservationId);
+            lifeNotice.setTitle("订单修改提醒");
+            lifeNotice.setContext(contextJson.toJSONString());
+            lifeNotice.setNoticeType(2);
+            lifeNotice.setIsRead(0);
+            lifeNoticeMapper.insert(lifeNotice);
+            log.info("用户修改预订已通知商家,reservationId={}, receiverId=store_{}", reservationId, storePhone);
+        } catch (Exception e) {
+            log.error("发送订单修改提醒通知异常,reservationId={}", reservationId, e);
+        }
+    }
+}

+ 42 - 7
alien-store/src/main/java/shop/alien/store/service/impl/UserReservationServiceImpl.java

@@ -1,7 +1,6 @@
 package shop.alien.store.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -71,6 +70,7 @@ public class UserReservationServiceImpl extends ServiceImpl<UserReservationMappe
 
     private final BaseRedisService baseRedisService;
     private final AliSms aliSms;
+    private final ReservationNoticeAsyncService reservationNoticeAsyncService;
 
     private ReservationOrderPageService reservationOrderPageService;
 
@@ -201,6 +201,11 @@ public class UserReservationServiceImpl extends ServiceImpl<UserReservationMappe
             throw new RuntimeException("预约不存在");
         }
 
+        // 修改前先取原时间与桌位(用于商家订单修改提醒)
+        String oldDateTime = existing.getStartTime() != null && !existing.getStartTime().trim().isEmpty()
+                ? existing.getStartTime().trim() : "未知时间";
+        String oldTableNumber = tableIdsToTableNumberString(listTableIdsByReservationId(existing.getId()));
+
         UserReservation entity = new UserReservation();
         BeanUtils.copyProperties(dto, entity, "tableIds", "reservationNo");
         entity.setId(existing.getId());
@@ -209,6 +214,12 @@ public class UserReservationServiceImpl extends ServiceImpl<UserReservationMappe
 
         saveReservationTables(existing.getId(), dto.getTableIds());
 
+        // 修改后文案,并异步给商家发送订单修改提醒
+        String newDateTime = dto.getStartTime() != null && !dto.getStartTime().trim().isEmpty()
+                ? dto.getStartTime().trim() : "未知时间";
+        String newTableNumber = tableIdsToTableNumberString(dto.getTableIds());
+        reservationNoticeAsyncService.sendUpdateReminderToStore(existing.getId(), oldDateTime, oldTableNumber, newDateTime, newTableNumber);
+
         // 与 add 一致:同步 user_reservation_order(无则创建,有则仅待支付时按门店配置刷新)
         UserReservation updatedReservation = this.getById(existing.getId());
         LambdaQueryWrapper<UserReservationOrder> orderWrapper = new LambdaQueryWrapper<>();
@@ -237,28 +248,37 @@ public class UserReservationServiceImpl extends ServiceImpl<UserReservationMappe
 
     @Override
     public boolean removeReservation(Integer id) {
-
-        UserReservationOrder one = userReservationOrderService.getOne(new LambdaUpdateWrapper<UserReservationOrder>()
+        UserReservationOrder one = userReservationOrderService.getOne(new LambdaQueryWrapper<UserReservationOrder>()
                 .eq(UserReservationOrder::getId, id));
         if (one == null) {
             throw new RuntimeException("预约不存在");
         }
+        Integer reservationId = one.getReservationId();
 
         // 当订单为未支付时,订单状态变为已关闭
         int orderStatus = 4;
-        if(one.getPaymentStatus()!= null && one.getPaymentStatus() == 0){
+        if (one.getPaymentStatus() != null && one.getPaymentStatus() == 0) {
             orderStatus = 5;
         }
 
-        // 订单状态置为已取消(4)
+        // 订单状态置为已取消(4)或已关闭(5)
         userReservationOrderService.update(
                 new LambdaUpdateWrapper<UserReservationOrder>()
                         .eq(UserReservationOrder::getId, one.getId())
                         .set(UserReservationOrder::getOrderStatus, orderStatus));
         // 预约不再逻辑删除,仅将 status 置为已取消(3)
-        return this.update(new LambdaUpdateWrapper<UserReservation>()
-                .eq(UserReservation::getId, one.getReservationId())
+        boolean updated = reservationId != null && this.update(new LambdaUpdateWrapper<UserReservation>()
+                .eq(UserReservation::getId, reservationId)
                 .set(UserReservation::getStatus, STATUS_CANCELLED));
+        if (reservationId == null) {
+            updated = true;
+        }
+
+        // 异步查询通知参数并给商家发送订单取消提醒(查询+落库均在异步中)
+        if (updated) {
+            reservationNoticeAsyncService.sendCancelReminderToStore(id);
+        }
+        return updated;
     }
 
     @Override
@@ -802,6 +822,21 @@ public class UserReservationServiceImpl extends ServiceImpl<UserReservationMappe
         return list.stream().map(UserReservationTable::getTableId).collect(Collectors.toList());
     }
 
+    /** 将桌位ID列表转为桌号文案,如 "A01,A02",空或查不到为 "未知桌号" */
+    private String tableIdsToTableNumberString(List<Integer> tableIds) {
+        if (tableIds == null || tableIds.isEmpty()) {
+            return "未知桌号";
+        }
+        List<String> numbers = tableIds.stream()
+                .map(tableId -> {
+                    StoreBookingTable t = storeBookingTableService.getById(tableId);
+                    return t != null && t.getTableNumber() != null ? t.getTableNumber().trim() : null;
+                })
+                .filter(n -> n != null && !n.isEmpty())
+                .collect(Collectors.toList());
+        return numbers.isEmpty() ? "未知桌号" : String.join(",", numbers);
+    }
+
     /**
      * 保存预约与桌号关联:先物理删除该预约下原有关联再插入
      */