rk
昨天 ab9cd2c82bd64de8e33510db1d1e78a5b3b4de70
server/services/src/main/java/com/doumee/service/business/impl/OrdersServiceImpl.java
@@ -8,14 +8,16 @@
import com.doumee.biz.system.OperationConfigBiz;
import com.doumee.biz.system.SystemDictDataBiz;
import com.doumee.config.wx.WxMiniConfig;
import com.doumee.config.wx.WxMiniUtilService;
import com.doumee.config.wx.WxPayProperties;
import com.doumee.config.wx.WxPayV3Service;
import com.wechat.pay.java.service.refund.model.Refund;
import com.doumee.core.constants.Constants;
import com.doumee.core.constants.ResponseStatus;
import com.doumee.core.exception.BusinessException;
import com.doumee.core.model.PageData;
import com.doumee.core.model.PageWrap;
import com.doumee.core.utils.DateUtil;
import com.doumee.core.utils.Tencent.MapUtil;
import com.doumee.core.utils.geocode.MapUtil;
import com.doumee.core.utils.Utils;
import com.doumee.dao.business.*;
import com.doumee.dao.business.model.*;
@@ -30,11 +32,12 @@
import com.doumee.dao.dto.MyOrderDTO;
import com.doumee.dao.dto.OrderItemDTO;
import com.doumee.dao.vo.*;
import com.doumee.service.business.NoticeService;
import com.doumee.service.business.OrderLogService;
import com.doumee.service.business.OrdersService;
import com.doumee.dao.business.model.Notice;
import com.doumee.service.business.AreasService;
import com.doumee.service.business.PricingRuleService;
import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.xiaoymin.knife4j.core.util.CollectionUtils;
@@ -45,17 +48,11 @@
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
 * 寄存订单信息Service实现
@@ -104,8 +101,7 @@
    @Autowired
    private RevenueMapper revenueMapper;
    @Autowired
    private WxMiniUtilService wxMiniUtilService;
    @Autowired
    private SystemUserMapper systemUserMapper;
@@ -127,6 +123,15 @@
    @Autowired
    private AreasService areasService;
    @Autowired
    private NoticeService noticeService;
    @Autowired
    private WxPayV3Service wxPayV3Service;
    @Autowired
    private WxPayProperties wxPayProperties;
    @Override
    public Integer create(Orders orders) {
@@ -413,7 +418,7 @@
        // 1. 调用腾讯地图距离矩阵API计算驾车距离
        String from = dto.getFromLat() + "," + dto.getFromLgt();
        String to = dto.getToLat() + "," + dto.getToLgt();
        JSONObject distanceResult = MapUtil.distanceSingle("driving", from, to);
        JSONObject distanceResult = MapUtil.direction("driving", from, to);
        BigDecimal distance = distanceResult.getBigDecimal("distance");
        // distance 单位为米,转为公里
        BigDecimal distanceKm = distance.divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP);
@@ -518,16 +523,17 @@
        // 5. 加急费用:物品价格 × 加急系数(字典 URGENT_COEFFICIENT)
        long urgentFeeFen = 0L;
        if (Boolean.TRUE.equals(dto.getUrgent())) {
            String urgentRateStr = systemDictDataBiz.queryByCode(
                    Constants.OPERATION_CONFIG, Constants.OP_URGENT_COEFFICIENT).getCode();
            BigDecimal urgentRate = new BigDecimal(urgentRateStr);
            urgentFeeFen = new BigDecimal(itemPriceTotal).multiply(urgentRate)
                    .setScale(0, RoundingMode.HALF_UP).longValue();
        }
        // 6. 总价格 = 物品价格 + 保价费用 + 加急费用
        long totalPrice = itemPriceTotal + insuranceFeeFen + urgentFeeFen;
        // 6. 总价格 = 物品价格 + 保价费用 + 加急费用(加急时才包含加急费)
        long totalPrice = itemPriceTotal + insuranceFeeFen;
        if (Boolean.TRUE.equals(dto.getUrgent())) {
            totalPrice += urgentFeeFen;
        }
        PriceCalculateVO result = new PriceCalculateVO();
        result.setItemList(itemList);
