qinxuyang 1 месяц назад
Родитель
Сommit
30d8e53e5d

+ 92 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/StoreReservationListVo.java

@@ -0,0 +1,92 @@
+package shop.alien.entity.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 商家端预约信息列表 VO
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@ApiModel(value = "StoreReservationListVo", description = "商家端预约信息列表VO")
+public class StoreReservationListVo {
+
+    @ApiModelProperty(value = "预约ID")
+    private Integer id;
+
+    @ApiModelProperty(value = "预约号(券码)")
+    private String reservationNo;
+
+    @ApiModelProperty(value = "预约日期")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private Date reservationDate;
+
+    @ApiModelProperty(value = "星期(如:周一、周二)")
+    private String weekDay;
+
+    @ApiModelProperty(value = "分类ID")
+    private Integer categoryId;
+
+    @ApiModelProperty(value = "分类名称(如:大厅、包间)")
+    private String categoryName;
+
+    @ApiModelProperty(value = "预约人数")
+    private Integer guestCount;
+
+    @ApiModelProperty(value = "桌号列表(逗号分隔,如:A01,A02)")
+    private String tableNumbers;
+
+    @ApiModelProperty(value = "预约开始时间 HH:mm")
+    private String startTime;
+
+    @ApiModelProperty(value = "预约结束时间 HH:mm")
+    private String endTime;
+
+    @ApiModelProperty(value = "时间段(如:10:00-12:00)")
+    private String timeSlot;
+
+    @ApiModelProperty(value = "用户ID")
+    private Integer userId;
+
+    @ApiModelProperty(value = "客户姓名")
+    private String customerName;
+
+    @ApiModelProperty(value = "联系方式(手机号)")
+    private String contactPhone;
+
+    @ApiModelProperty(value = "备注信息")
+    private String remark;
+
+    @ApiModelProperty(value = "预约状态 0:待确认 1:已确认 2:已到店 3:已取消 4:未到店超时")
+    private Integer status;
+
+    @ApiModelProperty(value = "状态文本(待使用、已完成、退款等)")
+    private String statusText;
+
+    @ApiModelProperty(value = "订单金额(元)")
+    private BigDecimal totalAmount;
+
+    @ApiModelProperty(value = "订金金额(元)")
+    private BigDecimal depositAmount;
+
+    @ApiModelProperty(value = "订单状态 0:待支付 1:待使用 2:已完成 3:已过期 4:已取消 5:已关闭 6:退款中 7:已退款 8:商家预订")
+    private Integer orderStatus;
+
+    @ApiModelProperty(value = "订单状态文本(待支付、待使用、已完成、退款等)")
+    private String orderStatusText;
+
+    @ApiModelProperty(value = "实际到店时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date actualArrivalTime;
+
+    @ApiModelProperty(value = "创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdTime;
+}

+ 23 - 0
alien-entity/src/main/java/shop/alien/mapper/UserReservationMapper.java

@@ -1,7 +1,12 @@
 package shop.alien.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
 import shop.alien.entity.store.UserReservation;
+import shop.alien.entity.store.vo.StoreReservationListVo;
+
+import java.util.Date;
+import java.util.List;
 
 /**
  * 用户预约信息表 Mapper 接口
@@ -9,4 +14,22 @@ import shop.alien.entity.store.UserReservation;
  * @author system
  */
 public interface UserReservationMapper extends BaseMapper<UserReservation> {
+
+    /**
+     * 查询商家端预约信息列表
+     *
+     * @param storeId    门店ID(必填)
+     * @param status     预约状态(可选,0:待确认 1:已确认 2:已到店 3:已取消 4:未到店超时)
+     * @param dateFrom   预约日期起(可选)
+     * @param dateTo     预约日期止(可选)
+     * @param orderStatus 订单状态(可选,0:待支付 1:待使用 2:已完成 3:已过期 4:已取消 5:已关闭 6:退款中 7:已退款 8:商家预订)
+     * @return 预约信息列表
+     */
+    List<StoreReservationListVo> getStoreReservationList(
+            @Param("storeId") Integer storeId,
+            @Param("status") Integer status,
+            @Param("dateFrom") Date dateFrom,
+            @Param("dateTo") Date dateTo,
+            @Param("orderStatus") Integer orderStatus
+    );
 }

+ 109 - 0
alien-entity/src/main/resources/mapper/UserReservationMapper.xml

@@ -22,4 +22,113 @@
         <result column="updated_user_id" property="updatedUserId" />
     </resultMap>
 
+    <!-- 商家端预约信息列表结果映射 -->
+    <resultMap id="StoreReservationListVoMap" type="shop.alien.entity.store.vo.StoreReservationListVo">
+        <id column="id" property="id" />
+        <result column="reservation_no" property="reservationNo" />
+        <result column="reservation_date" property="reservationDate" />
+        <result column="week_day" property="weekDay" />
+        <result column="category_id" property="categoryId" />
+        <result column="category_name" property="categoryName" />
+        <result column="guest_count" property="guestCount" />
+        <result column="table_numbers" property="tableNumbers" />
+        <result column="start_time" property="startTime" />
+        <result column="end_time" property="endTime" />
+        <result column="time_slot" property="timeSlot" />
+        <result column="user_id" property="userId" />
+        <result column="customer_name" property="customerName" />
+        <result column="contact_phone" property="contactPhone" />
+        <result column="remark" property="remark" />
+        <result column="status" property="status" />
+        <result column="status_text" property="statusText" />
+        <result column="total_amount" property="totalAmount" />
+        <result column="deposit_amount" property="depositAmount" />
+        <result column="order_status" property="orderStatus" />
+        <result column="order_status_text" property="orderStatusText" />
+        <result column="actual_arrival_time" property="actualArrivalTime" />
+        <result column="created_time" property="createdTime" />
+    </resultMap>
+
+    <!-- 查询商家端预约信息列表 -->
+    <select id="getStoreReservationList" resultMap="StoreReservationListVoMap">
+        SELECT
+            ur.id,
+            ur.reservation_no,
+            ur.reservation_date,
+            CASE DAYOFWEEK(ur.reservation_date)
+                WHEN 1 THEN '周日'
+                WHEN 2 THEN '周一'
+                WHEN 3 THEN '周二'
+                WHEN 4 THEN '周三'
+                WHEN 5 THEN '周四'
+                WHEN 6 THEN '周五'
+                WHEN 7 THEN '周六'
+            END AS week_day,
+            ur.category_id,
+            sbc.category_name,
+            ur.guest_count,
+            GROUP_CONCAT(sbt.table_number ORDER BY urt.sort ASC SEPARATOR ',') AS table_numbers,
+            ur.start_time,
+            ur.end_time,
+            CONCAT(IFNULL(ur.start_time, ''), '-', IFNULL(ur.end_time, '')) AS time_slot,
+            ur.user_id,
+            IFNULL(lu.real_name, lu.user_name) AS customer_name,
+            lu.user_phone AS contact_phone,
+            ur.remark,
+            ur.status,
+            CASE ur.status
+                WHEN 0 THEN '待确认'
+                WHEN 1 THEN '待使用'
+                WHEN 2 THEN '已完成'
+                WHEN 3 THEN '已取消'
+                WHEN 4 THEN '未到店'
+                ELSE '未知'
+            END AS status_text,
+            uro.refund_amount,
+            uro.deposit_amount,
+            uro.order_status,
+            CASE uro.order_status
+                WHEN 0 THEN '待支付'
+                WHEN 1 THEN '待使用'
+                WHEN 2 THEN '已完成'
+                WHEN 3 THEN '已过期'
+                WHEN 4 THEN '已取消'
+                WHEN 5 THEN '已关闭'
+                WHEN 6 THEN '退款中'
+                WHEN 7 THEN '已退款'
+                WHEN 8 THEN '商家预订'
+                ELSE '未知'
+            END AS order_status_text,
+            ur.actual_arrival_time,
+            ur.created_time
+        FROM
+            user_reservation ur
+        LEFT JOIN store_booking_category sbc ON ur.category_id = sbc.id AND sbc.delete_flag = 0
+        LEFT JOIN user_reservation_table urt ON ur.id = urt.reservation_id AND urt.delete_flag = 0
+        LEFT JOIN store_booking_table sbt ON urt.table_id = sbt.id AND sbt.delete_flag = 0
+        LEFT JOIN life_user lu ON ur.user_id = lu.id AND lu.delete_flag = 0
+        LEFT JOIN user_reservation_order uro ON ur.id = uro.reservation_id AND uro.delete_flag = 0
+        WHERE
+            ur.delete_flag = 0
+            AND ur.store_id = #{storeId}
+        <if test="status != null">
+            AND ur.status = #{status}
+        </if>
+        <if test="dateFrom != null">
+            AND ur.reservation_date &gt;= #{dateFrom}
+        </if>
+        <if test="dateTo != null">
+            AND ur.reservation_date &lt;= #{dateTo}
+        </if>
+        <if test="orderStatus != null">
+            AND uro.order_status = #{orderStatus}
+        </if>
+        GROUP BY
+            ur.id
+        ORDER BY
+            ur.reservation_date DESC,
+            ur.start_time ASC,
+            ur.created_time DESC
+    </select>
+
 </mapper>

+ 36 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreBookingCategoryController.java

@@ -188,4 +188,40 @@ public class StoreBookingCategoryController {
             return R.fail("更新排序失败:" + e.getMessage());
         }
     }
+
+    @ApiOperationSupport(order = 7)
+    @ApiOperation("设置预订服务分类显示状态")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", value = "分类ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "isDisplay", value = "显示状态(0:隐藏, 1:显示)", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/updateDisplayStatus")
+    public R<String> updateDisplayStatus(@RequestParam Integer id, @RequestParam Integer isDisplay) {
+        log.info("StoreBookingCategoryController.updateDisplayStatus?id={}&isDisplay={}", id, isDisplay);
+        
+        if (id == null) {
+            return R.fail("分类ID不能为空");
+        }
+        
+        if (isDisplay == null) {
+            return R.fail("显示状态不能为空");
+        }
+        
+        if (isDisplay != 0 && isDisplay != 1) {
+            return R.fail("显示状态值无效,必须为0(隐藏)或1(显示)");
+        }
+        
+        try {
+            boolean result = storeBookingCategoryService.updateDisplayStatus(id, isDisplay);
+            if (result) {
+                String message = isDisplay == 1 ? "设置显示成功" : "设置隐藏成功";
+                return R.success(message);
+            } else {
+                return R.fail("更新显示状态失败");
+            }
+        } catch (Exception e) {
+            log.error("更新预订服务分类显示状态失败", e);
+            return R.fail("更新显示状态失败:" + e.getMessage());
+        }
+    }
 }

