|
|
@@ -15,9 +15,7 @@ import com.ylx.common.core.domain.R;
|
|
|
import com.ylx.common.exception.ServiceException;
|
|
|
import com.ylx.common.utils.SecurityUtils;
|
|
|
import com.ylx.massage.domain.*;
|
|
|
-import com.ylx.massage.domain.vo.CouponReceiveVo;
|
|
|
-import com.ylx.massage.domain.vo.HomeBlock;
|
|
|
-import com.ylx.massage.domain.vo.OrderVerificationVo;
|
|
|
+import com.ylx.massage.domain.vo.*;
|
|
|
import com.ylx.massage.enums.BillTypeEnum;
|
|
|
import com.ylx.massage.enums.DiscountTypeEnum;
|
|
|
import com.ylx.massage.enums.JsStatusEnum;
|
|
|
@@ -33,7 +31,9 @@ import org.springframework.transaction.annotation.Transactional;
|
|
|
import javax.annotation.Resource;
|
|
|
import java.math.BigDecimal;
|
|
|
import java.math.RoundingMode;
|
|
|
+import java.time.LocalDate;
|
|
|
import java.time.LocalDateTime;
|
|
|
+import java.time.LocalTime;
|
|
|
import java.util.*;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
@@ -872,7 +872,7 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
|
|
|
orderNew.setEndTime(LocalDateTime.now());
|
|
|
updateById(orderNew);
|
|
|
|
|
|
- // 添加订单完成消息通知
|
|
|
+ // 添加订单完成消息通知(用户侧)
|
|
|
orderNotificationService.sendCompletedNotification(orderNew);
|
|
|
return true;
|
|
|
}
|
|
|
@@ -899,6 +899,172 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
|
|
|
consumptionLogService.save(tConsumptionLog);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取技师当天可预约时间
|
|
|
+ *
|
|
|
+ * @param technicianId 技师ID
|
|
|
+ * @param dateStr 查询日期(格式:yyyy-MM-dd),为null则查询当天
|
|
|
+ * @return 技师当天可预约时间VO
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public TechnicianAvailabilityVo getTechnicianAvailability(String technicianId, String dateStr) {
|
|
|
+ // 1. 参数校验
|
|
|
+ if (StringUtils.isBlank(technicianId)) {
|
|
|
+ throw new ServiceException("技师ID不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 查询技师信息
|
|
|
+ TJs js = jsService.getById(technicianId);
|
|
|
+ if (js == null) {
|
|
|
+ throw new ServiceException("技师不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 解析日期,默认为当天
|
|
|
+ LocalDate queryDate;
|
|
|
+ if (StringUtils.isBlank(dateStr)) {
|
|
|
+ queryDate = LocalDate.now();
|
|
|
+ } else {
|
|
|
+ try {
|
|
|
+ queryDate = LocalDate.parse(dateStr);
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new ServiceException("日期格式错误,请使用 yyyy-MM-dd 格式");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 定义当天的所有时间段(以30分钟为间隔,从00:00到23:30)
|
|
|
+ List<TimeSlotVo> timeSlots = new ArrayList<>();
|
|
|
+ for (int hour = 0; hour < 24; hour++) {
|
|
|
+ // 每小时生成两个时间段:xx:00 和 xx:30
|
|
|
+ timeSlots.add(TimeSlotVo.builder()
|
|
|
+ .time(String.format("%02d:00", hour))
|
|
|
+ .available(true)
|
|
|
+ .build());
|
|
|
+ timeSlots.add(TimeSlotVo.builder()
|
|
|
+ .time(String.format("%02d:30", hour))
|
|
|
+ .available(true)
|
|
|
+ .build());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 查询技师当天所有进行中的订单
|
|
|
+ // 开始时间
|
|
|
+ LocalDateTime startOfDay = queryDate.atStartOfDay();
|
|
|
+ // 结束时间
|
|
|
+ LocalDateTime endOfDay = queryDate.plusDays(1).atStartOfDay();
|
|
|
+ log.info("开始时间:{},结束时间:{}", startOfDay, endOfDay);
|
|
|
+
|
|
|
+ LambdaQueryWrapper<TOrder> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.eq(TOrder::getcJsId, technicianId)
|
|
|
+ .in(TOrder::getnStatus, OrderStatusEnum.WAIT_JD.getCode(),
|
|
|
+ OrderStatusEnum.RECEIVED_ORDER.getCode(),
|
|
|
+ OrderStatusEnum.DEPART.getCode(),
|
|
|
+ OrderStatusEnum.ARRIVED.getCode(),
|
|
|
+ OrderStatusEnum.SERVICE.getCode())
|
|
|
+ .ge(TOrder::getDtCreateTime, startOfDay)
|
|
|
+ .lt(TOrder::getDtCreateTime, endOfDay)
|
|
|
+ .eq(TOrder::getIsDelete, 0);
|
|
|
+
|
|
|
+ List<TOrder> orders = this.list(queryWrapper);
|
|
|
+ log.info("技师{},在{}天共有 {} 个进行中的订单", technicianId, queryDate, orders.size());
|
|
|
+
|
|
|
+ // 6. 标记不可预约的时间段
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ for (TOrder order : orders) {
|
|
|
+ // 6.1 计算订单的开始时间和结束时间
|
|
|
+ LocalDateTime orderStart = OrderTimeRangeUtils.estimateStartTime(order);
|
|
|
+ LocalDateTime orderEnd = OrderTimeRangeUtils.estimateEndTime(order);
|
|
|
+
|
|
|
+ if (orderStart == null || orderEnd == null) {
|
|
|
+ log.warn("订单 {} 的时间信息不完整,跳过", order.getOrderNo());
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6.2 限制在查询日期范围内
|
|
|
+ LocalDateTime effectiveStart = orderStart.isBefore(startOfDay) ? startOfDay : orderStart;
|
|
|
+ LocalDateTime effectiveEnd = orderEnd.isAfter(endOfDay) ? endOfDay : orderEnd;
|
|
|
+ // 6.3 标记不可预约的时间段
|
|
|
+ markTimeSlotsUnavailable(timeSlots, effectiveStart, effectiveEnd, order.getOrderNo());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 7. 根据查询日期判断是否可预约
|
|
|
+ LocalDate today = LocalDate.now();
|
|
|
+
|
|
|
+ if (queryDate.isBefore(today)) {
|
|
|
+ // 查询日期是过去的日期,所有时间段都不可预约
|
|
|
+ markAllTimeSlotsUnavailable(timeSlots, "日期已过期");
|
|
|
+ } else if (queryDate.equals(today)) {
|
|
|
+ // 查询日期是今天,标记过去的时间为不可预约
|
|
|
+ markPastTimeSlotsUnavailable(timeSlots, now);
|
|
|
+ }
|
|
|
+ // 查询日期是未来的日期,所有时间段默认可预约,无需处理
|
|
|
+ // 8. 构建返回结果
|
|
|
+ return TechnicianAvailabilityVo.builder()
|
|
|
+ .date(queryDate.toString())
|
|
|
+ .technicianId(technicianId)
|
|
|
+ .technicianName(js.getcName())
|
|
|
+ .timeSlots(timeSlots)
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 标记指定时间范围内的时间段为不可预约
|
|
|
+ *
|
|
|
+ * @param timeSlots 时间段列表
|
|
|
+ * @param start 开始时间
|
|
|
+ * @param end 结束时间
|
|
|
+ * @param orderNo 订单号
|
|
|
+ */
|
|
|
+ private void markTimeSlotsUnavailable(List<TimeSlotVo> timeSlots, LocalDateTime start, LocalDateTime end, String orderNo) {
|
|
|
+ LocalDate date = start.toLocalDate();
|
|
|
+ LocalTime startTime = start.toLocalTime();
|
|
|
+ LocalTime endTime = end.toLocalTime();
|
|
|
+
|
|
|
+ for (TimeSlotVo slot : timeSlots) {
|
|
|
+ LocalTime slotTime = LocalTime.parse(slot.getTime());
|
|
|
+
|
|
|
+ // 判断时间段是否在订单时间范围内
|
|
|
+ boolean isInRange = !slotTime.isBefore(startTime) && slotTime.isBefore(endTime);
|
|
|
+ if (isInRange) {
|
|
|
+ slot.setAvailable(false);
|
|
|
+ slot.setReason("已有订单");
|
|
|
+ slot.setOrderNo(orderNo);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 标记所有时间段为不可预约
|
|
|
+ *
|
|
|
+ * @param timeSlots 时间段列表
|
|
|
+ * @param reason 不可预约原因
|
|
|
+ */
|
|
|
+ private void markAllTimeSlotsUnavailable(List<TimeSlotVo> timeSlots, String reason) {
|
|
|
+ for (TimeSlotVo slot : timeSlots) {
|
|
|
+ slot.setAvailable(false);
|
|
|
+ slot.setReason(reason);
|
|
|
+ slot.setOrderNo(null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 标记过去的时间段为不可预约
|
|
|
+ *
|
|
|
+ * @param timeSlots 时间段列表
|
|
|
+ * @param now 当前时间
|
|
|
+ */
|
|
|
+ private void markPastTimeSlotsUnavailable(List<TimeSlotVo> timeSlots, LocalDateTime now) {
|
|
|
+ LocalTime currentTime = now.toLocalTime();
|
|
|
+ for (TimeSlotVo slot : timeSlots) {
|
|
|
+ LocalTime slotTime = LocalTime.parse(slot.getTime());
|
|
|
+
|
|
|
+ // 如果当前时间已经过了这个时间段,标记为不可预约
|
|
|
+ if (slotTime.isBefore(currentTime)) {
|
|
|
+ slot.setAvailable(false);
|
|
|
+ slot.setReason("已过期");
|
|
|
+ slot.setOrderNo(null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 取消订单
|
|
|
*
|
|
|
@@ -1043,11 +1209,12 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public void takingOrders(TOrder order) {
|
|
|
- if (order == null || StringUtils.isBlank(order.getcId())) {
|
|
|
- throw new IllegalArgumentException("订单对象不能为空");
|
|
|
+ String orderId = order.getcId();
|
|
|
+ if (orderId == null || StringUtils.isBlank(orderId)) {
|
|
|
+ throw new IllegalArgumentException("订单ID不能为空");
|
|
|
}
|
|
|
|
|
|
- TOrder orderNew = this.getById(order.getcId());
|
|
|
+ TOrder orderNew = this.getById(orderId);
|
|
|
|
|
|
// 【新增】订单状态锁校验 - 检查技师是否可以接单
|
|
|
log.info("开始校验技师 {} 是否可以接单,订单号:{}", orderNew.getcJsId(), orderNew.getOrderNo());
|
|
|
@@ -1056,9 +1223,8 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
|
|
|
|
|
|
// 检查订单对应的技师是否存在
|
|
|
// updateJs (orderNew);
|
|
|
- // 更新订单状态
|
|
|
TOrder orderParam = new TOrder();
|
|
|
- orderParam.setcId(order.getcId());
|
|
|
+ orderParam.setcId(orderId);
|
|
|
//设置订单状态:已接单
|
|
|
orderParam.setnStatus(OrderStatusEnum.RECEIVED_ORDER.getCode());
|
|
|
orderParam.setAcceptanceTime(LocalDateTime.now());
|
|
|
@@ -1066,7 +1232,6 @@ public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> impleme
|
|
|
|
|
|
// 已接单消息通知(用户侧)
|
|
|
orderNotificationService.sendReceivedNotification(orderNew);
|
|
|
-
|
|
|
// 已接单消息通知(技师侧)
|
|
|
orderNotificationService.sendTechnicianReceivedNotification(orderNew);
|
|
|
}
|