@@ -666,9 +672,9 @@
                takeLocationValue = takeShop.getAddress();
            } else if (dto.getTakeLat() != null && dto.getTakeLgt() != null && StringUtils.isNotBlank(dto.getTakeLocation())) {
                // 无取件门店,校验存件点与自选取件点是否在同一城市
                if (MapUtil.isSameCity(depositShop.getLatitude(), depositShop.getLongitude(),
                if (!MapUtil.isSameCity(depositShop.getLatitude(), depositShop.getLongitude(),
                        dto.getTakeLat().doubleValue(), dto.getTakeLgt().doubleValue())) {
                    throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "异地寄存订单存取点不能在同一城市,如需同城寄存请选择就近门店");
                    throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "异地寄存订单存取点不在同一城市,如需请选择同城门店");
                }
                takeLat = dto.getTakeLat();
                takeLgt = dto.getTakeLgt();
@@ -836,7 +842,8 @@
        if (member == null || StringUtils.isBlank(member.getOpenid())) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "用户信息异常,无法发起支付");
        }
        PayResponse payResponse = wxPay(orders, member.getOpenid(), Constants.OrdersAttach.STORAGE_ORDER);
        PayResponse payResponse = wxPayV3(orders.getOutTradeNo(), orders.getTotalAmount(), orders.getId(),
                member.getOpenid(), Constants.OrdersAttach.STORAGE_ORDER);
        payResponse.setLockKey(lockKey);
        return payResponse;
    }
@@ -871,7 +878,8 @@
        if (member == null || StringUtils.isBlank(member.getOpenid())) {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "用户信息异常,无法发起支付");
        }
        return wxPay(orders, member.getOpenid(), Constants.OrdersAttach.STORAGE_ORDER);
        return wxPayV3(orders.getOutTradeNo(), orders.getTotalAmount(), orders.getId(),
                member.getOpenid(), Constants.OrdersAttach.STORAGE_ORDER);
    }
    /**
@@ -906,7 +914,32 @@
        }
    }
    /**
     * 唤起微信支付V3
     *
     * @param outTradeNo   商户订单号
     * @param totalCents   支付金额(分)
     * @param orderId      订单主键
     * @param openid       用户微信openid
     * @param ordersAttach 订单支付类型
     * @return PayResponse 包含微信调起参数和订单主键
     */
    private PayResponse wxPayV3(String outTradeNo, Long totalCents, Integer orderId,
                                String openid, Constants.OrdersAttach ordersAttach) {
        Map<String, String> payParams = wxPayV3Service.createOrder(
                outTradeNo,
                ordersAttach.getName(),
                totalCents != null ? totalCents : 0L,
                openid,
                wxPayProperties.getV3NotifyUrl(),
                ordersAttach.getKey()
        );
        PayResponse payResponse = new PayResponse();
        payResponse.setResponse(payParams);
        payResponse.setOrderId(orderId);
        return payResponse;
    }
@@ -1240,14 +1273,17 @@
        // 寄件门店占比:fieldA=0(企业寄)/1(个人寄)
        int depositFieldA = Constants.equalsInteger(depositShop.getCompanyType(), Constants.ONE) ? Constants.ZERO : Constants.ONE;
        BigDecimal depositShopRata = getRevenueShareRata(cityId, depositFieldA);
        // 取件门店占比:fieldA=2(企业取)/3(个人取)
        // 取件门店占比:无取件门店时比例为0
        BigDecimal takeShopRata = BigDecimal.ZERO;
        if (takeShop != null) {
        int takeFieldA = Constants.equalsInteger(takeShop.getCompanyType(), Constants.ONE) ? Constants.TWO : Constants.THREE;
        BigDecimal takeShopRata = getRevenueShareRata(cityId, takeFieldA);
            takeShopRata = getRevenueShareRata(cityId, takeFieldA);
        }
        // 计算薪酬(分):totalAmount 为分,rata 为比例值(如 0.15 表示 15%)
        long driverFee = new BigDecimal(totalAmount).multiply(driverRata).longValue();
        long depositShopFee = new BigDecimal(totalAmount).multiply(depositShopRata).longValue();
        long takeShopFee = totalAmount - driverFee - depositShopFee;
        long takeShopFee = new BigDecimal(totalAmount).multiply(takeShopRata).longValue();
        orders.setDriverFee(driverFee);
        orders.setDepositShopFee(depositShopFee);
