rk
4 天以前 2ab42edae9d271f5f464b7be475e217752dacb38
server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java
@@ -294,7 +294,7 @@
            return BigDecimal.ZERO;
        }
        String rateStr = systemDictDataBiz.queryByCode(Constants.OPERATION_CONFIG, Constants.OP_INSURANCE_RATE).getCode();
        BigDecimal rate = new BigDecimal(rateStr);
        BigDecimal rate = new BigDecimal(rateStr).divide(new BigDecimal("100"), 4, BigDecimal.ROUND_HALF_UP);
        return declaredValue.multiply(rate).setScale(2, BigDecimal.ROUND_HALF_UP);
    }
@@ -342,9 +342,11 @@
        List<Category> categories = categoryMapper.selectBatchIds(categoryIds);
        Map<Integer, String> categoryNameMap = new HashMap<>();
        Map<Integer, String> categoryDetailMap = new HashMap<>();
        Map<Integer, String> categoryOtherFieldMap = new HashMap<>();
        for (Category c : categories) {
            categoryNameMap.put(c.getId(), c.getName());
            categoryDetailMap.put(c.getId(), c.getDetail());
            categoryOtherFieldMap.put(c.getId(),c.getOtherField());
        }
        // 计算每项物品费用:小计 = 单价 × 数量 × 天数
@@ -364,7 +366,7 @@
            ItemPriceVO vo = new ItemPriceVO();
            vo.setCategoryId(item.getCategoryId());
            vo.setCategoryName(categoryNameMap.getOrDefault(item.getCategoryId(), ""));
            vo.setDetail(categoryDetailMap.get(item.getCategoryId()));
            vo.setDetail(categoryOtherFieldMap.get(item.getCategoryId()));
            vo.setQuantity(item.getQuantity());
            vo.setUnitPrice(unitPrice);
            vo.setLocallyPrice(unitPrice);
@@ -420,8 +422,11 @@
        String to = dto.getToLat() + "," + dto.getToLgt();
        JSONObject distanceResult = MapUtil.direction("driving", from, to);
        BigDecimal distance = distanceResult.getBigDecimal("distance");
        // distance 单位为米,转为公里
        // distance 单位为米,转为公里(不足1公里按1公里计算)
        BigDecimal distanceKm = distance.divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP);
        if (distanceKm.compareTo(BigDecimal.ONE) < 0) {
            distanceKm = BigDecimal.ONE;
        }
        // 收集所有物品类型ID
        List<Integer> categoryIds = new ArrayList<>();
@@ -459,9 +464,11 @@
        List<Category> categories = categoryMapper.selectBatchIds(categoryIds);
        Map<Integer, String> categoryNameMap = new HashMap<>();
        Map<Integer, String> categoryDetailMap = new HashMap<>();
        Map<Integer, String> categoryOtherFieldMap = new HashMap<>();
        for (Category c : categories) {
            categoryNameMap.put(c.getId(), c.getName());
            categoryDetailMap.put(c.getId(), c.getDetail());
            categoryOtherFieldMap.put(c.getId(),c.getOtherField());
        }
        // 3. 逐项计算运费:起步价 + 超出部分阶梯价
@@ -500,7 +507,7 @@
            ItemPriceVO vo = new ItemPriceVO();
            vo.setCategoryId(item.getCategoryId());
            vo.setCategoryName(categoryNameMap.getOrDefault(item.getCategoryId(), ""));
            vo.setDetail(categoryDetailMap.get(item.getCategoryId()));
            vo.setDetail(categoryOtherFieldMap.get(item.getCategoryId()));
            vo.setQuantity(item.getQuantity());
            vo.setUnitPrice(unitPrice);
            vo.setLocallyPrice(locallyPrice);
