Эх сурвалжийг харах

订单模块代码优化,抽离围栏信息

wangzhijun 1 сар өмнө
parent
commit
ad2792e2c7

+ 5 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/TGeoFenceService.java

@@ -3,6 +3,7 @@ package com.ylx.massage.service;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.ylx.massage.domain.TGeoFence;
 import com.ylx.massage.domain.TGeoFence;
+import com.ylx.order.service.IGeoRiskInfo;
 
 
 import java.util.List;
 import java.util.List;
 
 
@@ -53,4 +54,8 @@ public interface TGeoFenceService extends IService<TGeoFence> {
     Boolean deleteGeoFence(TGeoFence geoFence);
     Boolean deleteGeoFence(TGeoFence geoFence);
 
 
     List<TGeoFence> selectValidFences(String cityCode);
     List<TGeoFence> selectValidFences(String cityCode);
+
+    List<TGeoFence> filterUsableFence(List<TGeoFence> allFence);
+
+    void calcOrderGeoFenceInfo(IGeoRiskInfo info, List<TGeoFence> usableFence);
 }
 }

+ 64 - 0
nightFragrance-massage/src/main/java/com/ylx/massage/service/impl/TGeoFenceServiceImpl.java

@@ -1,18 +1,24 @@
 package com.ylx.massage.service.impl;
 package com.ylx.massage.service.impl;
 
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ylx.common.exception.ServiceException;
 import com.ylx.common.exception.ServiceException;
 import com.ylx.common.utils.DateUtils;
 import com.ylx.common.utils.DateUtils;
+import com.ylx.common.utils.DistanceUtil;
 import com.ylx.massage.domain.TGeoFence;
 import com.ylx.massage.domain.TGeoFence;
 import com.ylx.massage.mapper.TGeoFenceMapper;
 import com.ylx.massage.mapper.TGeoFenceMapper;
 import com.ylx.massage.service.TGeoFenceService;
 import com.ylx.massage.service.TGeoFenceService;
+import com.ylx.order.service.IGeoRiskInfo;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 
 
 import java.math.BigDecimal;
 import java.math.BigDecimal;
+import java.util.Collections;
 import java.util.List;
 import java.util.List;