+ 155 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreReservationController.java

@@ -0,0 +1,155 @@
+package shop.alien.store.controller;
+
+import io.swagger.annotations.*;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.entity.store.vo.StoreReservationListVo;
+import shop.alien.store.service.UserReservationService;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 商家端预约信息管理 前端控制器
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+@Api(tags = {"商家端-预约信息管理"})
+@ApiSort(16)
+@CrossOrigin
+@RestController
+@RequestMapping("/store/reservation")
+@RequiredArgsConstructor
+public class StoreReservationController {
+
+    private final UserReservationService userReservationService;
+
+    @ApiOperationSupport(order = 1)
+    @ApiOperation("查询商家端预约信息列表")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "storeId", value = "门店ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "status", value = "预约状态(可选,0:待确认 1:已确认 2:已到店 3:已取消 4:未到店超时)", dataType = "Integer", paramType = "query", required = false),
+            @ApiImplicitParam(name = "dateFrom", value = "预约日期起 yyyy-MM-dd", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "dateTo", value = "预约日期止 yyyy-MM-dd", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "orderStatus", value = "订单状态(可选,0:待支付 1:待使用 2:已完成 3:已过期 4:已取消 5:已关闭 6:退款中 7:已退款 8:商家预订)", dataType = "Integer", paramType = "query", required = false)
+    })
+    @GetMapping("/list")
+    public R<List<StoreReservationListVo>> getReservationList(
+            @RequestParam Integer storeId,
+            @RequestParam(required = false) Integer status,
+            @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date dateFrom,
+            @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date dateTo,
+            @RequestParam(required = false) Integer orderStatus) {
+        log.info("StoreReservationController.getReservationList?storeId={}, status={}, dateFrom={}, dateTo={}, orderStatus={}",
+                storeId, status, dateFrom, dateTo, orderStatus);
+
+        if (storeId == null) {
+            return R.fail("门店ID不能为空");
+        }
+
+        try {
+            List<StoreReservationListVo> list = userReservationService.getStoreReservationList(storeId, status, dateFrom, dateTo, orderStatus);
+            return R.data(list);
+        } catch (Exception e) {
+            log.error("查询商家端预约信息列表失败", e);
+            return R.fail("查询失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperationSupport(order = 2)
+    @ApiOperation("商家端取消预约")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reservationId", value = "预约ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/cancel")
+    public R<String> cancelReservation(@RequestParam Integer reservationId) {
+        log.info("StoreReservationController.cancelReservation?reservationId={}", reservationId);
+
+        if (reservationId == null) {
+            return R.fail("预约ID不能为空");
+        }
+
+        try {
+            boolean result = userReservationService.cancelReservationByStore(reservationId);
+            if (result) {
+                return R.success("取消预约成功");
+            } else {
+                return R.fail("取消预约失败");
+            }
+        } catch (Exception e) {
+            log.error("商家端取消预约失败", e);
+            return R.fail("取消预约失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperationSupport(order = 3)
+    @ApiOperation("商家端删除预订信息")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reservationId", value = "预约ID", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/delete")
+    public R<String> deleteReservation(@RequestParam Integer reservationId) {
+        log.info("StoreReservationController.deleteReservation?reservationId={}", reservationId);
+
+        if (reservationId == null) {
+            return R.fail("预约ID不能为空");
+        }
+
+        try {
+            boolean result = userReservationService.deleteReservationByStore(reservationId);
+            if (result) {
+                return R.success("删除预订信息成功");
+            } else {
+                return R.fail("删除预订信息失败");
+            }
+        } catch (Exception e) {
+            log.error("商家端删除预订信息失败", e);
+            return R.fail("删除预订信息失败:" + e.getMessage());
+        }
+    }
+
+    @ApiOperationSupport(order = 4)
+    @ApiOperation("商家端加时")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "reservationId", value = "预约ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "addTimeStart", value = "加时开始时间(HH:mm格式,如:13:00)", dataType = "String", paramType = "query", required = true),
+            @ApiImplicitParam(name = "addTimeMinutes", value = "加时分钟数(必须大于0)", dataType = "Integer", paramType = "query", required = true)
+    })
+    @PostMapping("/addTime")
+    public R<String> addTime(
+            @RequestParam Integer reservationId,
+            @RequestParam String addTimeStart,
+            @RequestParam Integer addTimeMinutes) {
+        log.info("StoreReservationController.addTime?reservationId={}, addTimeStart={}, addTimeMinutes={}", 
+                reservationId, addTimeStart, addTimeMinutes);
+
+        if (reservationId == null) {
+            return R.fail("预约ID不能为空");
+        }
+
+        if (addTimeStart == null || addTimeStart.trim().isEmpty()) {
+            return R.fail("加时开始时间不能为空");
+        }
+
+        if (addTimeMinutes == null || addTimeMinutes <= 0) {
+            return R.fail("加时分钟数必须大于0");
+        }
+
+        try {
+            boolean result = userReservationService.addTimeByStore(reservationId, addTimeStart, addTimeMinutes);
+            if (result) {
+                return R.success("加时成功");
+            } else {
+                return R.fail("加时失败");
+            }
+        } catch (Exception e) {
+            log.error("商家端加时失败", e);
+            return R.fail("加时失败:" + e.getMessage());
+        }
+    }
+}

+ 9 - 0
alien-store/src/main/java/shop/alien/store/service/StoreBookingCategoryService.java

@@ -53,4 +53,13 @@ public interface StoreBookingCategoryService extends IService<StoreBookingCatego
      * @return boolean
      */
     boolean updateCategorySort(Integer storeId, List<Integer> categoryIds);
+
+    /**
+     * 更新预订服务分类显示状态
+     *
+     * @param id        分类ID
+     * @param isDisplay 显示状态(0:隐藏, 1:显示)
+     * @return boolean
+     */
+    boolean updateDisplayStatus(Integer id, Integer isDisplay);
 }

+ 41 - 0
alien-store/src/main/java/shop/alien/store/service/UserReservationService.java

@@ -85,4 +85,45 @@ public interface UserReservationService extends IService<UserReservation> {
      * @return 含 date(yyyy-MM-dd)与 reservations(该日预约列表)的 Map;未找到则 reservations 为空且 date 为最后检查的日期
      */
     Map<String, Object> findFirstAvailableDayReservations(Integer storeId);
+
+    /**
+     * 查询商家端预约信息列表
+     *
+     * @param storeId    门店ID(必填)
+     * @param status     预约状态(可选,0:待确认 1:已确认 2:已到店 3:已取消 4:未到店超时)
+     * @param dateFrom   预约日期起(可选)
+     * @param dateTo     预约日期止(可选)
+     * @param orderStatus 订单状态(可选,0:待支付 1:待使用 2:已完成 3:已过期 4:已取消 5:已关闭 6:退款中 7:已退款 8:商家预订)
+     * @return 预约信息列表
+     */
+    java.util.List<shop.alien.entity.store.vo.StoreReservationListVo> getStoreReservationList(
+            Integer storeId, Integer status, java.util.Date dateFrom, java.util.Date dateTo, Integer orderStatus);
+
+    /**
+     * 商家端取消预约
+     *
+     * @param reservationId 预约ID
+     * @return 是否成功
+     */
+    boolean cancelReservationByStore(Integer reservationId);
+
+    /**
+     * 商家端删除预订信息
+     * 只有订单状态为已取消(4)、已退款(7)、已完成(2)时才能删除
+     *
+     * @param reservationId 预约ID
+     * @return 是否成功
+     */
+    boolean deleteReservationByStore(Integer reservationId);
+
+    /**
+     * 商家端加时
+     * 更新预订信息的结束时间,新的结束时间 = 加时开始时间 + 加时分钟数
+     *
+     * @param reservationId 预约ID
+     * @param addTimeStart 加时开始时间(HH:mm格式)
+     * @param addTimeMinutes 加时分钟数
+     * @return 是否成功
+     */
+    boolean addTimeByStore(Integer reservationId, String addTimeStart, Integer addTimeMinutes);
 }

+ 35 - 0
alien-store/src/main/java/shop/alien/store/service/impl/StoreBookingCategoryServiceImpl.java

@@ -256,6 +256,41 @@ public class StoreBookingCategoryServiceImpl extends ServiceImpl<StoreBookingCat
         return true;
     }
 