@@ -1664,10 +1700,11 @@
            refund.setCreateTime(now);
            refund.setDeleted(Constants.ZERO);
            // 调用微信退款,全额退款
            String refundCode = wxMiniUtilService.wxRefund(order.getOutTradeNo(), order.getPayAmount(), order.getPayAmount());
            refund.setRefundCode(refundCode);
            refund.setRefundTime(new Date());
            // 调用微信退款V3,全额退款
            Refund refundResult = wxPayV3Service.refund(order.getOutTradeNo(), order.getPayAmount(), order.getPayAmount(),
                    "订单退款", wxPayProperties.getV3RefundNotifyUrl());
            refund.setRefundCode(refundResult.getOutRefundNo());
            refund.setStatus(Constants.ZERO); // 退款中
            ordersRefundMapper.insert(refund);
            order.setStatus(Constants.OrderStatus.cancelled.getStatus());
@@ -1676,6 +1713,9 @@
            ordersMapper.updateById(order);
            saveCancelLog(order, "会员取消订单(待寄存,全额退款)", reason, memberId);
            // 通知会员:退款中
            sendOrderNotice(memberId, Constants.MemberOrderNotify.REFUNDING, orderId,
                    "orderNo", order.getCode());
            return;
        }
@@ -1686,6 +1726,11 @@
            order.setCancelTime(now);
            ordersMapper.updateById(order);
            saveCancelLog(order, "会员申请取消订单(已寄存/已接单)", reason, memberId);
            // 通知存件门店:退款申请
            if (order.getDepositShopId() != null) {
                sendShopNotice(order.getDepositShopId(), Constants.ShopOrderNotify.REFUNDING, orderId,
                        "orderNo", order.getCode());
            }
            return;
        }
@@ -1724,6 +1769,52 @@
        log.setCreateTime(new Date());
        log.setDeleted(Constants.ZERO);
        orderLogService.create(log);
    }
    /**
     * 发送订单站内信通知
     */
    private void sendOrderNotice(Integer memberId, Constants.MemberOrderNotify notify, Integer orderId, String... params) {
        Notice notice = new Notice();
        notice.setUserType(0); // 0=会员
        notice.setUserId(memberId);
        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);
    }
    /**
     * 发送门店站内信通知
     */
    private void sendShopNotice(Integer shopId, Constants.ShopOrderNotify notify, Integer orderId, String... params) {
        Notice notice = new Notice();
        notice.setUserType(2); // 2=门店
        notice.setUserId(shopId);
        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);
    }
    /**
     * 通知存件门店和取件门店(订单完成/评价等)
     */
    private void notifyBothShops(Orders order, Constants.ShopOrderNotify notify, String... params) {
        if (order.getDepositShopId() != null) {
            sendShopNotice(order.getDepositShopId(), notify, order.getId(), params);
        }
        if (order.getTakeShopId() != null) {
            sendShopNotice(order.getTakeShopId(), notify, order.getId(), params);
        }
    }
    @Override
@@ -1767,6 +1858,17 @@
            }
        }
        ordersMapper.updateById(order);
        // 通知会员:订单待核验
        sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.WAIT_VERIFY, order.getId(),
                "orderNo", order.getCode(),
                "storeCode", order.getMemberVerifyCode());
        // 就地寄存订单:通知存件门店待核验
        if (Constants.ZERO.equals(order.getType()) && order.getDepositShopId() != null) {
            sendShopNotice(order.getDepositShopId(), Constants.ShopOrderNotify.WAIT_VERIFY, order.getId(),
                    "orderNo", order.getCode());
        }
    }
    @Override