+import java.util.stream.Collectors;
 
 
 /**
 /**
  * 地理围栏服务实现类
  * 地理围栏服务实现类
@@ -115,6 +121,64 @@ public class TGeoFenceServiceImpl extends ServiceImpl<TGeoFenceMapper, TGeoFence
         return this.list(queryWrapper);
         return this.list(queryWrapper);
     }
     }
 
 
+    /**
+     * 过滤可用围栏:剔除坐标/半径为空、半径<=0的无效围栏,减少内层循环次数
+     */
+    @Override
+    public List<TGeoFence> filterUsableFence(List<TGeoFence> allFence) {
+        if (CollUtil.isEmpty(allFence)) {
+            return Collections.emptyList();
+        }
+        return allFence.stream()
+                .filter(fence -> !ObjectUtil.hasNull(fence.getLatitude(), fence.getLongitude(), fence.getRadiusKm()))
+                .filter(fence -> fence.getRadiusKm().compareTo(BigDecimal.ZERO) > 0)
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 计算单条订单围栏距离、是否高风险区域、围栏备注
+     */
+    @Override
+    public void calcOrderGeoFenceInfo(IGeoRiskInfo info, List<TGeoFence> usableFence) {
+        // 用户坐标为空直接标记非风险
+        BigDecimal userLat = info.getUserLatitude();
+        BigDecimal userLon = info.getUserLongitude();
+        if (ObjectUtil.hasNull(userLat, userLon) || CollUtil.isEmpty(usableFence)) {
+            info.setIsHighRiskArea(false);
+            info.setDistanceKm(null);
+            info.setFenceIntro(null);
+            return;
+        }
+
+        BigDecimal minDistanceKm = null;
+        TGeoFence nearestFence = null;
+        // 遍历可用围栏,计算最小距离围栏
+        for (TGeoFence fence : usableFence) {
+            BigDecimal distKm = DistanceUtil.formatDistanceInKilometers(userLat, userLon, fence.getLatitude(), fence.getLongitude());
+            if (ObjectUtil.isNull(distKm)) {
+                continue;
+            }
+            // 更新最近围栏
+            if (ObjectUtil.isNull(minDistanceKm) || distKm.compareTo(minDistanceKm) < 0) {
+                minDistanceKm = distKm;
+                nearestFence = fence;
+            }
+        }
+
+        // 赋值围栏相关信息
+        if (ObjectUtil.isNull(minDistanceKm) || ObjectUtil.isNull(nearestFence)) {
+            info.setIsHighRiskArea(false);
+            info.setDistanceKm(null);
+            info.setFenceIntro(null);
+            return;
+        }
+        info.setDistanceKm(minDistanceKm);
+        // 判断是否在围栏半径内
+        boolean isHighRisk = minDistanceKm.compareTo(nearestFence.getRadiusKm()) <= 0;
+        info.setIsHighRiskArea(isHighRisk);
+        info.setFenceIntro(nearestFence.getFenceIntro());
+    }
+
     private void validateUpdateParam(TGeoFence geoFence) {
     private void validateUpdateParam(TGeoFence geoFence) {
         if (geoFence == null) {
         if (geoFence == null) {
             throw new ServiceException("参数不能为空");
             throw new ServiceException("参数不能为空");

+ 394 - 452
nightFragrance-massage/src/main/java/com/ylx/order/service/impl/TOrderServiceImpl.java

@@ -43,7 +43,6 @@ import com.ylx.order.domain.vo.merchant.OrderPageVO;
 import com.ylx.order.enums.*;
 import com.ylx.order.enums.*;
 import com.ylx.order.mapper.TOrderMapper;
 import com.ylx.order.mapper.TOrderMapper;
 import com.ylx.order.service.IAfterSalesServiceService;
 import com.ylx.order.service.IAfterSalesServiceService;
-import com.ylx.order.service.IGeoRiskInfo;
 import com.ylx.order.service.OrderStatusFlowService;
 import com.ylx.order.service.OrderStatusFlowService;
 import com.ylx.order.service.TOrderService;
 import com.ylx.order.service.TOrderService;
 import com.ylx.project.domain.Project;
 import com.ylx.project.domain.Project;
@@ -333,154 +332,6 @@ public class TOrderServiceImpl extends BaseServiceImpl<TOrderMapper, TOrder> imp
         log.info("订单状态变更: orderId={}, oldStatus={}, newStatus={}", orderId, oldStatus, newStatus);
         log.info("订单状态变更: orderId={}, oldStatus={}, newStatus={}", orderId, oldStatus, newStatus);
     }
     }
 
 
-
-    /**
-     * 将 TOrder 转换为 OrderDateQueryVo,并处理 serviceTime 字段
-     */
-    private OrderDateQueryVo convertToVo(TOrder order) {
-        OrderDateQueryVo vo = new OrderDateQueryVo();
-        // 拷贝相同字段
-        BeanUtil.copyProperties(order, vo);
-        // 手动设置字段名不一致或需要特殊处理的
-        vo.setOrderStatus(order.getStatus());
-        vo.setOrderStatusName(getOrderStatusName(order.getStatus()));
-        // 服务时间范围:预约开始时间 + 项目时长(分钟)
-        // 使用 startTime 和 completedTime 构建服务时间展示
-        String serviceTime = buildServiceTime(order.getStartTime(), order.getCompletedTime());
-        vo.setServiceTime(serviceTime);
-
-        // 获取订单售后数据
-        this.afterSalesServiceService.fillCurrentAfterSaleInfo(vo, order.getId());
-
-        return vo;
-    }
-
-    /**
-     * 订单状态码 -> 中文描述
-     */
-    private String getOrderStatusName(Integer status) {
-        return OrderStatusEnum.getInfoByCode(status);
-    }
-
-    /**
-     * 拼接服务时间字符串,例如 “2026-06-08 14:30 (90分钟)”
-     */
-    private String buildServiceTime(LocalDateTime startTime, LocalDateTime completedTime) {
-        if (startTime == null || completedTime == null) {
-            return "";
-        }
-        String date = startTime.format(dateFormatter);
-        String start = startTime.format(timeFormatter);
-        String end = completedTime.format(timeFormatter);
-        return date + " " + start + "-" + end;
-    }
-
-    /**
-     * 组装订单对象
-     */
-    private TOrder buildOrder(OrderSubmitDTO dto, Project project, MaTechnician maTechnician, TAddress address, List<TAddress> merchantAddressList, BigDecimal basePrice, BigDecimal trafficFee, BigDecimal couponDiscount, BigDecimal finalAmount, Long userId) {
-        TOrder order = new TOrder();
-        order.setOrderNo(orderNumberGenerator.generateNextOrderNumber(""));
-        order.setUserId(userId);
-        order.setProjectId(dto.getProjectId());
-        order.setProjectType(project.getType());
-        order.setProjectName(project.getTitle());
-        order.setProjectCover(project.getCover());
-        order.setHighlight(project.getHighlight());
-        order.setAppointmentStartTime(dto.getAppointmentStartTime());
-
-        // 根据项目的unitType字段将小时转换为分钟
-        Integer projectDuration = this.convertToMinutes(project.getStandardDuration(), project.getUnitType());
-        order.setProjectDuration(projectDuration);
-        order.setMerchantId(dto.getMerchantId());
-        order.setMerchantType(maTechnician.getTechType());
-        order.setMerchantNickName(maTechnician.getTeNickName());
-        order.setMerchantAvatar(maTechnician.getTeAvatar());
-        order.setContactPersonName(address.getUserName());
-        order.setContactPhoneNumber(address.getPhone());
-        order.setContactAddressInfo(address.getDetailAddress());
-        order.setCityCode(address.getCityCode());
-        order.setBasePrice(basePrice);
-        order.setCouponDiscount(couponDiscount);
-        order.setTrafficFee(trafficFee);
-        order.setFinalAmount(finalAmount);
-        order.setCouponId(dto.getCouponId());
-        order.setCreateTime(DateUtils.getNowDate());
-        order.setStatus(OrderStatusEnum.PENDING_PAYMENT.getCode());
-        order.setExecStatus(ExecStatusEnum.WAIT_DEPART.getCode());
-        order.setDispatchedStatus(DispatchedStatusEnum.UN_DISPATCHED.getCode());
-        order.setPaymentMethod(dto.getPaymentMethod());
-
-        // 经纬度安全赋值
-        order.setUserLatitude(address.getLatitude());
-        order.setUserLongitude(address.getLongitude());
-
-        merchantAddressList.stream().filter(a -> a.getType().equals(ADDR_TYPE_REAL)).findFirst().ifPresent(addr -> {
-            order.setMerchantLongitude(addr.getLongitude());
-            order.setMerchantLatitude(addr.getLatitude());
-        });
-        merchantAddressList.stream().filter(a -> a.getType().equals(ADDR_TYPE_VIRTUAL)).findFirst().ifPresent(addr -> {
-            order.setVirtualLongitude(addr.getLongitude());
-            order.setVirtualLatitude(addr.getLatitude());
-        });
-
-        // 设置支付状态
-        if (ObjectUtil.equals(PaymentMethodEnum.BALANCE.getCode(), dto.getPaymentMethod())) {
-            order.setStatus(OrderStatusEnum.PENDING_DISPATCH.getCode());
-            order.setPaidTime(LocalDateTime.now());
-        } else {
-            order.setStatus(OrderStatusEnum.PENDING_PAYMENT.getCode());
-        }
-        return order;
-    }
-
-    /**
-     * 处理余额支付 (核心优化:数据库原子操作防并发)
-     */
-    private void handleBalancePayment(Long userId, BigDecimal finalAmount, TOrder order) {
-        // 1. 数据库层面原子扣减余额,并校验余额是否充足
-        boolean deductSuccess = wxUserService.lambdaUpdate()
-                .setSql("d_balance = d_balance - {0}")
-                .eq(TWxUser::getId, userId)
-                // 两个位置共用同一个参数,逗号传参
-                .apply("d_balance >= {0}", finalAmount)
-                .update();
-
-        if (!deductSuccess) {
-            throw new ServiceException("余额不足,扣款失败");
-        }
-
-        // 2. 重新查询最新余额用于记录流水
-        TWxUser user = this.wxUserService.getById(userId);
-
-        // 3. 记录购物金明细
-        ShoppingFundsDetailAddDto detailDto = new ShoppingFundsDetailAddDto();
-        detailDto.setUserId(userId.toString());
-        detailDto.setAmount(finalAmount);
-        detailDto.setOrderNo(order.getOrderNo());
-        detailDto.setExpenseType(ShoppingFundsExpenseTypeEnum.CONSUMPTION.getCode());
-        detailDto.setBalance(user.getdBalance());
-        this.shoppingFundsDetailService.addShoppingFundsDetail(detailDto);
-
-        // 4. 记录状态流转流水
-        OrderUpdateStatusDTO dto = new OrderUpdateStatusDTO();
-        dto.setOrderId(order.getId());
-        dto.setStatus(order.getStatus());
-        this.updateOrderStatus(dto);
-    }
-
-    /**
-     * 创建微信支付订单(事务外执行,减少事务时长)
-     */
-    private Map<String, Object> createWxPayOrder(TOrder order, WxLoginUser wxLoginUser) {
-        try {
-            return wxPayV3Service.createV3JsapiOrder(order.getOrderNo(), order.getFinalAmount(), "购买情绪价值商品", wxLoginUser.getCOpenid(), WxPayTypeEnum.EMOTION_GOODS.getCode());
-        } catch (Exception e) {
-            log.error("微信支付下单失败,订单号: {}", order.getOrderNo(), e);
-            throw new ServiceException("支付服务异常,请稍后重试");
-        }
-    }
-
     /**
     /**
      * 根据订单ID查询订单详情
      * 根据订单ID查询订单详情
      */
      */
@@ -489,153 +340,12 @@ public class TOrderServiceImpl extends BaseServiceImpl<TOrderMapper, TOrder> imp
         // 1. 根据ID查询订单主表
         // 1. 根据ID查询订单主表
         TOrder order = validateAndGetOrder(orderId, null);
         TOrder order = validateAndGetOrder(orderId, null);
 
 
-        // 2. 查询订单状态流转记录
-        LambdaQueryWrapper<OrderStatusFlow> flowWrapper = Wrappers.<OrderStatusFlow>lambdaQuery().eq(OrderStatusFlow::getOrderId, order.getId()).orderByAsc(OrderStatusFlow::getCreateTime);
-        List<OrderStatusFlow> flowList = orderStatusFlowService.getBaseMapper().selectList(flowWrapper);
-
-        // 3. 组装VO
-        return buildOrderDetailVO(order, flowList);
-    }
-
-    /**
-     * 将订单实体和流转记录转换为详情VO
-     */
-    private OrderDetailVO buildOrderDetailVO(TOrder order, List<OrderStatusFlow> flowList) {
-        OrderDetailVO vo = new OrderDetailVO();
-
-        // 基础订单信息
-        vo.setOrderStatus(order.getStatus());
-        vo.setOrderStatusName(OrderStatusEnum.getInfoByCode(order.getStatus()));
-        vo.setMerchantAvatar(order.getMerchantAvatar());
-        // 1. 获取服务次数(从项目表获取 projectUsersNum)
-        Integer serviceNum = null;
-        if (order.getProjectId() != null && order.getMerchantId() != null) {
-            MaProject project = maProjectMapper.selectByProjectIdAndMerchantId(order.getProjectId(), order.getMerchantId());
-            if (project != null && project.getProjectUsersNum() != null) {
-                serviceNum = project.getProjectUsersNum().intValue();
-            }
-        }
-        vo.setServiceNum(serviceNum);
-
-        // 查询技师表
-        if (order.getMerchantId() != null) {
-            MaTechnician technician = maTechnicianMapper.selectById(order.getMerchantId());
-            if (technician != null) {
-                //评分
-                vo.setStar(technician.getNStar());
-                //简介
-                vo.setBrief(technician.getTeBrief());
-            } else {
-                log.warn("技师信息不存在,merchantId: {}", order.getMerchantId());
-            }
-        } else {
-            // 无 merchantId,字段置空(或可后续扩展)
-            vo.setStar(null);
-            vo.setBrief(null);
-        }
-
-        vo.setProjectName(order.getProjectName());
-        vo.setProjectDuration(order.getProjectDuration());
-        vo.setHighlight(order.getHighlight());
-        vo.setFinalAmount(order.getFinalAmount());
-        vo.setBasePrice(order.getBasePrice());
-        vo.setTrafficFee(order.getTrafficFee());
-        vo.setCouponDiscount(order.getCouponDiscount());
-        vo.setOrderNo(order.getOrderNo());
-        vo.setOrderId(String.valueOf(order.getId()));
-        vo.setMerchantName(order.getMerchantNickName());
-        vo.setMerchantId(order.getMerchantId());
-        // 时间处理
-        if (order.getAppointmentStartTime() != null) {
-            LocalDateTime start = order.getAppointmentStartTime();
-            // 预约结束时间 = 开始时间 + 项目时长(分钟)
-            LocalDateTime end = start.plusMinutes(order.getProjectDuration() == null ? 0 : order.getProjectDuration());
-
-            // 原始格式
-            vo.setAppointmentStartTime(start.format(DATE_TIME_FORMATTER));
-            vo.setAppointmentEndTime(end.format(DATE_TIME_FORMATTER));
-
-            // 生成“5月12日 16:00-18:00”格式
-            String timeRange = formatAppointmentTimeRange(start, end);
-            vo.setAppointmentTimeRange(timeRange);
-        }
-        if (order.getCreateTime() != null) {
-            vo.setCreateTime(DATE_FORMAT.format(order.getCreateTime()));
-        }
-        if (order.getPaidTime() != null) {
-            vo.setPaidTime(order.getPaidTime().format(DATE_TIME_FORMATTER));
-        }
-
-        vo.setContactAddresInfo(order.getContactAddressInfo());
-        // 联系信息组合:姓名 + 手机号(中间4位脱敏)
-        String contactInfo = buildContactInfo(order.getContactPersonName(), order.getContactPhoneNumber());
-        vo.setContactInfo(contactInfo);
-
-        vo.setPaymentMethod(order.getPaymentMethod());
-        vo.setPaymentMethodName(PaymentMethodEnum.getNameByCode(order.getPaymentMethod()));
-
-        // 获取订单售后数据
-        this.afterSalesServiceService.fillCurrentAfterSaleInfo(vo, order.getId());
-
-        // 订单状态流转列表转换
-        List<OrderStatusFlowVO> flowVOList = flowList.stream().map(this::convertToFlowVO).sorted(Comparator.comparing(OrderStatusFlowVO::getCreateTime)).collect(Collectors.toList());
-        vo.setOrderStatusFlowList(flowVOList);
-
-        return vo;
-    }
-
-    /**
-     * 格式化预约时间范围
-     *
-     * @param start 预约开始时间
-     * @param end   预约结束时间
-     * @return 示例:5月12日 16:00-18:00
-     */
-    private String formatAppointmentTimeRange(LocalDateTime start, LocalDateTime end) {
-        if (start == null) {
-            return "";
-        }
-        // 日期格式:M月d日(不带年份,同一年内)
-        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("M月d日");
-        String dateStr = start.format(dateFormatter);
-        // 时间格式:HH:mm
-        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm");
-        String startTime = start.format(timeFormatter);
-        String endTime = (end != null) ? end.format(timeFormatter) : "";
-        return dateStr + " " + startTime + "-" + endTime;
-    }
-
-    /**
-     * 构建联系信息(姓名 + 脱敏手机号)
-     * 示例:王先生,188****5555
-     */
-    private String buildContactInfo(String name, String phone) {
-        if (!StringUtils.hasText(name) && !StringUtils.hasText(phone)) {
-            return "";
-        }
-        String maskedPhone = "";
-        if (StringUtils.hasText(phone) && phone.length() >= 11) {
-            maskedPhone = phone.substring(0, 3) + "****" + phone.substring(7);
-        } else if (StringUtils.hasText(phone)) {
-            maskedPhone = phone; // 长度不足则直接显示
-        }
-        if (StringUtils.hasText(name)) {
-            return name + "," + maskedPhone;
-        } else {
-            return maskedPhone;
-        }
-    }
+        // 2. 查询订单状态流转记录
+        LambdaQueryWrapper<OrderStatusFlow> flowWrapper = Wrappers.<OrderStatusFlow>lambdaQuery().eq(OrderStatusFlow::getOrderId, order.getId()).orderByAsc(OrderStatusFlow::getCreateTime);
+        List<OrderStatusFlow> flowList = orderStatusFlowService.getBaseMapper().selectList(flowWrapper);
 
 
-    /**
-     * 将 OrderStatusFlow 转换为 OrderStatusFlowVO
-     */
-    private OrderStatusFlowVO convertToFlowVO(OrderStatusFlow flow) {
-        OrderStatusFlowVO vo = new OrderStatusFlowVO();
-        vo.setStatusName(OrderStatusEnum.getInfoByCode(flow.getStatus()));
-        if (flow.getCreateTime() != null) {
-            vo.setCreateTime(DATE_FORMAT.format(flow.getCreateTime()));
-        }
-        return vo;
+        // 3. 组装VO
+        return buildOrderDetailVO(order, flowList);
     }
     }
 
 
     @Override
     @Override
@@ -729,15 +439,15 @@ public class TOrderServiceImpl extends BaseServiceImpl<TOrderMapper, TOrder> imp
         }
         }
 
 
         // 5. 查询当前城市有效地理围栏,提前过滤非法围栏
         // 5. 查询当前城市有效地理围栏,提前过滤非法围栏