+    @Override
+    public boolean updateDisplayStatus(Integer id, Integer isDisplay) {
+        log.info("StoreBookingCategoryServiceImpl.updateDisplayStatus?id={}&isDisplay={}", id, isDisplay);
+        
+        if (id == null) {
+            log.warn("更新预订服务分类显示状态失败:分类ID不能为空");
+            throw new RuntimeException("分类ID不能为空");
+        }
+        
+        if (isDisplay == null || (isDisplay != 0 && isDisplay != 1)) {
+            log.warn("更新预订服务分类显示状态失败:显示状态值无效,isDisplay={}", isDisplay);
+            throw new RuntimeException("显示状态值无效,必须为0(隐藏)或1(显示)");
+        }
+        
+        StoreBookingCategory category = this.getById(id);
+        if (category == null) {
+            log.warn("更新预订服务分类显示状态失败:分类不存在,id={}", id);
+            throw new RuntimeException("分类不存在");
+        }
+        
+        // 从JWT获取当前登录用户ID
+        Integer userId = getCurrentUserId();
+        
+        // 更新显示状态
+        LambdaUpdateWrapper<StoreBookingCategory> updateWrapper = new LambdaUpdateWrapper<>();
+        updateWrapper.eq(StoreBookingCategory::getId, id)
+                .set(StoreBookingCategory::getIsDisplay, isDisplay);
+        
+        if (userId != null) {
+            updateWrapper.set(StoreBookingCategory::getUpdatedUserId, userId);
+        }
+        
+        return this.update(updateWrapper);
+    }
+
     /**
      * 从JWT获取当前登录用户ID
      *

+ 235 - 0
alien-store/src/main/java/shop/alien/store/service/impl/UserReservationServiceImpl.java

@@ -12,11 +12,13 @@ import org.springframework.transaction.annotation.Transactional;
 import shop.alien.entity.store.*;
 import shop.alien.entity.store.dto.UserReservationDTO;
 import shop.alien.entity.store.vo.StoreMainInfoVo;
+import shop.alien.entity.store.vo.StoreReservationListVo;
 import shop.alien.entity.store.vo.UserReservationVo;
 import shop.alien.mapper.UserReservationMapper;
 import shop.alien.mapper.UserReservationTableMapper;
 import shop.alien.store.service.*;
 
+import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.*;
 import java.util.concurrent.ThreadLocalRandom;
@@ -43,6 +45,8 @@ public class UserReservationServiceImpl extends ServiceImpl<UserReservationMappe
 
     private final StoreInfoService storeInfoService;
 
+    private final UserReservationOrderService userReservationOrderService;
+
     /** 预约状态:待确认 */
     private static final int STATUS_PENDING = 0;
     /** 预约状态:已取消(不参与约满统计与展示) */