@@ -1810,8 +1912,9 @@
        otherOrders.setCreateTime(now);
        otherOrdersMapper.insert(otherOrders);
        // 5. 唤起微信支付
        return wxPayForOtherOrder(otherOrders, member.getOpenid(), Constants.OrdersAttach.OVERDUE_FEE);
        // 5. 唤起微信支付V3
        return wxPayV3(otherOrders.getOutTradeNo(), otherOrders.getPayAccount(), otherOrders.getId(),
                member.getOpenid(), Constants.OrdersAttach.OVERDUE_FEE);
    }
    @Override
@@ -1910,8 +2013,9 @@
        otherOrders.setCreateTime(now);
        otherOrdersMapper.insert(otherOrders);
        // 5. 唤起微信支付
        return wxPayForOtherOrder(otherOrders, member.getOpenid(), Constants.OrdersAttach.SHOP_DEPOSIT);
        // 5. 唤起微信支付V3
        return wxPayV3(otherOrders.getOutTradeNo(), otherOrders.getPayAccount(), otherOrders.getId(),
                member.getOpenid(), Constants.OrdersAttach.SHOP_DEPOSIT);
    }
    @Override
@@ -2048,6 +2152,12 @@
                    }
                }
            }
            // 通知相关门店:订单已结算
            notifyBothShops(order, Constants.ShopOrderNotify.SETTLED,
                    "orderNo", order.getCode(),
                    "amount", String.valueOf(Constants.getFormatMoney(
                            order.getTotalAmount() != null ? order.getTotalAmount() : 0L)));
        }
    }