-        List<TGeoFence> validFenceList = geoFenceService.selectValidFences(dto.getCityCode());
-        List<TGeoFence> usableFence = filterUsableFence(validFenceList);
+        List<TGeoFence> validFenceList = this.geoFenceService.selectValidFences(dto.getCityCode());
+        List<TGeoFence> usableFence = this.geoFenceService.filterUsableFence(validFenceList);
 
 
 
 
         // 6. 批量遍历订单,计算风险区域+客户标签
         // 6. 批量遍历订单,计算风险区域+客户标签
         LocalDateTime now = DateUtils.getNowLocalDateTime();
         LocalDateTime now = DateUtils.getNowLocalDateTime();
         for (OrderPageVO vo : voRecords) {
         for (OrderPageVO vo : voRecords) {
             // 计算高风险围栏信息
             // 计算高风险围栏信息
-            calcOrderGeoFenceInfo(vo, usableFence);
+            this.geoFenceService.calcOrderGeoFenceInfo(vo, usableFence);
             // 计算客户标签
             // 计算客户标签
             Integer tagCode = calcCustomerTagCode(vo, merchantId, now);
             Integer tagCode = calcCustomerTagCode(vo, merchantId, now);
             vo.setCustomerTagCode(tagCode);
             vo.setCustomerTagCode(tagCode);
@@ -1046,131 +756,419 @@ public class TOrderServiceImpl extends BaseServiceImpl<TOrderMapper, TOrder> imp
     @Override
     @Override
     public Page<RecentMerchantVO> getRecentMerchants(RecentOrderQueryDTO dto) {
     public Page<RecentMerchantVO> getRecentMerchants(RecentOrderQueryDTO dto) {
 
 
-        // 1. 获取当前登录用户
-        Long userId = this.getCurrentUserId();
-        dto.setUserId(userId);
+        // 1. 获取当前登录用户
+        Long userId = this.getCurrentUserId();
+        dto.setUserId(userId);
+
+        // 2. 构建分页对象
+        Page<RecentMerchantVO> page = new Page<>(dto.getCurrent(), dto.getSize());
+        page = this.baseMapper.getRecentMerchants(page, dto);
+
+        return page;
+    }
+
+    @Override
+    public MerchantOrderDetailVO getMerchantOrderDetail(MerchantOrderDetailDTO dto) {
+
+        // 1. 根据ID查询订单主表
+        TOrder order = validateAndGetOrder(dto.getOrderId(), null);
+
+        MerchantOrderDetailVO vo = new MerchantOrderDetailVO<>();
+
+        // 2. 基础字段映射 (使用 BeanUtil 或手动 set)
+        BeanUtil.copyProperties(order, vo);
+        vo.setOrderStatus(order.getStatus());
+        vo.setOrderStatusName(OrderStatusEnum.getInfoByCode(order.getStatus()));
+
+        // 特殊处理 ID 转 String (前端 Long 精度丢失问题)
+        vo.setOrderId(String.valueOf(order.getId()));
+
+        // 格式化时间范围 (例如: 5月12日 16:00-18:00)
+        vo.setAppointmentTimeRange(formatTimeRange(order.getAppointmentStartTime(), order.getProjectDuration()));
+
+        // 3. 联系信息脱敏处理
+        vo.setContactPhoneNumber(PhoneUtils.maskPhoneNumberStrict(order.getContactPhoneNumber()));
+
+        // 4. 计算距离与告警提示 (依赖地理围栏表 t_geo_fence)
+        List<TGeoFence> validFenceList = geoFenceService.selectValidFences(order.getCityCode());
+        List<TGeoFence> usableFence = this.geoFenceService.filterUsableFence(validFenceList);
+
+        // 5. 查询关联的售后信息 (如果有)
+        this.afterSalesServiceService.fillCurrentAfterSaleInfo(vo, order.getId());
+
+        // 6. 计算高风险围栏信息
+        this.geoFenceService.calcOrderGeoFenceInfo(vo, usableFence);
+
+        return vo;
+    }
+
+    @Override
+    public List<BookingSlotsVO> bookingSlots(BookingSlotsDTO dto) {
+
+        // 1. 获取项目信息(关键:服务时长)
+        Project project = this.projectService.getById(dto.getProjectId());
+        if (ObjectUtil.isNull(project)) {
+            throw new ServiceException("项目不存在");
+        }
+
+        Integer standardDuration = project.getStandardDuration();
+        Integer unitType = project.getUnitType();
+        String unitTypeName = unitDictCache.getOrDefault(unitType, "未知单位");
+
+        Integer durationMinutes = convertToMinutes(standardDuration, unitType);
+        if (ObjectUtil.isNull(durationMinutes) || durationMinutes <= 0) {
+            throw new ServiceException("项目时长配置异常");
+        }
+
+        // 2. 生成所有候选开始时间(半点粒度,步进30分钟)
+        List<LocalTime> candidateStarts = TimeSlotUtil.getAvailableTimeSlots(dto.getAppointmentDate(), APPOINT_BUFFER_MINUTES);
+
+        // 3. 查询该商户当天的【有效占用】订单
+        LocalDateTime dayStart = dto.getAppointmentDate().atStartOfDay();
+        LocalDateTime dayEnd = dto.getAppointmentDate().atTime(LocalTime.MAX);
+        List<TOrder> validOrders = this.baseMapper.selectValidOrdersByMerchantAndDate(
+                dto.getMerchantId(), dto.getProjectId(), dayStart, dayEnd);
+
+        // 4. 遍历候选时段,构建 VO
+        List<BookingSlotsVO> voList = new ArrayList<>(candidateStarts.size());
+        for (LocalTime startTime : candidateStarts) {
+            LocalDateTime slotStart = LocalDateTime.of(dto.getAppointmentDate(), startTime);
+            LocalDateTime actualEnd = slotStart.plusMinutes(durationMinutes);
+
+            BookingSlotsVO vo = new BookingSlotsVO();
+            vo.setStartTime(startTime);
+            vo.setStandardDuration(standardDuration);
+            vo.setUnitTypeName(unitTypeName);
+            vo.setEndTime(actualEnd.toLocalTime());
+            vo.setIsDay(maProjectFareSettingService.isDayTimePeriod(slotStart));
+
+            boolean exceedsDay = !actualEnd.toLocalDate().isEqual(dto.getAppointmentDate());
+            vo.setAvailable(!exceedsDay && isAppointmentSlotAvailable(slotStart, durationMinutes, validOrders));
+            voList.add(vo);
+        }
+
+        return voList;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void startGoOut(MerchantOrderOperateDTO dto) {
+
+        // 1. 获取当前登录商户
+        WxLoginUser loginUser = getCurrentWxLoginUser();
+        Long merchantId = Long.parseLong(loginUser.getId());
+        Long orderId = dto.getOrderId();
+
+        // 查询订单
+        TOrder order = validateAndGetOrder(orderId, merchantId);
+
+        // 获取履约状态
+        Integer execStatus = order.getExecStatus();
+        if (!ExecStatusEnum.WAIT_DEPART.getCode().equals(execStatus)) {
+            throw new ServiceException("仅【待出发/待签到】订单可修改为已出发,当前状态:" + ExecStatusEnum.getInfoByCode(execStatus));
+        }
+
+        // 更新出发信息、履约信息
+        LocalDateTime now = LocalDateTime.now();
+        LambdaUpdateWrapper<TOrder> update = Wrappers.lambdaUpdate(TOrder.class)
+                .set(TOrder::getDepartedTime, now)
+                .set(TOrder::getUpdateTime, now)
+                .set(TOrder::getUpdateBy, loginUser.getCNickName())
+                .set(TOrder::getExecStatus, ExecStatusEnum.DEPARTED.getCode())
+                .eq(TOrder::getId, orderId);
+        int count = baseMapper.update(null, update);
+        if (count <= 0) {
+            throw new ServiceException("订单履约状态已变更,请刷新重试");
+        }
+
+    }
+
+    /**
+     * 将 TOrder 转换为 OrderDateQueryVo,并处理 serviceTime 字段
+     */
+    private OrderDateQueryVo convertToVo(TOrder order) {
+        OrderDateQueryVo vo = new OrderDateQueryVo();
+        // 拷贝相同字段
+        BeanUtil.copyProperties(order, vo);
+        // 手动设置字段名不一致或需要特殊处理的
+        vo.setOrderStatus(order.getStatus());
+        vo.setOrderStatusName(getOrderStatusName(order.getStatus()));
+        // 服务时间范围:预约开始时间 + 项目时长(分钟)
+        // 使用 startTime 和 completedTime 构建服务时间展示
+        String serviceTime = buildServiceTime(order.getStartTime(), order.getCompletedTime());
+        vo.setServiceTime(serviceTime);
+
+        // 获取订单售后数据
+        this.afterSalesServiceService.fillCurrentAfterSaleInfo(vo, order.getId());
+
+        return vo;
+    }
+
+    /**
+     * 订单状态码 -> 中文描述
+     */
+    private String getOrderStatusName(Integer status) {
+        return OrderStatusEnum.getInfoByCode(status);
+    }
+
+    /**
+     * 拼接服务时间字符串,例如 “2026-06-08 14:30 (90分钟)”
+     */
+    private String buildServiceTime(LocalDateTime startTime, LocalDateTime completedTime) {
+        if (startTime == null || completedTime == null) {
+            return "";
+        }
+        String date = startTime.format(dateFormatter);
+        String start = startTime.format(timeFormatter);
+        String end = completedTime.format(timeFormatter);
+        return date + " " + start + "-" + end;
+    }
+
+    /**
+     * 组装订单对象
+     */
+    private TOrder buildOrder(OrderSubmitDTO dto, Project project, MaTechnician maTechnician, TAddress address, List<TAddress> merchantAddressList, BigDecimal basePrice, BigDecimal trafficFee, BigDecimal couponDiscount, BigDecimal finalAmount, Long userId) {
+        TOrder order = new TOrder();
+        order.setOrderNo(orderNumberGenerator.generateNextOrderNumber(""));
+        order.setUserId(userId);
+        order.setProjectId(dto.getProjectId());
+        order.setProjectType(project.getType());
+        order.setProjectName(project.getTitle());
+        order.setProjectCover(project.getCover());
+        order.setHighlight(project.getHighlight());
+        order.setAppointmentStartTime(dto.getAppointmentStartTime());
+
+        // 根据项目的unitType字段将小时转换为分钟
+        Integer projectDuration = this.convertToMinutes(project.getStandardDuration(), project.getUnitType());
+        order.setProjectDuration(projectDuration);
+        order.setMerchantId(dto.getMerchantId());
+        order.setMerchantType(maTechnician.getTechType());
+        order.setMerchantNickName(maTechnician.getTeNickName());
+        order.setMerchantAvatar(maTechnician.getTeAvatar());
+        order.setContactPersonName(address.getUserName());
+        order.setContactPhoneNumber(address.getPhone());
+        order.setContactAddressInfo(address.getDetailAddress());
+        order.setCityCode(address.getCityCode());
+        order.setBasePrice(basePrice);
+        order.setCouponDiscount(couponDiscount);
+        order.setTrafficFee(trafficFee);
+        order.setFinalAmount(finalAmount);
+        order.setCouponId(dto.getCouponId());
+        order.setCreateTime(DateUtils.getNowDate());
+        order.setStatus(OrderStatusEnum.PENDING_PAYMENT.getCode());
+        order.setExecStatus(ExecStatusEnum.WAIT_DEPART.getCode());
+        order.setDispatchedStatus(DispatchedStatusEnum.UN_DISPATCHED.getCode());
+        order.setPaymentMethod(dto.getPaymentMethod());
+
+        // 经纬度安全赋值
+        order.setUserLatitude(address.getLatitude());
+        order.setUserLongitude(address.getLongitude());
+
+        merchantAddressList.stream().filter(a -> a.getType().equals(ADDR_TYPE_REAL)).findFirst().ifPresent(addr -> {
+            order.setMerchantLongitude(addr.getLongitude());
+            order.setMerchantLatitude(addr.getLatitude());
+        });
+        merchantAddressList.stream().filter(a -> a.getType().equals(ADDR_TYPE_VIRTUAL)).findFirst().ifPresent(addr -> {
+            order.setVirtualLongitude(addr.getLongitude());
+            order.setVirtualLatitude(addr.getLatitude());
+        });
+
+        // 设置支付状态
+        if (ObjectUtil.equals(PaymentMethodEnum.BALANCE.getCode(), dto.getPaymentMethod())) {
+            order.setStatus(OrderStatusEnum.PENDING_DISPATCH.getCode());
+            order.setPaidTime(LocalDateTime.now());
+        } else {
+            order.setStatus(OrderStatusEnum.PENDING_PAYMENT.getCode());
+        }
+        return order;
+    }
+
+    /**
+     * 处理余额支付 (核心优化:数据库原子操作防并发)
+     */
+    private void handleBalancePayment(Long userId, BigDecimal finalAmount, TOrder order) {
+        // 1. 数据库层面原子扣减余额,并校验余额是否充足
+        boolean deductSuccess = wxUserService.lambdaUpdate()
+                .setSql("d_balance = d_balance - {0}")
+                .eq(TWxUser::getId, userId)
+                // 两个位置共用同一个参数,逗号传参
+                .apply("d_balance >= {0}", finalAmount)
+                .update();
+
+        if (!deductSuccess) {
+            throw new ServiceException("余额不足,扣款失败");
+        }
+
+        // 2. 重新查询最新余额用于记录流水
+        TWxUser user = this.wxUserService.getById(userId);
 
 
-        // 2. 构建分页对象
-        Page<RecentMerchantVO> page = new Page<>(dto.getCurrent(), dto.getSize());
-        page = this.baseMapper.getRecentMerchants(page, dto);
+        // 3. 记录购物金明细
+        ShoppingFundsDetailAddDto detailDto = new ShoppingFundsDetailAddDto();
+        detailDto.setUserId(userId.toString());
+        detailDto.setAmount(finalAmount);
+        detailDto.setOrderNo(order.getOrderNo());
+        detailDto.setExpenseType(ShoppingFundsExpenseTypeEnum.CONSUMPTION.getCode());
+        detailDto.setBalance(user.getdBalance());
+        this.shoppingFundsDetailService.addShoppingFundsDetail(detailDto);
 
 
-        return page;
+        // 4. 记录状态流转流水
+        OrderUpdateStatusDTO dto = new OrderUpdateStatusDTO();
+        dto.setOrderId(order.getId());
+        dto.setStatus(order.getStatus());
+        this.updateOrderStatus(dto);
     }
     }
 
 
-    @Override
-    public MerchantOrderDetailVO getMerchantOrderDetail(MerchantOrderDetailDTO dto) {
-
-        // 1. 根据ID查询订单主表
-        TOrder order = validateAndGetOrder(dto.getOrderId(), null);
+    /**
+     * 创建微信支付订单(事务外执行,减少事务时长)
+     */
+    private Map<String, Object> createWxPayOrder(TOrder order, WxLoginUser wxLoginUser) {
+        try {
+            return wxPayV3Service.createV3JsapiOrder(order.getOrderNo(), order.getFinalAmount(), "购买情绪价值商品", wxLoginUser.getCOpenid(), WxPayTypeEnum.EMOTION_GOODS.getCode());
+        } catch (Exception e) {
+            log.error("微信支付下单失败,订单号: {}", order.getOrderNo(), e);
+            throw new ServiceException("支付服务异常,请稍后重试");
+        }
+    }
 
 
-        MerchantOrderDetailVO vo = new MerchantOrderDetailVO<>();
+    /**
+     * 将订单实体和流转记录转换为详情VO
+     */
+    private OrderDetailVO buildOrderDetailVO(TOrder order, List<OrderStatusFlow> flowList) {
+        OrderDetailVO vo = new OrderDetailVO();
 
 
-        // 2. 基础字段映射 (使用 BeanUtil 或手动 set)
-        BeanUtil.copyProperties(order, vo);
+        // 基础订单信息
         vo.setOrderStatus(order.getStatus());
         vo.setOrderStatus(order.getStatus());
         vo.setOrderStatusName(OrderStatusEnum.getInfoByCode(order.getStatus()));
         vo.setOrderStatusName(OrderStatusEnum.getInfoByCode(order.getStatus()));
+        vo.setMerchantAvatar(order.getMerchantAvatar());
+        // 1. 获取服务次数(从项目表获取 projectUsersNum)
+        Integer serviceNum = null;
+        if (order.getProjectId() != null && order.getMerchantId() != null) {
+            MaProject project = maProjectMapper.selectByProjectIdAndMerchantId(order.getProjectId(), order.getMerchantId());
+            if (project != null && project.getProjectUsersNum() != null) {
+                serviceNum = project.getProjectUsersNum().intValue();
+            }
+        }
+        vo.setServiceNum(serviceNum);
 
 
-        // 特殊处理 ID 转 String (前端 Long 精度丢失问题)
+        // 查询技师表
+        if (order.getMerchantId() != null) {
+            MaTechnician technician = maTechnicianMapper.selectById(order.getMerchantId());
+            if (technician != null) {
+                //评分
+                vo.setStar(technician.getNStar());
+                //简介
+                vo.setBrief(technician.getTeBrief());
+            } else {
+                log.warn("技师信息不存在,merchantId: {}", order.getMerchantId());
+            }
+        } else {
+            // 无 merchantId,字段置空(或可后续扩展)
+            vo.setStar(null);
+            vo.setBrief(null);
+        }
+
+        vo.setProjectName(order.getProjectName());
+        vo.setProjectDuration(order.getProjectDuration());
+        vo.setHighlight(order.getHighlight());
+        vo.setFinalAmount(order.getFinalAmount());
+        vo.setBasePrice(order.getBasePrice());
+        vo.setTrafficFee(order.getTrafficFee());
+        vo.setCouponDiscount(order.getCouponDiscount());
+        vo.setOrderNo(order.getOrderNo());
         vo.setOrderId(String.valueOf(order.getId()));
         vo.setOrderId(String.valueOf(order.getId()));
+        vo.setMerchantName(order.getMerchantNickName());
+        vo.setMerchantId(order.getMerchantId());
+        // 时间处理
+        if (order.getAppointmentStartTime() != null) {
+            LocalDateTime start = order.getAppointmentStartTime();
+            // 预约结束时间 = 开始时间 + 项目时长(分钟)
+            LocalDateTime end = start.plusMinutes(order.getProjectDuration() == null ? 0 : order.getProjectDuration());
 
 
-        // 格式化时间范围 (例如: 5月12日 16:00-18:00)
-        vo.setAppointmentTimeRange(formatTimeRange(order.getAppointmentStartTime(), order.getProjectDuration()));
+            // 原始格式
+            vo.setAppointmentStartTime(start.format(DATE_TIME_FORMATTER));
+            vo.setAppointmentEndTime(end.format(DATE_TIME_FORMATTER));
 
 
-        // 3. 联系信息脱敏处理
-        vo.setContactPhoneNumber(PhoneUtils.maskPhoneNumberStrict(order.getContactPhoneNumber()));
+            // 生成“5月12日 16:00-18:00”格式
+            String timeRange = formatAppointmentTimeRange(start, end);
+            vo.setAppointmentTimeRange(timeRange);
+        }
+        if (order.getCreateTime() != null) {
+            vo.setCreateTime(DATE_FORMAT.format(order.getCreateTime()));
+        }
+        if (order.getPaidTime() != null) {
+            vo.setPaidTime(order.getPaidTime().format(DATE_TIME_FORMATTER));
+        }
 
 
-        // 4. 计算距离与告警提示 (依赖地理围栏表 t_geo_fence)
-        List<TGeoFence> validFenceList = geoFenceService.selectValidFences(order.getCityCode());
-        List<TGeoFence> usableFence = filterUsableFence(validFenceList);
+        vo.setContactAddresInfo(order.getContactAddressInfo());
+        // 联系信息组合:姓名 + 手机号(中间4位脱敏)
+        String contactInfo = buildContactInfo(order.getContactPersonName(), order.getContactPhoneNumber());
+        vo.setContactInfo(contactInfo);
 
 
-        // 5. 查询关联的售后信息 (如果有)
+        vo.setPaymentMethod(order.getPaymentMethod());
+        vo.setPaymentMethodName(PaymentMethodEnum.getNameByCode(order.getPaymentMethod()));
+
+        // 获取订单售后数据
         this.afterSalesServiceService.fillCurrentAfterSaleInfo(vo, order.getId());
         this.afterSalesServiceService.fillCurrentAfterSaleInfo(vo, order.getId());
 
 
-        // 6. 计算高风险围栏信息
-        calcOrderGeoFenceInfo(vo, usableFence);
+        // 订单状态流转列表转换
+        List<OrderStatusFlowVO> flowVOList = flowList.stream().map(this::convertToFlowVO).sorted(Comparator.comparing(OrderStatusFlowVO::getCreateTime)).collect(Collectors.toList());
+        vo.setOrderStatusFlowList(flowVOList);
 
 
         return vo;
         return vo;
     }
     }
 
 
-    @Override
-    public List<BookingSlotsVO> bookingSlots(BookingSlotsDTO dto) {
-
-        // 1. 获取项目信息(关键:服务时长)
-        Project project = this.projectService.getById(dto.getProjectId());
-        if (ObjectUtil.isNull(project)) {
-            throw new ServiceException("项目不存在");
+    /**
+     * 格式化预约时间范围
+     *
+     * @param start 预约开始时间
+     * @param end   预约结束时间
+     * @return 示例:5月12日 16:00-18:00
+     */
+    private String formatAppointmentTimeRange(LocalDateTime start, LocalDateTime end) {
+        if (start == null) {
+            return "";
         }
         }
+        // 日期格式:M月d日(不带年份,同一年内)
+        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("M月d日");
+        String dateStr = start.format(dateFormatter);
+        // 时间格式:HH:mm
+        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm");
+        String startTime = start.format(timeFormatter);
+        String endTime = (end != null) ? end.format(timeFormatter) : "";
+        return dateStr + " " + startTime + "-" + endTime;
+    }
 
 
-        Integer standardDuration = project.getStandardDuration();
-        Integer unitType = project.getUnitType();
-        String unitTypeName = unitDictCache.getOrDefault(unitType, "未知单位");
-
-        Integer durationMinutes = convertToMinutes(standardDuration, unitType);
-        if (ObjectUtil.isNull(durationMinutes) || durationMinutes <= 0) {
-            throw new ServiceException("项目时长配置异常");
+    /**
+     * 构建联系信息(姓名 + 脱敏手机号)
+     * 示例:王先生,188****5555
+     */
+    private String buildContactInfo(String name, String phone) {
+        if (!StringUtils.hasText(name) && !StringUtils.hasText(phone)) {
+            return "";
         }
         }
-
-        // 2. 生成所有候选开始时间(半点粒度,步进30分钟)
-        List<LocalTime> candidateStarts = TimeSlotUtil.getAvailableTimeSlots(dto.getAppointmentDate(), APPOINT_BUFFER_MINUTES);
-
-        // 3. 查询该商户当天的【有效占用】订单
-        LocalDateTime dayStart = dto.getAppointmentDate().atStartOfDay();
-        LocalDateTime dayEnd = dto.getAppointmentDate().atTime(LocalTime.MAX);
-        List<TOrder> validOrders = this.baseMapper.selectValidOrdersByMerchantAndDate(
-                dto.getMerchantId(), dto.getProjectId(), dayStart, dayEnd);
-
-        // 4. 遍历候选时段,构建 VO
-        List<BookingSlotsVO> voList = new ArrayList<>(candidateStarts.size());
-        for (LocalTime startTime : candidateStarts) {
-            LocalDateTime slotStart = LocalDateTime.of(dto.getAppointmentDate(), startTime);
-            LocalDateTime actualEnd = slotStart.plusMinutes(durationMinutes);
-
-            BookingSlotsVO vo = new BookingSlotsVO();
-            vo.setStartTime(startTime);
-            vo.setStandardDuration(standardDuration);
-            vo.setUnitTypeName(unitTypeName);
-            vo.setEndTime(actualEnd.toLocalTime());
-            vo.setIsDay(maProjectFareSettingService.isDayTimePeriod(slotStart));
-
-            boolean exceedsDay = !actualEnd.toLocalDate().isEqual(dto.getAppointmentDate());
-            vo.setAvailable(!exceedsDay && isAppointmentSlotAvailable(slotStart, durationMinutes, validOrders));
-            voList.add(vo);
+        String maskedPhone = "";
+        if (StringUtils.hasText(phone) && phone.length() >= 11) {
+            maskedPhone = phone.substring(0, 3) + "****" + phone.substring(7);
+        } else if (StringUtils.hasText(phone)) {
+            maskedPhone = phone; // 长度不足则直接显示
         }
         }
-
-        return voList;
-    }
-
-    @Override
-    @Transactional(rollbackFor = Exception.class)
-    public void startGoOut(MerchantOrderOperateDTO dto) {
-
-        // 1. 获取当前登录商户
-        WxLoginUser loginUser = getCurrentWxLoginUser();
-        Long merchantId = Long.parseLong(loginUser.getId());
-        Long orderId = dto.getOrderId();
-
-        // 查询订单
-        TOrder order = validateAndGetOrder(orderId, merchantId);
-
-        // 获取履约状态
-        Integer execStatus = order.getExecStatus();
-        if (!ExecStatusEnum.WAIT_DEPART.getCode().equals(execStatus)) {
-            throw new ServiceException("仅【待出发/待签到】订单可修改为已出发,当前状态:" + ExecStatusEnum.getInfoByCode(execStatus));
+        if (StringUtils.hasText(name)) {
+            return name + "," + maskedPhone;
+        } else {
+            return maskedPhone;
         }
         }
+    }
 
 
-        // 更新出发信息、履约信息
-        LocalDateTime now = LocalDateTime.now();
-        LambdaUpdateWrapper<TOrder> update = Wrappers.lambdaUpdate(TOrder.class)
-                .set(TOrder::getDepartedTime, now)
-                .set(TOrder::getUpdateTime, now)
-                .set(TOrder::getUpdateBy, loginUser.getCNickName())
-                .set(TOrder::getExecStatus, ExecStatusEnum.DEPARTED.getCode())
-                .eq(TOrder::getId, orderId);
-        int count = baseMapper.update(null, update);
-        if (count <= 0) {
-            throw new ServiceException("订单履约状态已变更,请刷新重试");
+    /**
+     * 将 OrderStatusFlow 转换为 OrderStatusFlowVO
+     */
+    private OrderStatusFlowVO convertToFlowVO(OrderStatusFlow flow) {
+        OrderStatusFlowVO vo = new OrderStatusFlowVO();
+        vo.setStatusName(OrderStatusEnum.getInfoByCode(flow.getStatus()));
+        if (flow.getCreateTime() != null) {
+            vo.setCreateTime(DATE_FORMAT.format(flow.getCreateTime()));
         }
         }
-
+        return vo;
     }
     }
 
 
     /**
     /**
@@ -1310,62 +1308,6 @@ public class TOrderServiceImpl extends BaseServiceImpl<TOrderMapper, TOrder> imp
         dto.setLongitude(lon);
         dto.setLongitude(lon);
     }
     }
 
 
-    /**
-     * 过滤可用围栏:剔除坐标/半径为空、半径<=0的无效围栏,减少内层循环次数
-     */
-    private List<TGeoFence> filterUsableFence(List<TGeoFence> allFence) {
-        if (CollUtil.isEmpty(allFence)) {
-            return Collections.emptyList();
-        }
-        return allFence.stream()
-                .filter(fence -> !ObjectUtil.hasNull(fence.getLatitude(), fence.getLongitude(), fence.getRadiusKm()))
-                .filter(fence -> fence.getRadiusKm().compareTo(BigDecimal.ZERO) > 0)
-                .collect(Collectors.toList());
-    }
-
-    /**
-     * 计算单条订单围栏距离、是否高风险区域、围栏备注
-     */
-    private void calcOrderGeoFenceInfo(IGeoRiskInfo orderVO, List<TGeoFence> usableFence) {
-        // 用户坐标为空直接标记非风险
-        BigDecimal userLat = orderVO.getUserLatitude();
-        BigDecimal userLon = orderVO.getUserLongitude();
-        if (ObjectUtil.hasNull(userLat, userLon) || CollUtil.isEmpty(usableFence)) {
-            orderVO.setIsHighRiskArea(false);
-            orderVO.setDistanceKm(null);
-            orderVO.setFenceIntro(null);
-            return;
-        }
-
-        BigDecimal minDistanceKm = null;
-        TGeoFence nearestFence = null;
-        // 遍历可用围栏,计算最小距离围栏
-        for (TGeoFence fence : usableFence) {
-            BigDecimal distKm = DistanceUtil.formatDistanceInKilometers(userLat, userLon, fence.getLatitude(), fence.getLongitude());
-            if (ObjectUtil.isNull(distKm)) {
-                continue;
-            }
-            // 更新最近围栏
-            if (ObjectUtil.isNull(minDistanceKm) || distKm.compareTo(minDistanceKm) < 0) {
-                minDistanceKm = distKm;
-                nearestFence = fence;
-            }
-        }
-
-        // 赋值围栏相关信息
-        if (ObjectUtil.isNull(minDistanceKm) || ObjectUtil.isNull(nearestFence)) {
-            orderVO.setIsHighRiskArea(false);
-            orderVO.setDistanceKm(null);
-            orderVO.setFenceIntro(null);
-            return;
-        }
-        orderVO.setDistanceKm(minDistanceKm);
-        // 判断是否在围栏半径内
-        boolean isHighRisk = minDistanceKm.compareTo(nearestFence.getRadiusKm()) <= 0;
-        orderVO.setIsHighRiskArea(isHighRisk);
-        orderVO.setFenceIntro(nearestFence.getFenceIntro());
-    }
-
     /**
     /**
      * 格式化预约时间范围
      * 格式化预约时间范围
      * 示例输出: "5月12日 16:00-18:00"
      * 示例输出: "5月12日 16:00-18:00"