@@ -645,8 +652,11 @@
        // ========== 3. 查询寄件店铺信息 ==========
        ShopInfo depositShop = shopInfoMapper.selectById(dto.getDepositShopId());
        if (depositShop == null) {
        if (depositShop == null || Constants.equalsInteger(depositShop.getDeleted(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "寄件店铺不存在");
        }
        if (depositShop.getStatus() == null || !Constants.equalsInteger(depositShop.getStatus(), Constants.ZERO)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "寄件店铺已停业,请选择其他门店");
        }
        // ========== 4. 计算费用 ==========
@@ -663,9 +673,15 @@
            }
            // 取件点:店铺 or 自选点,至少提供一组
            if (dto.getTakeShopId() != null) {
                if (dto.getTakeShopId().equals(dto.getDepositShopId())) {
                    throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "异地寄存订单存件门店和取件门店不能相同");
                }
                takeShop = shopInfoMapper.selectById(dto.getTakeShopId());
                if (takeShop == null) {
                if (takeShop == null || Constants.equalsInteger(takeShop.getDeleted(), Constants.ONE)) {
                    throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "取件店铺不存在");
                }
                if (takeShop.getStatus() == null || !Constants.equalsInteger(takeShop.getStatus(), Constants.ONE)) {
                    throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "取件店铺已停业,请选择其他门店");
                }
                takeLat = BigDecimal.valueOf(takeShop.getLatitude());
                takeLgt = BigDecimal.valueOf(takeShop.getLongitude());
@@ -764,6 +780,7 @@
            orders.setTakeLat(takeLat);
            orders.setTakeLgt(takeLgt);
            orders.setIsUrgent(dto.getIsUrgent());
            orders.setDistance(priceResult.getDistance());
        } else {
            // 就地:取件点同寄件店铺
            orders.setTakeShopId(dto.getDepositShopId());
@@ -775,7 +792,12 @@
        // 物品信息
        orders.setGoodType(dto.getGoodType());
        orders.setGoodLevel(goodTypeCategory.getRelationId());
        // 查询物品级别 type = 3
        Category levelCategory = categoryMapper.selectById(goodTypeCategory.getRelationId());
        if(Objects.isNull(levelCategory)){
            throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(),"未查询到物品等级信息:{}"+goodTypeCategory.getName());
        }
        orders.setGoodLevel(Integer.valueOf(levelCategory.getDetail()));
        // 拼接物品信息:物品类型名称、尺寸名称*数量(数组字符串)
        List<String> goodsParts = new ArrayList<>();
        for (ItemPriceVO itemVO : priceResult.getItemList()) {
@@ -797,7 +819,6 @@
        }
        orders.setDeclaredFee(priceResult.getInsuranceFee());
        orders.setPrice(priceResult.getItemPrice());
        // 薪酬计算与占比存储
        calculateAndSetFeeAllocation(orders, depositShop, takeShop);
@@ -1172,9 +1193,9 @@
                item.setLuggageName(d.getLuggageName());
                item.setLuggageDetail(d.getLuggageDetail());
                item.setNum(d.getNum());
                double unitPriceYuan = d.getUnitPrice() != null ? Constants.getFormatMoney(d.getUnitPrice()) : 0;
                item.setUnitPriceYuan(unitPriceYuan);
                item.setSubtotal(unitPriceYuan * (d.getNum() != null ? d.getNum() : 0));
                long unitPriceFen = d.getUnitPrice() != null ? d.getUnitPrice() : 0L;
                item.setUnitPrice(unitPriceFen);
                item.setSubtotal(unitPriceFen * (d.getNum() != null ? d.getNum() : 0));
                items.add(item);
            }
        }
@@ -1307,8 +1328,9 @@
                .eq(PricingRule::getCityId, cityId)
                .eq(PricingRule::getFieldA, String.valueOf(fieldA))
                .last("limit 1"));
        if (rule != null && StringUtils.isNotBlank(rule.getFieldC())) {
            return new BigDecimal(rule.getFieldC());
        if (rule != null && StringUtils.isNotBlank(rule.getFieldB())) {
            // fieldB 存储的是百分比整数(如15表示15%),转换为小数比例(0.15)
            return new BigDecimal(rule.getFieldB()).divide(new BigDecimal("100"), 4, BigDecimal.ROUND_HALF_UP);
        }
        return BigDecimal.ZERO;
    }