@@ -2144,6 +2254,14 @@
        if (isRemote && order.getAcceptDriver() != null) {
            updateTargetScore(Constants.THREE, order.getAcceptDriver());
        }
        // 通知会员:订单已评价
        sendOrderNotice(memberId, Constants.MemberOrderNotify.EVALUATED, order.getId(),
                "orderNo", order.getCode());
        // 通知存件门店和取件门店:订单已评价
        notifyBothShops(order, Constants.ShopOrderNotify.EVALUATED,
                "orderNo", order.getCode());
    }
    /**
@@ -2246,6 +2364,16 @@
            saveVerifyImages(order.getId(), images, Constants.FileType.ORDER_DEPOSIT.getKey(), shopId);
            // 记录订单日志
            saveShopVerifyLog(order, "门店确认寄存", "门店【" + shopName + "】确认寄存", remark, shopId);
            // 通知会员:门店核销成功
            if (Constants.equalsInteger(order.getType(), Constants.ONE)) {
                // 异地寄存 → 待抢单
                sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.WAIT_GRAB, order.getId(),
                        "orderNo", order.getCode());
            } else {
                // 就地寄存 → 待取件提醒
                sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.WAIT_PICKUP_REMIND, order.getId(),
                        "orderNo", order.getCode());
            }
        } else if (Constants.equalsInteger(status, Constants.OrderStatus.arrived.getStatus())) {
            // 异地寄存 + 无取件门店 → 无法核销(客户自取,无门店操作)
            if (Constants.equalsInteger(order.getType(), Constants.ONE) && order.getTakeShopId() == null) {
@@ -2268,6 +2396,14 @@
            generateRevenueRecords(order.getId());
            // 记录订单日志
            saveShopVerifyLog(order, "门店确认取件", "门店【" + shopName + "】确认取件,订单完成", remark, shopId);
            // 通知会员:订单已完成
            sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.FINISHED, order.getId(),
                    "orderNo", order.getCode());
            // 通知存件门店和取件门店:订单已完成
            String settleDays = operationConfigBiz.getConfig().getSettlementDate();
            notifyBothShops(order, Constants.ShopOrderNotify.FINISHED,
                    "orderNo", order.getCode(),
                    "settleDays", settleDays != null ? settleDays : "7");
        } else {
            throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "当前订单状态不允许核销");
        }
@@ -2344,13 +2480,14 @@
            refundRecord.setDeleted(Constants.ZERO);
            ordersRefundMapper.insert(refundRecord);
            // 调用微信退款(放在最后,确保前置操作全部成功)
            String refundCode = wxMiniUtilService.wxRefund(
                    order.getOutTradeNo(), order.getPayAmount(), order.getRefundAmount());
            // 调用微信退款V3(放在最后,确保前置操作全部成功)
            Refund refundResult = wxPayV3Service.refund(
                    order.getOutTradeNo(), order.getPayAmount(), order.getRefundAmount(),
                    "订单退款", wxPayProperties.getV3RefundNotifyUrl());
            // 退款成功后回填退款单号和时间
            refundRecord.setRefundCode(refundCode);
            refundRecord.setRefundTime(new Date());
            // 退款成功后回填退款单号,标记退款中
            refundRecord.setRefundCode(refundResult.getOutRefundNo());
            refundRecord.setStatus(Constants.ZERO); // 退款中
            ordersRefundMapper.updateById(refundRecord);
        }
@@ -2364,6 +2501,14 @@
            logInfo += ",退款" + Constants.getFormatMoney(order.getRefundAmount()) + "元";
        }
        saveShopVerifyLog(order, "门店确认出库", logInfo, remark, shopId);
        // 通知会员:订单已完成
        sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.FINISHED, order.getId(),
                "orderNo", order.getCode());
        // 通知存件门店和取件门店:订单已完成
        String settleDays = operationConfigBiz.getConfig().getSettlementDate();
        notifyBothShops(order, Constants.ShopOrderNotify.FINISHED,
                "orderNo", order.getCode(),
                "settleDays", settleDays != null ? settleDays : "7");
    }
    @Override
@@ -2401,6 +2546,15 @@
        // 7. 生成收益记录
        calculateAndSaveOrderFees(orderId);
        generateRevenueRecords(orderId);
        // 通知会员:订单已完成
        sendOrderNotice(memberId, Constants.MemberOrderNotify.FINISHED, orderId,
                "orderNo", order.getCode());
        // 通知存件门店和取件门店:订单已完成
        String settleDays = operationConfigBiz.getConfig().getSettlementDate();
        notifyBothShops(order, Constants.ShopOrderNotify.FINISHED,
                "orderNo", order.getCode(),
                "settleDays", settleDays != null ? settleDays : "7");
    }
    @Override
@@ -2552,6 +2706,26 @@
        // 保存附件(obj_type=3 门店入库图片,最多3张)
        saveVerifyImages(order.getId(), images, Constants.FileType.ORDER_TAKE.getKey(), driverId);
        // 通知会员:订单已送达
        String destination = order.getTakeShopAddress() != null ? order.getTakeShopAddress() : "";
        if (order.getMemberVerifyCode() != null) {
            sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.ARRIVED_HAS_SHOP, order.getId(),
                    "orderNo", order.getCode(),
                    "destination", destination,
                    "pickupCode", order.getMemberVerifyCode());
        } else {
            sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.ARRIVED_NO_SHOP, order.getId(),
                    "orderNo", order.getCode(),
                    "destination", destination);
        }
        // 通知取件门店:订单已送达
        if (order.getTakeShopId() != null) {
            sendShopNotice(order.getTakeShopId(), Constants.ShopOrderNotify.ARRIVED, order.getId(),
                    "orderNo", order.getCode(),
                    "destination", destination);
        }
    }
    /**
@@ -2953,7 +3127,7 @@
        // 腾讯地图距离矩阵API计算实际距离
        String from = fromLat + "," + fromLng;
        String to = toLat + "," + toLng;
        JSONObject distanceResult = MapUtil.distanceSingle("driving", from, to);
        JSONObject distanceResult = MapUtil.direction("driving", from, to);
        // 获取距离(米),转公里
        int distanceMeters = distanceResult.getIntValue("distance");