p-honggang.li
2025-08-28 f0da95925bcb8ad19b0d1cc78afe4b4a4ae188a6
src/main/java/com/webmanage/service/impl/OrderInfoServiceImpl.java
@@ -8,17 +8,24 @@
import com.webmanage.common.PageResult;
import com.webmanage.dto.CreateOrderDTO;
import com.webmanage.dto.CreateOrderItemDTO;
import com.webmanage.dto.FileCheckDTO;
import com.webmanage.dto.OrderApprovalDTO;
import com.webmanage.dto.OrderQueryDTO;
import com.webmanage.dto.UpdateOrderDetailDTO;
import com.webmanage.entity.OrderApproval;
import com.webmanage.entity.OrderAttachment;
import com.webmanage.entity.OrderDetail;
import com.webmanage.entity.OrderEvaluation;
import com.webmanage.entity.OrderInfo;
import com.webmanage.mapper.OrderApprovalMapper;
import com.webmanage.mapper.OrderAttachmentMapper;
import com.webmanage.mapper.OrderDetailMapper;
import com.webmanage.mapper.OrderEvaluationMapper;
import com.webmanage.mapper.OrderInfoMapper;
import com.webmanage.mapper.ReportResultSubmissionMapper;
import com.webmanage.service.OrderInfoService;
import com.webmanage.service.OrderNoService;
import com.webmanage.service.MinioService;
import com.webmanage.vo.OrderAttachmentVO;
import com.webmanage.vo.OrderDetailItemVO;
import com.webmanage.vo.OrderDetailVO;
@@ -53,10 +60,19 @@
    private OrderEvaluationMapper orderEvaluationMapper;
    @Resource
    private OrderApprovalMapper orderApprovalMapper;
    @Resource
    private OrderNoService orderNoService;
    @Resource
    private MinioService minioService;
    @Resource
    private ReportResultSubmissionMapper reportResultSubmissionMapper;
    @Override
    public PageResult<OrderInfo> getBuyerOrderPage(OrderQueryDTO queryDTO) {
    public PageResult<OrderDetailVO> getBuyerOrderPage(OrderQueryDTO queryDTO) {
        // 参数校验
        if (queryDTO.getUserId() == null) {
            throw new BusinessException("用户ID不能为空");
@@ -76,10 +92,23 @@
            queryDTO.getCreateTimeEnd() != null ? queryDTO.getCreateTimeEnd().toString() : null,
            queryDTO.getOrderBy(), queryDTO.getOrderDirection()
        );
        // 将订单与详情联表封装到VO
        List<OrderDetailVO> voList = result.getRecords().stream().map(order -> {
            OrderDetailVO vo = new OrderDetailVO();
            BeanUtils.copyProperties(order, vo);
            List<OrderDetail> details = orderDetailMapper.selectByOrderId(order.getOrderId());
            List<OrderDetailItemVO> items = details.stream().map(d -> {
                OrderDetailItemVO item = new OrderDetailItemVO();
                BeanUtils.copyProperties(d, item);
                return item;
            }).collect(java.util.stream.Collectors.toList());
            vo.setOrderDetails(items);
            return vo;
        }).collect(java.util.stream.Collectors.toList());
        // 构建返回结果
        return new PageResult<OrderInfo>(
            result.getRecords(),
        return new PageResult<OrderDetailVO>(
            voList,
            result.getTotal(),
            queryDTO.getPageNum().longValue(),
            queryDTO.getPageSize().longValue(),
@@ -88,10 +117,10 @@
    }
    @Override
    public PageResult<OrderInfo> getSellerOrderPage(OrderQueryDTO queryDTO) {
    public PageResult<OrderDetailVO> getSellerOrderPage(OrderQueryDTO queryDTO) {
        // 参数校验
        if (queryDTO.getUserId() == null) {
            throw new BusinessException("用户ID不能为空");
        if (queryDTO.getProviderId() == null) {
            throw new BusinessException("提供者ID不能为空");
        }
        // 创建分页对象
@@ -99,7 +128,7 @@
        // 执行分页查询
        IPage<OrderInfo> result = baseMapper.selectSellerOrderPage(
            page, queryDTO.getUserId(), queryDTO.getOrderStatus(), queryDTO.getPaymentStatus(),
            page, queryDTO.getProviderId(), queryDTO.getOrderStatus(), queryDTO.getPaymentStatus(),
            queryDTO.getProductName(), queryDTO.getOrderId(),
            queryDTO.getApplyTimeStart() != null ? queryDTO.getApplyTimeStart().toString() : null,
            queryDTO.getApplyTimeEnd() != null ? queryDTO.getApplyTimeEnd().toString() : null,
@@ -108,9 +137,23 @@
            queryDTO.getOrderBy(), queryDTO.getOrderDirection()
        );
        // 将订单与详情联表封装到VO
        List<OrderDetailVO> voList = result.getRecords().stream().map(order -> {
            OrderDetailVO vo = new OrderDetailVO();
            BeanUtils.copyProperties(order, vo);
            List<OrderDetail> details = orderDetailMapper.selectByOrderId(order.getOrderId());
            List<OrderDetailItemVO> items = details.stream().map(d -> {
                OrderDetailItemVO item = new OrderDetailItemVO();
                BeanUtils.copyProperties(d, item);
                return item;
            }).collect(java.util.stream.Collectors.toList());
            vo.setOrderDetails(items);
            return vo;
        }).collect(java.util.stream.Collectors.toList());
        // 构建返回结果
        return new PageResult<OrderInfo>(
            result.getRecords(),
        return new PageResult<OrderDetailVO>(
            voList,
            result.getTotal(),
            queryDTO.getPageNum().longValue(),
            queryDTO.getPageSize().longValue(),
@@ -135,6 +178,139 @@
        // 构建返回结果
        return new PageResult<OrderInfo>(
            result.getRecords(),
            result.getTotal(),
            queryDTO.getPageNum().longValue(),
            queryDTO.getPageSize().longValue(),
            result.getPages()
        );
    }
    @Override
    public PageResult<OrderDetailVO> getBuyerOrderPageWithProductConditions(OrderQueryDTO queryDTO) {
        // 参数校验
        if (queryDTO.getUserId() == null) {
            throw new BusinessException("用户ID不能为空");
        }
        // 根据产品条件查询产品ID列表
        List<String> productIds = null;
        if (StringUtils.hasText(queryDTO.getIndustryId()) || StringUtils.hasText(queryDTO.getUnitProjectId()) ||
            StringUtils.hasText(queryDTO.getProductTypeId()) || StringUtils.hasText(queryDTO.getProductSubTypeId())) {
            productIds = reportResultSubmissionMapper.selectProductIdsByConditions(
                queryDTO.getIndustryId(), queryDTO.getUnitProjectId(),
                queryDTO.getProductTypeId(), queryDTO.getProductSubTypeId()
            );
            // 如果没有找到匹配的产品,直接返回空结果
            if (CollectionUtils.isEmpty(productIds)) {
                return new PageResult<OrderDetailVO>(
                    java.util.Collections.emptyList(),
                    0L,
                    queryDTO.getPageNum().longValue(),
                    queryDTO.getPageSize().longValue(),
                    0L
                );
            }
        }
        // 创建分页对象
        Page<OrderInfo> page = new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize());
        // 执行分页查询
        IPage<OrderInfo> result = baseMapper.selectBuyerOrderPageWithProductConditions(
            page, queryDTO.getUserId(), queryDTO.getUnitId(), queryDTO.getOrderStatus(),
            queryDTO.getPaymentStatus(), queryDTO.getPaymentType(), queryDTO.getProductName(),
            queryDTO.getProviderName(), queryDTO.getOrderId(),
            queryDTO.getApplyTimeStart() != null ? queryDTO.getApplyTimeStart().toString() : null,
            queryDTO.getApplyTimeEnd() != null ? queryDTO.getApplyTimeEnd().toString() : null,
            queryDTO.getCreateTimeStart() != null ? queryDTO.getCreateTimeStart().toString() : null,
            queryDTO.getCreateTimeEnd() != null ? queryDTO.getCreateTimeEnd().toString() : null,
            queryDTO.getOrderBy(), queryDTO.getOrderDirection(), productIds
        );
        // 将订单与详情联表封装到VO
        List<OrderDetailVO> voList = result.getRecords().stream().map(order -> {
            OrderDetailVO vo = new OrderDetailVO();
            BeanUtils.copyProperties(order, vo);
            List<OrderDetail> details = orderDetailMapper.selectByOrderId(order.getOrderId());
            List<OrderDetailItemVO> items = details.stream().map(d -> {
                OrderDetailItemVO item = new OrderDetailItemVO();
                BeanUtils.copyProperties(d, item);
                return item;
            }).collect(java.util.stream.Collectors.toList());
            vo.setOrderDetails(items);
            return vo;
        }).collect(java.util.stream.Collectors.toList());
        // 构建返回结果
        return new PageResult<OrderDetailVO>(
            voList,
            result.getTotal(),
            queryDTO.getPageNum().longValue(),
            queryDTO.getPageSize().longValue(),
            result.getPages()
        );
    }
    @Override
    public PageResult<OrderDetailVO> getSellerOrderPageWithProductConditions(OrderQueryDTO queryDTO) {
        // 参数校验
        if (queryDTO.getProviderId() == null) {
            throw new BusinessException("提供者ID不能为空");
        }
        // 根据产品条件查询产品ID列表
        List<String> productIds = null;
        if (StringUtils.hasText(queryDTO.getIndustryId()) || StringUtils.hasText(queryDTO.getUnitProjectId()) ||
            StringUtils.hasText(queryDTO.getProductTypeId()) || StringUtils.hasText(queryDTO.getProductSubTypeId())) {
            productIds = reportResultSubmissionMapper.selectProductIdsByConditions(
                queryDTO.getIndustryId(), queryDTO.getUnitProjectId(),
                queryDTO.getProductTypeId(), queryDTO.getProductSubTypeId()
            );
            // 如果没有找到匹配的产品,直接返回空结果
            if (CollectionUtils.isEmpty(productIds)) {
                return new PageResult<OrderDetailVO>(
                    java.util.Collections.emptyList(),
                    0L,
                    queryDTO.getPageNum().longValue(),
                    queryDTO.getPageSize().longValue(),
                    0L
                );
            }
        }
        // 创建分页对象
        Page<OrderInfo> page = new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize());
        // 执行分页查询
        IPage<OrderInfo> result = baseMapper.selectSellerOrderPageWithProductConditions(
            page, queryDTO.getProviderId(), queryDTO.getOrderStatus(), queryDTO.getPaymentStatus(),
            queryDTO.getProductName(), queryDTO.getOrderId(),
            queryDTO.getApplyTimeStart() != null ? queryDTO.getApplyTimeStart().toString() : null,
            queryDTO.getApplyTimeEnd() != null ? queryDTO.getApplyTimeEnd().toString() : null,
            queryDTO.getCreateTimeStart() != null ? queryDTO.getCreateTimeStart().toString() : null,
            queryDTO.getCreateTimeEnd() != null ? queryDTO.getCreateTimeEnd().toString() : null,
            queryDTO.getOrderBy(), queryDTO.getOrderDirection(), productIds
        );
        // 将订单与详情联表封装到VO
        List<OrderDetailVO> voList = result.getRecords().stream().map(order -> {
            OrderDetailVO vo = new OrderDetailVO();
            BeanUtils.copyProperties(order, vo);
            List<OrderDetail> details = orderDetailMapper.selectByOrderId(order.getOrderId());
            List<OrderDetailItemVO> items = details.stream().map(d -> {
                OrderDetailItemVO item = new OrderDetailItemVO();
                BeanUtils.copyProperties(d, item);
                return item;
            }).collect(java.util.stream.Collectors.toList());
            vo.setOrderDetails(items);
            return vo;
        }).collect(java.util.stream.Collectors.toList());
        // 构建返回结果
        return new PageResult<OrderDetailVO>(
            voList,
            result.getTotal(),
            queryDTO.getPageNum().longValue(),
            queryDTO.getPageSize().longValue(),
@@ -219,7 +395,11 @@
        orderInfo.setUserId(createOrderDTO.getUserId());
        orderInfo.setUnitId(createOrderDTO.getUnitId());
        orderInfo.setApplyTime(LocalDateTime.now());
        orderInfo.setOrderStatus("待审批");
        // 根据订单明细中是否包含协议价格类型来决定初始状态
        String initialStatus = determineInitialOrderStatus(createOrderDTO.getItems());
        orderInfo.setOrderStatus(initialStatus);
        orderInfo.setTotalAmount(createOrderDTO.getTotalAmount() != null ? createOrderDTO.getTotalAmount() : totalAmount);
        orderInfo.setPaymentType(createOrderDTO.getPaymentType());
        orderInfo.setPaymentStatus("未支付");
@@ -263,7 +443,7 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean uploadOrderAttachment(String orderId, String fileName, String originalName,
    public Long uploadOrderAttachment(String orderId, String fileName, String originalName,
                                      String fileType, Long fileSize, String fileUrl, 
                                      String bucketName, String objectName, Long uploadUserId, 
                                      String uploadUserName, String attachmentType, String description) {
@@ -296,8 +476,13 @@
        attachment.setAttachmentType(attachmentType);
        attachment.setDescription(description);
        // 保存附件
        return orderAttachmentMapper.insert(attachment) > 0;
        // 保存附件并返回附件ID
        int result = orderAttachmentMapper.insert(attachment);
        if (result > 0) {
            return attachment.getId();
        } else {
            throw new BusinessException("保存附件失败");
        }
    }
    @Override
@@ -419,4 +604,524 @@
        // 保存评价
        return orderEvaluationMapper.updateById(evaluation) > 0;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateOrderDetail(UpdateOrderDetailDTO updateOrderDetailDTO) {
        // 参数校验
        if (updateOrderDetailDTO == null) {
            throw new BusinessException("更新参数不能为空");
        }
        if (!StringUtils.hasText(updateOrderDetailDTO.getOrderId())) {
            throw new BusinessException("订单ID不能为空");
        }
        if (!StringUtils.hasText(updateOrderDetailDTO.getOrderStatus())) {
            throw new BusinessException("订单状态不能为空");
        }
        if (CollectionUtils.isEmpty(updateOrderDetailDTO.getOrderDetails())) {
            throw new BusinessException("订单详情列表不能为空");
        }
        // 查询订单信息
        OrderInfo orderInfo = this.getById(updateOrderDetailDTO.getOrderId());
        if (orderInfo == null) {
            throw new BusinessException("订单不存在");
        }
        // 更新订单状态
        orderInfo.setOrderStatus(updateOrderDetailDTO.getOrderStatus());
        orderInfo.setUpdatedAt(LocalDateTime.now());
        int orderUpdated = this.baseMapper.updateById(orderInfo);
        if (orderUpdated <= 0) {
            throw new BusinessException("更新订单状态失败");
        }
        // 更新订单详情备注
        for (UpdateOrderDetailDTO.UpdateOrderDetailItemDTO itemDTO : updateOrderDetailDTO.getOrderDetails()) {
            if (itemDTO.getId() == null) {
                continue;
            }
            OrderDetail orderDetail = orderDetailMapper.selectById(itemDTO.getId());
            if (orderDetail == null) {
                log.warn("订单详情不存在,ID: {}", itemDTO.getId());
                continue;
            }
            // 更新备注
            orderDetail.setRemarks(itemDTO.getRemarks());
            orderDetail.setUpdatedAt(LocalDateTime.now());
            int detailUpdated = orderDetailMapper.updateById(orderDetail);
            if (detailUpdated <= 0) {
                log.warn("更新订单详情失败,ID: {}", itemDTO.getId());
            }
        }
        return true;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean checkFiles(FileCheckDTO fileCheckDTO) {
        // 参数校验
        if (fileCheckDTO == null) {
            throw new BusinessException("文件核查参数不能为空");
        }
        if (!StringUtils.hasText(fileCheckDTO.getOrderId())) {
            throw new BusinessException("订单ID不能为空");
        }
        if (fileCheckDTO.getIsApprove() == null) {
            throw new BusinessException("审批结果不能为空");
        }
        if (fileCheckDTO.getApproverId() == null) {
            throw new BusinessException("审批人ID不能为空");
        }
        if (!StringUtils.hasText(fileCheckDTO.getApproverName())) {
            throw new BusinessException("审批人姓名不能为空");
        }
        // 查询订单信息
        OrderInfo orderInfo = this.getById(fileCheckDTO.getOrderId());
        if (orderInfo == null) {
            throw new BusinessException("订单不存在");
        }
        // 检查订单状态是否为"待授权"
        if (!"待授权".equals(orderInfo.getOrderStatus())) {
            throw new BusinessException("订单状态不正确,当前状态为:" + orderInfo.getOrderStatus());
        }
        // 更新订单状态
        if (fileCheckDTO.getIsApprove()) {
            // 通过:更新为"待交易确认"
            orderInfo.setOrderStatus("待交易确认");
        } else {
            // 驳回:更新为"待上传文件"(需要重新上传)
            orderInfo.setOrderStatus("待上传文件");
        }
        orderInfo.setUpdatedAt(LocalDateTime.now());
        // 保存订单
        int updated = this.baseMapper.updateById(orderInfo);
        if (updated <= 0) {
            throw new BusinessException("更新订单状态失败");
        }
        log.info("文件核查完成,订单ID: {}, 审批结果: {}, 审批人: {}, 审批意见: {}",
                fileCheckDTO.getOrderId(),
                fileCheckDTO.getIsApprove() ? "通过" : "驳回",
                fileCheckDTO.getApproverName(),
                fileCheckDTO.getApprovalOpinion());
        return true;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean approveOrder(OrderApprovalDTO orderApprovalDTO) {
        // 参数校验
        if (orderApprovalDTO == null) {
            throw new BusinessException("审批参数不能为空");
        }
        if (!StringUtils.hasText(orderApprovalDTO.getOrderId())) {
            throw new BusinessException("订单ID不能为空");
        }
        if (!StringUtils.hasText(orderApprovalDTO.getApprovalOpinion())) {
            throw new BusinessException("审批意见不能为空");
        }
        if (orderApprovalDTO.getApproverId() == null) {
            throw new BusinessException("审批人ID不能为空");
        }
        if (!StringUtils.hasText(orderApprovalDTO.getApproverName())) {
            throw new BusinessException("审批人姓名不能为空");
        }
        if (!StringUtils.hasText(orderApprovalDTO.getApprovalType())) {
            throw new BusinessException("审批类型不能为空");
        }
        if (!StringUtils.hasText(orderApprovalDTO.getApprovalResult())) {
            throw new BusinessException("审批结果不能为空");
        }
        // 查询订单信息
        OrderInfo orderInfo = this.getById(orderApprovalDTO.getOrderId());
        if (orderInfo == null) {
            throw new BusinessException("订单不存在");
        }
        // 创建审批记录
        OrderApproval orderApproval = new OrderApproval();
        orderApproval.setOrderId(orderApprovalDTO.getOrderId());
        orderApproval.setApprovalStep("审批授权");
        orderApproval.setApprovalType(orderApprovalDTO.getApprovalType());
        orderApproval.setApprovalResult(orderApprovalDTO.getApprovalResult());
        orderApproval.setApproverId(orderApprovalDTO.getApproverId());
        orderApproval.setApproverName(orderApprovalDTO.getApproverName());
        orderApproval.setApprovalOpinion(orderApprovalDTO.getApprovalOpinion());
        orderApproval.setApprovalTime(LocalDateTime.now());
        // 插入审批记录
        int insertResult = orderApprovalMapper.insert(orderApproval);
        if (insertResult <= 0) {
            throw new BusinessException("插入审批记录失败");
        }
        log.info("审批记录添加成功,订单ID: {}, 审批类型: {}, 审批结果: {}, 审批人: {}, 审批意见: {}",
                orderApprovalDTO.getOrderId(),
                orderApprovalDTO.getApprovalType(),
                orderApprovalDTO.getApprovalResult(),
                orderApprovalDTO.getApproverName(),
                orderApprovalDTO.getApprovalOpinion());
        return true;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateOrderStatusToNext(String orderId) {
        // 参数校验
        if (!StringUtils.hasText(orderId)) {
            throw new BusinessException("订单ID不能为空");
        }
        // 查询订单信息
        OrderInfo orderInfo = this.getById(orderId);
        if (orderInfo == null) {
            throw new BusinessException("订单不存在");
        }
        // 获取当前状态
        String currentStatus = orderInfo.getOrderStatus();
        if (!StringUtils.hasText(currentStatus)) {
            throw new BusinessException("订单当前状态为空");
        }
        // 获取下一个状态
        String nextStatus = getNextOrderStatus(currentStatus);
        if (nextStatus == null) {
            throw new BusinessException("当前状态 " + currentStatus + " 已是最终状态,无法继续流转");
        }
        // 更新订单状态
        orderInfo.setOrderStatus(nextStatus);
        orderInfo.setUpdatedAt(LocalDateTime.now());
        int updated = this.baseMapper.updateById(orderInfo);
        if (updated <= 0) {
            throw new BusinessException("更新订单状态失败");
        }
        log.info("订单状态更新成功,订单ID: {}, 从 {} 更新为 {}", orderId, currentStatus, nextStatus);
        return true;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateOrderStatusToPrevious(String orderId) {
        // 参数校验
        if (!StringUtils.hasText(orderId)) {
            throw new BusinessException("订单ID不能为空");
        }
        // 查询订单信息
        OrderInfo orderInfo = this.getById(orderId);
        if (orderInfo == null) {
            throw new BusinessException("订单不存在");
        }
        // 获取当前状态
        String currentStatus = orderInfo.getOrderStatus();
        if (!StringUtils.hasText(currentStatus)) {
            throw new BusinessException("订单当前状态为空");
        }
        // 获取上一个状态
        String previousStatus = getPreviousOrderStatus(currentStatus);
        if (previousStatus == null) {
            throw new BusinessException("当前状态 " + currentStatus + " 已是初始状态,无法回退");
        }
        // 更新订单状态
        orderInfo.setOrderStatus(previousStatus);
        orderInfo.setUpdatedAt(LocalDateTime.now());
        int updated = this.baseMapper.updateById(orderInfo);
        if (updated <= 0) {
            throw new BusinessException("更新订单状态失败");
        }
        log.info("订单状态更新成功,订单ID: {}, 从 {} 回退为 {}", orderId, currentStatus, previousStatus);
        return true;
    }
    /**
     * 根据订单明细确定初始订单状态
     * @param items 订单明细列表
     * @return 初始状态
     */
    private String determineInitialOrderStatus(List<CreateOrderItemDTO> items) {
        // 检查是否有价格类型为"协议"的明细项
        boolean hasAgreementPrice = items.stream()
                .anyMatch(item -> "协议".equals(item.getPriceType()));
        // 如果有协议价格,从"待上传文件"开始,否则从"待授权"开始
        String initialStatus = hasAgreementPrice ? "待上传文件" : "待授权";
        log.info("订单初始状态确定为: {}, 是否包含协议价格: {}", initialStatus, hasAgreementPrice);
        return initialStatus;
    }
    /**
     * 获取下一个订单状态
     * @param currentStatus 当前状态
     * @return 下一个状态,如果是最终状态则返回null
     */
    private String getNextOrderStatus(String currentStatus) {
        switch (currentStatus) {
            case "待上传文件":
                return "待授权";
            case "待授权":
                return "待交易确认";
            case "待交易确认":
                return "已完成";
            case "已完成":
                return "已评价";
            case "已评价":
                return null; // 最终状态
            default:
                throw new BusinessException("未知的订单状态:" + currentStatus);
        }
    }
    /**
     * 获取上一个订单状态
     * @param currentStatus 当前状态
     * @return 上一个状态,如果是初始状态则返回null
     */
    private String getPreviousOrderStatus(String currentStatus) {
        switch (currentStatus) {
            case "待上传文件":
                return null; // 初始状态
            case "待授权":
                return "待上传文件";
            case "待交易确认":
                return "待授权";
            case "已完成":
                return "待交易确认";
            case "已评价":
                return "已完成";
            default:
                throw new BusinessException("未知的订单状态:" + currentStatus);
        }
    }
    @Override
    public boolean hasAgreementPriceType(String orderId) {
        // 参数校验
        if (!StringUtils.hasText(orderId)) {
            throw new BusinessException("订单ID不能为空");
        }
        // 查询订单详情,检查是否有价格类型为"协议"的子订单
        QueryWrapper<OrderDetail> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("order_id", orderId);
        queryWrapper.eq("price_type", "协议");
        queryWrapper.eq("deleted", 0);
        List<OrderDetail> agreementDetails = orderDetailMapper.selectList(queryWrapper);
        boolean hasAgreement = !CollectionUtils.isEmpty(agreementDetails);
        log.info("检查订单协议类型,订单ID: {}, 是否包含协议类型: {}", orderId, hasAgreement);
        return hasAgreement;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateOrderDetailRemarksOnly(UpdateOrderDetailDTO.UpdateOrderDetailRemarksOnlyDTO updateOrderDetailDTO) {
        // 参数校验
        if (updateOrderDetailDTO == null) {
            throw new BusinessException("更新参数不能为空");
        }
        if (!StringUtils.hasText(updateOrderDetailDTO.getOrderId())) {
            throw new BusinessException("订单ID不能为空");
        }
        if (CollectionUtils.isEmpty(updateOrderDetailDTO.getOrderDetails())) {
            throw new BusinessException("订单详情列表不能为空");
        }
        // 查询订单信息(验证订单存在)
        OrderInfo orderInfo = this.getById(updateOrderDetailDTO.getOrderId());
        if (orderInfo == null) {
            throw new BusinessException("订单不存在");
        }
        // 只更新订单详情备注,不更新订单状态
        for (UpdateOrderDetailDTO.UpdateOrderDetailItemDTO itemDTO : updateOrderDetailDTO.getOrderDetails()) {
            if (itemDTO.getId() == null) {
                continue;
            }
            OrderDetail orderDetail = orderDetailMapper.selectById(itemDTO.getId());
            if (orderDetail == null) {
                log.warn("订单详情不存在,ID: {}", itemDTO.getId());
                continue;
            }
            // 更新备注
            orderDetail.setRemarks(itemDTO.getRemarks());
            orderDetail.setUpdatedAt(LocalDateTime.now());
            int detailUpdated = orderDetailMapper.updateById(orderDetail);
            if (detailUpdated <= 0) {
                log.warn("更新订单详情失败,ID: {}", itemDTO.getId());
            }
        }
        log.info("订单详情备注更新成功,订单ID: {}", updateOrderDetailDTO.getOrderId());
        return true;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteOrderAttachment(Long attachmentId) {
        // 参数校验
        if (attachmentId == null) {
            throw new BusinessException("附件ID不能为空");
        }
        log.info("开始删除订单附件,附件ID: {}", attachmentId);
        // 查询附件信息
        OrderAttachment attachment = orderAttachmentMapper.selectById(attachmentId);
        if (attachment == null) {
            log.error("附件不存在,附件ID: {}", attachmentId);
            throw new BusinessException("附件不存在");
        }
        log.info("查询到附件信息: ID={}, 文件名={}, 对象名称={}, 当前删除状态={}",
                attachment.getId(), attachment.getFileName(), attachment.getObjectName(), attachment.getDeleted());
        try {
            // 1. 删除MinIO中的文件
            if (StringUtils.hasText(attachment.getObjectName())) {
                log.info("开始删除MinIO文件,对象名称: {}", attachment.getObjectName());
                minioService.deleteFile(attachment.getObjectName());
                log.info("MinIO文件删除成功,对象名称: {}", attachment.getObjectName());
            } else {
                log.warn("附件对象名称为空,跳过MinIO文件删除");
            }
            // 2. 删除数据库中的附件记录(逻辑删除)
            log.info("开始逻辑删除数据库记录,附件ID: {}", attachmentId);
            // 使用MyBatis-Plus的逻辑删除方法
            int result = orderAttachmentMapper.deleteById(attachmentId);
            log.info("数据库逻辑删除结果: 影响行数={}", result);
            if (result > 0) {
                log.info("订单附件删除成功,附件ID: {}, 文件名: {}", attachmentId, attachment.getFileName());
                return true;
            } else {
                log.error("数据库更新失败,影响行数为0,附件ID: {}", attachmentId);
                throw new BusinessException("删除附件记录失败");
            }
        } catch (Exception e) {
            log.error("删除订单附件失败,附件ID: {}", attachmentId, e);
            throw new BusinessException("删除订单附件失败:" + e.getMessage());
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean cancelOrder(String orderId) {
        // 参数校验
        if (!StringUtils.hasText(orderId)) {
            throw new BusinessException("订单ID不能为空");
        }
        log.info("开始取消订单,订单ID: {}", orderId);
        // 查询订单信息
        OrderInfo orderInfo = this.getById(orderId);
        if (orderInfo == null) {
            throw new BusinessException("订单不存在");
        }
        // 检查订单状态,只有"已完成"状态前的订单才能取消
        String currentStatus = orderInfo.getOrderStatus();
        if ("已完成".equals(currentStatus) || "已评价".equals(currentStatus)) {
            throw new BusinessException("已完成或已评价的订单不能取消");
        }
        try {
            // 1. 删除订单附件(包括MinIO文件和数据库记录)
            log.info("开始删除订单附件,订单ID: {}", orderId);
            List<OrderAttachment> attachments = orderAttachmentMapper.selectByOrderId(orderId);
            for (OrderAttachment attachment : attachments) {
                try {
                    // 删除MinIO中的文件
                    if (StringUtils.hasText(attachment.getObjectName())) {
                        log.info("删除MinIO文件,对象名称: {}", attachment.getObjectName());
                        minioService.deleteFile(attachment.getObjectName());
                    }
                    // 删除数据库记录
                    orderAttachmentMapper.deleteById(attachment.getId());
                    log.info("删除附件记录成功,附件ID: {}", attachment.getId());
                } catch (Exception e) {
                    log.error("删除附件失败,附件ID: {}, 错误: {}", attachment.getId(), e.getMessage());
                    // 继续删除其他附件,不中断整个流程
                }
            }
            // 2. 逻辑删除订单详情
            log.info("开始逻辑删除订单详情,订单ID: {}", orderId);
            // 先查询订单详情列表,然后逐个逻辑删除
            List<OrderDetail> orderDetails = orderDetailMapper.selectByOrderId(orderId);
            int detailDeleted = 0;
            for (OrderDetail detail : orderDetails) {
                int result = orderDetailMapper.deleteById(detail.getId());
                if (result > 0) {
                    detailDeleted++;
                }
            }
            log.info("逻辑删除订单详情完成,影响行数: {}", detailDeleted);
            // 3. 删除订单信息(逻辑删除)
            log.info("开始删除订单信息,订单ID: {}", orderId);
            int orderDeleted = this.baseMapper.deleteById(orderId);
            log.info("删除订单信息完成,影响行数: {}", orderDeleted);
            if (orderDeleted > 0) {
                log.info("订单取消成功,订单ID: {}", orderId);
                return true;
            } else {
                log.error("删除订单信息失败,影响行数为0,订单ID: {}", orderId);
                throw new BusinessException("删除订单信息失败");
            }
        } catch (Exception e) {
            log.error("取消订单失败,订单ID: {}", orderId, e);
            throw new BusinessException("取消订单失败:" + e.getMessage());
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateWorkflowId(String orderId, String workflowId) {
        if (!StringUtils.hasText(orderId)) {
            throw new BusinessException("订单ID不能为空");
        }
        if (!StringUtils.hasText(workflowId)) {
            throw new BusinessException("工作流ID不能为空");
        }
        OrderInfo orderInfo = this.getById(orderId);
        if (orderInfo == null) {
            throw new BusinessException("订单不存在");
        }
        orderInfo.setWorkflowId(workflowId);
        orderInfo.setUpdatedAt(LocalDateTime.now());
        return this.updateById(orderInfo);
    }
}