@@ -1357,16 +1379,22 @@
                vo.setCode(o.getCode());
                vo.setType(o.getType());
                vo.setStatus(o.getStatus());
                vo.setStatusName(Constants.OrderStatus.getDescByKey(o.getStatus(),
                        Constants.equalsInteger(o.getType(), Constants.ZERO)?o.getType():Objects.nonNull(o.getTakeShopId())?Constants.ONE:Constants.TWO)
                );
                vo.setCreateTime(o.getCreateTime());
                vo.setExpectedTakeTime(o.getExpectedTakeTime());
                vo.setMemberVerifyCode(o.getMemberVerifyCode());
                // 存件门店(关联查询直接取值)
                vo.setDepositShopId(o.getDepositShopId());
                vo.setDepositShopName(o.getDepositShopName());
                vo.setDepositShopLinkName(o.getDepositShopLinkName());
                vo.setDepositShopPhone(o.getDepositShopLinkPhone());
                // 取件信息:有取件门店取门店,无则取用户自选取件点
                if (o.getTakeShopId() != null) {
                    vo.setTakeShopId(o.getTakeShopId());
                    vo.setTakeShopName(o.getTakeShopName());
                    vo.setTakeShopAddress(o.getTakeShopAddress());
                } else {
@@ -1381,6 +1409,9 @@
                // 费用(分)
                vo.setDeclaredFee(o.getDeclaredFee());
                vo.setEstimatedAmount(o.getEstimatedAmount());
                // 评价状态
                vo.setCommentStatus(o.getCommentStatus());
                // 查询物品明细(一次查询,同时用于物品列表和逾期计算)
                List<OrdersDetail> details = ordersDetailMapper.selectList(
@@ -1454,6 +1485,9 @@
                vo.setCode(o.getCode());
                vo.setType(o.getType());
                vo.setStatus(o.getStatus());
                vo.setStatusName(Constants.OrderStatus.getDescByKey(o.getStatus(),
                        Constants.equalsInteger(o.getType(), Constants.ZERO)?o.getType():Objects.nonNull(o.getTakeShopId())?Constants.ONE:Constants.TWO)
                );
                vo.setCreateTime(o.getCreateTime());
                vo.setExpectedTakeTime(o.getExpectedTakeTime());
@@ -1669,11 +1703,6 @@
            throw new BusinessException(ResponseStatus.DATA_EMPTY);
        }
        // 仅异地寄存可取消
        if (!Constants.equalsInteger(order.getType(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "仅异地寄存订单可取消");
        }
        Integer status = order.getStatus();
        if (status == null) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "订单状态异常");
@@ -1681,7 +1710,7 @@
        Date now = new Date();
        // 待支付:直接取消
        // 待支付:直接取消(不限订单类型)
        if (Constants.equalsInteger(status, Constants.OrderStatus.waitPay.getStatus())) {
            order.setStatus(Constants.OrderStatus.cancelled.getStatus());
            order.setCancelTime(now);
@@ -1690,7 +1719,7 @@
            return;
        }
        // 待寄存:直接取消,全额退款
        // 待寄存:直接取消,全额退款(不限订单类型)
        if (Constants.equalsInteger(status, Constants.OrderStatus.waitDeposit.getStatus())) {
            // 记录退款信息
            OrdersRefund refund = new OrdersRefund();
@@ -1719,6 +1748,11 @@
            return;
        }
        // 已寄存/已接单:仅异地寄存可取消
        if (!Constants.equalsInteger(order.getType(), Constants.ONE)) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "仅异地寄存订单可取消");
        }
        // 已寄存/已接单:进入取消中状态
        if (Constants.equalsInteger(status, Constants.OrderStatus.deposited.getStatus())
                || Constants.equalsInteger(status, Constants.OrderStatus.accepted.getStatus())) {
@@ -1729,6 +1763,11 @@
            // 通知存件门店:退款申请
            if (order.getDepositShopId() != null) {
                sendShopNotice(order.getDepositShopId(), Constants.ShopOrderNotify.REFUNDING, orderId,
                        "orderNo", order.getCode());
            }
            // 通知司机:订单退款中(已接单情况下司机需停止服务)
            if (order.getAcceptDriver() != null && Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.accepted.getStatus())) {
                sendDriverNotice(order.getAcceptDriver(), Constants.DriverOrderNotify.REFUNDING, orderId,
                        "orderNo", order.getCode());
            }
            return;