@@ -275,6 +279,237 @@ public class UserReservationServiceImpl extends ServiceImpl<UserReservationMappe
         }
     }
 
+    @Override
+    public List<StoreReservationListVo> getStoreReservationList(Integer storeId, Integer status, Date dateFrom, Date dateTo, Integer orderStatus) {
+        log.info("UserReservationServiceImpl.getStoreReservationList?storeId={}, status={}, dateFrom={}, dateTo={}, orderStatus={}",
+                storeId, status, dateFrom, dateTo, orderStatus);
+        
+        if (storeId == null) {
+            throw new RuntimeException("门店ID不能为空");
+        }
+        
+        return baseMapper.getStoreReservationList(storeId, status, dateFrom, dateTo, orderStatus);
+    }
+
+    @Override
+    public boolean cancelReservationByStore(Integer reservationId) {
+        log.info("UserReservationServiceImpl.cancelReservationByStore?reservationId={}", reservationId);
+        
+        if (reservationId == null) {
+            throw new RuntimeException("预约ID不能为空");
+        }
+        
+        // 查询预约信息
+        UserReservation reservation = this.getById(reservationId);
+        if (reservation == null) {
+            throw new RuntimeException("预约不存在");
+        }
+        
+        // 检查预约状态,已取消的不能再次取消
+        if (reservation.getStatus() != null && reservation.getStatus() == STATUS_CANCELLED) {
+            throw new RuntimeException("预约已取消,不能重复取消");
+        }
+        
+        // 查询关联的订单信息
+        LambdaQueryWrapper<UserReservationOrder> orderWrapper = new LambdaQueryWrapper<>();
+        orderWrapper.eq(UserReservationOrder::getReservationId, reservationId);
+        UserReservationOrder order = userReservationOrderService.getOne(orderWrapper);
+        
+        if (order == null) {
+            // 如果没有订单,直接更新预约状态为3(已取消)
+            reservation.setStatus(STATUS_CANCELLED); // STATUS_CANCELLED = 3
+            boolean updateResult = this.updateById(reservation);
+            if (!updateResult) {
+                throw new RuntimeException("更新预约状态失败");
+            }
+            log.info("商家端取消预约成功(无订单),reservationId={}", reservationId);
+            return true;
+        }
+        
+        // 判断订单费用类型:0-免费, 1-收费
+        Integer orderCostType = order.getOrderCostType();
+        if (orderCostType == null) {
+            // 如果订单费用类型为空,默认按免费处理
+            orderCostType = 0;
+        }
+        
+        if (orderCostType == 0) {
+            // 免费订单:更新订单状态为4(已取消),更新预约状态为3(已取消)
+            order.setOrderStatus(4); // 4:已取消
+            boolean orderUpdateResult = userReservationOrderService.updateById(order);
+            if (!orderUpdateResult) {
+                throw new RuntimeException("更新订单状态失败");
+            }
+            
+            reservation.setStatus(STATUS_CANCELLED); // STATUS_CANCELLED = 3:已取消
+            boolean reservationUpdateResult = this.updateById(reservation);
+            if (!reservationUpdateResult) {
+                throw new RuntimeException("更新预约状态失败");
+            }
+            
+            log.info("商家端取消预约成功(免费订单),reservationId={}, orderId={}", reservationId, order.getId());
+            return true;
+        } else if (orderCostType == 1) {
+            // 付费订单:功能预留(暂不更新状态,等待后续实现退款逻辑)
+            throw new RuntimeException("付费订单取消功能暂未实现,请稍后再试");
+        } else {
+            throw new RuntimeException("订单费用类型异常,orderCostType=" + orderCostType);
+        }
+    }
+
+    @Override
+    public boolean deleteReservationByStore(Integer reservationId) {
+        log.info("UserReservationServiceImpl.deleteReservationByStore?reservationId={}", reservationId);
+        
+        if (reservationId == null) {
+            throw new RuntimeException("预约ID不能为空");
+        }
+        
+        // 查询预约信息
+        UserReservation reservation = this.getById(reservationId);
+        if (reservation == null) {
+            throw new RuntimeException("预约不存在");
+        }
+        
+        // 查询关联的订单信息
+        LambdaQueryWrapper<UserReservationOrder> orderWrapper = new LambdaQueryWrapper<>();
+        orderWrapper.eq(UserReservationOrder::getReservationId, reservationId);
+        UserReservationOrder order = userReservationOrderService.getOne(orderWrapper);
+        
+        if (order == null) {
+            // 如果没有订单,直接删除预约记录
+            boolean deleteResult = this.removeById(reservationId);
+            if (!deleteResult) {
+                throw new RuntimeException("删除预约记录失败");
+            }
+            log.info("商家端删除预订信息成功(无订单),reservationId={}", reservationId);
+            return true;
+        }
+        
+        // 判断订单状态:只有已取消(4)、已退款(7)、已完成(2)状态才能删除
+        Integer orderStatus = order.getOrderStatus();
+        if (orderStatus == null) {
+            throw new RuntimeException("订单状态异常,无法删除");
+        }
+        
+        // 定义可删除的订单状态:2:已完成, 4:已取消, 7:已退款
+        boolean canDelete = orderStatus == 2 || orderStatus == 4 || orderStatus == 7;
+        
+        if (!canDelete) {
+            String statusText = getOrderStatusText(orderStatus);
+            throw new RuntimeException("订单状态为" + statusText + ",不允许删除。只有已取消、已退款、已完成状态的订单可以删除");
+        }
+        
+        // 删除订单记录(逻辑删除)
+        boolean orderDeleteResult = userReservationOrderService.removeById(order.getId());
+        if (!orderDeleteResult) {
+            throw new RuntimeException("删除订单记录失败");
+        }
+        
+        // 删除预约记录(逻辑删除)
+        boolean reservationDeleteResult = this.removeById(reservationId);
+        if (!reservationDeleteResult) {
+            throw new RuntimeException("删除预约记录失败");
+        }
+        
+        log.info("商家端删除预订信息成功,reservationId={}, orderId={}, orderStatus={}", 
+                reservationId, order.getId(), orderStatus);
+        return true;
+    }
+
+    /**
+     * 获取订单状态文本
+     */
+    private String getOrderStatusText(Integer orderStatus) {
+        if (orderStatus == null) {
+            return "未知";
+        }
+        switch (orderStatus) {
+            case 0: return "待支付";
+            case 1: return "待使用";
+            case 2: return "已完成";
+            case 3: return "已过期";
+            case 4: return "已取消";
+            case 5: return "已关闭";
+            case 6: return "退款中";
+            case 7: return "已退款";
+            case 8: return "商家预订";
+            default: return "未知";
+        }
+    }
+
+    @Override
+    public boolean addTimeByStore(Integer reservationId, String addTimeStart, Integer addTimeMinutes) {
+        log.info("UserReservationServiceImpl.addTimeByStore?reservationId={}, addTimeStart={}, addTimeMinutes={}", 
+                reservationId, addTimeStart, addTimeMinutes);
+        
+        if (reservationId == null) {
+            throw new RuntimeException("预约ID不能为空");
+        }
+        
+        if (addTimeStart == null || addTimeStart.trim().isEmpty()) {
+            throw new RuntimeException("加时开始时间不能为空");
+        }
+        
+        if (addTimeMinutes == null || addTimeMinutes <= 0) {
+            throw new RuntimeException("加时分钟数必须大于0");
+        }
+        
+        // 验证时间格式 HH:mm
+        if (!addTimeStart.matches("^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$")) {
+            throw new RuntimeException("加时开始时间格式错误,应为HH:mm格式");
+        }
+        
+        // 查询预约信息
+        UserReservation reservation = this.getById(reservationId);
+        if (reservation == null) {
+            throw new RuntimeException("预约不存在");
+        }
+        
+        // 保存原结束时间用于日志
+        String oldEndTime = reservation.getEndTime();
+        
+        // 计算新的结束时间:加时开始时间 + 加时分钟数
+        String newEndTime = calculateNewEndTime(addTimeStart, addTimeMinutes);
+        
+        // 更新预约结束时间
+        reservation.setEndTime(newEndTime);
+        boolean updateResult = this.updateById(reservation);
+        if (!updateResult) {
+            throw new RuntimeException("更新预约结束时间失败");
+        }
+        
+        log.info("商家端加时成功,reservationId={}, 原结束时间={}, 加时开始时间={}, 加时分钟数={}, 新结束时间={}", 
+                reservationId, oldEndTime, addTimeStart, addTimeMinutes, newEndTime);
+        return true;
+    }
+
+    /**
+     * 计算新的结束时间:加时开始时间 + 加时分钟数
+     *
+     * @param addTimeStart 加时开始时间(HH:mm格式)
+     * @param addTimeMinutes 加时分钟数
+     * @return 新的结束时间(HH:mm格式)
+     */
+    private String calculateNewEndTime(String addTimeStart, Integer addTimeMinutes) {
+        try {
+            // 解析加时开始时间
+            SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
+            Date startDate = sdf.parse(addTimeStart);
+            
+            // 加上加时分钟数
+            Calendar calendar = Calendar.getInstance();
+            calendar.setTime(startDate);
+            calendar.add(Calendar.MINUTE, addTimeMinutes);
+            
+            // 格式化为HH:mm
+            return sdf.format(calendar.getTime());
+        } catch (ParseException e) {
+            log.error("计算新的结束时间失败,addTimeStart={}, addTimeMinutes={}", addTimeStart, addTimeMinutes, e);
+            throw new RuntimeException("时间计算失败:" + e.getMessage());
+        }
+    }
+
     private static String generateReservationNo() {
         return "RV" + System.currentTimeMillis() + ThreadLocalRandom.current().nextInt(1000, 9999);
     }