@@ -1817,6 +1856,27 @@
        }
    }
    /**
     * 发送司机站内信通知
     */
    private void sendDriverNotice(Integer driverId, Constants.DriverOrderNotify notify, Integer orderId, String... params) {
        DriverInfo driver = driverInfoMapper.selectById(driverId);
        if (driver == null || driver.getMemberId() == null) {
            return;
        }
        Notice notice = new Notice();
        notice.setUserType(1); // 1=司机
        notice.setUserId(driver.getMemberId());
        notice.setTitle(notify.getTitle());
        notice.setContent(notify.format(params));
        notice.setObjId(orderId);
        notice.setObjType(0); // 0=订单
        notice.setStatus(0);  // 0=未读
        notice.setIsdeleted(Constants.ZERO);
        notice.setCreateDate(new Date());
        noticeService.create(notice);
    }
    @Override
    @Transactional(rollbackFor = {Exception.class, BusinessException.class})
    public void handleStorageOrderPayNotify(String outTradeNo, String wxTradeNo) {
@@ -1836,6 +1896,7 @@
        order.setPayStatus(Constants.ONE); // 已支付
        order.setPayTime(now);
        order.setWxExternalNo(wxTradeNo);
        order.setPayAmount(order.getTotalAmount());
        order.setUpdateTime(now);
        // 生成会员核销码
        order.setMemberVerifyCode(generateVerifyCode());
@@ -2159,6 +2220,14 @@
                    "orderNo", order.getCode(),
                    "amount", String.valueOf(Constants.getFormatMoney(
                            order.getTotalAmount() != null ? order.getTotalAmount() : 0L)));
            // 通知司机:订单已结算
            if (order.getAcceptDriver() != null) {
                sendDriverNotice(order.getAcceptDriver(), Constants.DriverOrderNotify.SETTLED, order.getId(),
                        "orderNo", order.getCode(),
                        "amount", String.valueOf(Constants.getFormatMoney(
                                order.getDriverFee() != null ? order.getDriverFee() : 0L)));
            }
        }
    }
@@ -2263,6 +2332,12 @@
        // 通知存件门店和取件门店:订单已评价
        notifyBothShops(order, Constants.ShopOrderNotify.EVALUATED,
                "orderNo", order.getCode());
        // 通知司机:订单已评价
        if (order.getAcceptDriver() != null) {
            sendDriverNotice(order.getAcceptDriver(), Constants.DriverOrderNotify.EVALUATED, order.getId(),
                    "orderNo", order.getCode());
        }
    }
    /**
@@ -2405,6 +2480,12 @@
            notifyBothShops(order, Constants.ShopOrderNotify.FINISHED,
                    "orderNo", order.getCode(),
                    "settleDays", settleDays != null ? settleDays : "7");
            // 通知司机:订单已完成
            if (order.getAcceptDriver() != null) {
                sendDriverNotice(order.getAcceptDriver(), Constants.DriverOrderNotify.FINISHED, order.getId(),
                        "orderNo", order.getCode(),
                        "settleDays", settleDays != null ? settleDays : "7");
            }
        } else {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "当前订单状态不允许核销");
        }
@@ -2510,6 +2591,12 @@
        notifyBothShops(order, Constants.ShopOrderNotify.FINISHED,
                "orderNo", order.getCode(),
                "settleDays", settleDays != null ? settleDays : "7");
        // 通知司机:订单已完成
        if (order.getAcceptDriver() != null) {
            sendDriverNotice(order.getAcceptDriver(), Constants.DriverOrderNotify.FINISHED, order.getId(),
                    "orderNo", order.getCode(),
                    "settleDays", settleDays != null ? settleDays : "7");
        }
    }
    @Override
@@ -2556,6 +2643,12 @@
        notifyBothShops(order, Constants.ShopOrderNotify.FINISHED,
                "orderNo", order.getCode(),
                "settleDays", settleDays != null ? settleDays : "7");
        // 通知司机:订单已完成
        if (order.getAcceptDriver() != null) {
            sendDriverNotice(order.getAcceptDriver(), Constants.DriverOrderNotify.FINISHED, order.getId(),
                    "orderNo", order.getCode(),
                    "settleDays", settleDays != null ? settleDays : "7");
        }
    }
    @Override
@@ -3122,6 +3215,61 @@
    }
    @Override
    public ActiveOrderTipVO getActiveOrderTip(Integer memberId) {
        // 查询状态为 0~5 的最新一条订单
        QueryWrapper<Orders> wrapper = new QueryWrapper<>();
        wrapper.eq("MEMBER_ID", memberId)
                .in("STATUS", 0, 1, 2, 3, 4, 5)
                .orderByDesc("CREATE_TIME")
                .last("LIMIT 1");
        Orders order = ordersMapper.selectOne(wrapper);
        if (order == null) {
            return null;
        }
        ActiveOrderTipVO vo = new ActiveOrderTipVO();
        vo.setOrderId(order.getId());
        vo.setStatus(order.getStatus());
        // 构建提示文案
        boolean isLocal = Constants.equalsInteger(order.getType(), Constants.ZERO);
        Integer status = order.getStatus();
        String tip = null;
        if (Constants.equalsInteger(status, Constants.OrderStatus.waitPay.getStatus())) {
            // 待支付:提示支付倒计时
            String minutes = "";
            try {
                minutes = operationConfigBiz.getConfig().getAutoCancelTime();
            } catch (Exception ignored) {}
            tip = "请在" + (StringUtils.isNotBlank(minutes) ? minutes : "") + "分钟内完成支付,超时订单将自动取消";
            // 计算支付倒计时
            if (StringUtils.isNotBlank(minutes) && order.getCreateTime() != null) {
                long timeoutMs = Long.parseLong(minutes) * 60 * 1000;
                long deadline = order.getCreateTime().getTime() + timeoutMs;
                long remain = deadline - System.currentTimeMillis();
                vo.setPayCountdown(remain > 0 ? remain : -1L);
            } else {
                vo.setPayCountdown(-1L);
            }
        } else if (Constants.equalsInteger(status, Constants.OrderStatus.waitDeposit.getStatus())) {
            tip = "订单已支付,请前往门店寄存";
        } else if (Constants.equalsInteger(status, Constants.OrderStatus.deposited.getStatus())) {
            tip = isLocal ? "行李已寄存,请凭取件码前往指定门店取件" : "门店已接单,正在为您安排取件司机";
        } else if (Constants.equalsInteger(status, Constants.OrderStatus.accepted.getStatus())) {
            tip = isLocal ? "行李已寄存,请凭取件码前往指定门店取件" : "已有司机抢单,正前往取件地点";
        } else if (Constants.equalsInteger(status, Constants.OrderStatus.delivering.getStatus())) {
            tip = "司机已取件,正运往目的地";
        } else if (Constants.equalsInteger(status, Constants.OrderStatus.arrived.getStatus())) {
            tip = "行李已送达服务点,请及时前往取件";
        }
        vo.setTip(tip);
        return vo;
    }
    @Override
    public EstimatedDeliveryResultVO calculateEstimatedDelivery(Integer cityId,
                                                                Double fromLat, Double fromLng,
                                                                Double toLat, Double toLng) {