| | |
| | | package com.doumee.service.business.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.doumee.biz.system.AreasBiz; |
| | | import com.doumee.config.xyy.XyyConfig; |
| | | import com.doumee.config.xyy.dto.PrintRequest; |
| | | import com.doumee.biz.system.OperationConfigBiz; |
| | | import com.doumee.biz.system.SystemDictDataBiz; |
| | | import com.doumee.config.wx.WxMiniConfig; |
| | | import com.doumee.config.wx.WxPayProperties; |
| | | import com.doumee.config.wx.WxPayV3Service; |
| | | import com.doumee.core.utils.jpush.JPushUtil; |
| | | 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.model.PageData; |
| | | import com.doumee.core.model.PageWrap; |
| | | import com.doumee.core.utils.DateUtil; |
| | | import com.doumee.core.utils.GeoUtils; |
| | | import com.doumee.core.utils.ID; |
| | | import com.doumee.core.utils.geocode.MapUtil; |
| | | import com.doumee.core.utils.Utils; |
| | | import com.doumee.core.utils.aliyun.AliSmsService; |
| | |
| | | import com.doumee.dao.dto.CommentOrderDTO; |
| | | import com.doumee.dao.dto.CreateOrderDTO; |
| | | import com.doumee.dao.dto.DispatchDTO; |
| | | import com.doumee.dao.dto.HandleOrderExceptionDTO; |
| | | import com.doumee.dao.dto.ManualRefundDTO; |
| | | import com.doumee.dao.dto.MyOrderDTO; |
| | | import com.doumee.dao.dto.OrderItemDTO; |
| | | import com.doumee.dao.vo.*; |
| | |
| | | import com.github.xiaoymin.knife4j.core.util.CollectionUtils; |
| | | import com.github.yulichang.wrapper.MPJLambdaWrapper; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.util.*; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | |
| | | @Autowired |
| | | private RevenueMapper revenueMapper; |
| | | |
| | | @Autowired |
| | | private RewardRecordMapper rewardRecordMapper; |
| | | |
| | | |
| | | |
| | | @Autowired |
| | |
| | | private RedisTemplate<String, Object> redisTemplate; |
| | | |
| | | @Autowired |
| | | private MemberCouponMapper memberCouponMapper; |
| | | |
| | | @Autowired |
| | | private AreasBiz areasBiz; |
| | | |
| | | @Autowired |
| | |
| | | |
| | | @Autowired |
| | | private AreasService areasService; |
| | | |
| | | @Autowired |
| | | private AreasMapper aareasMapper; |
| | | |
| | | @Autowired |
| | | private NoticeService noticeService; |
| | |
| | | |
| | | @Autowired |
| | | private WxPayProperties wxPayProperties; |
| | | |
| | | @Autowired |
| | | private XyyConfig xyyConfig; |
| | | |
| | | @Autowired |
| | | private PrintService printService; |
| | | |
| | | @Override |
| | | public Integer create(Orders orders) { |
| | |
| | | .leftJoin(Category.class, Category::getId, Orders::getGoodType) |
| | | .leftJoin(DriverInfo.class, DriverInfo::getId, Orders::getAcceptDriver) |
| | | .leftJoin("shop_info s1 on s1.id = t.DEPOSIT_SHOP_ID") |
| | | .leftJoin("shop_info s2 on s2.id = t.TAKE_SHOP_ID"); |
| | | .leftJoin("shop_info s2 on s2.id = t.TAKE_SHOP_ID") ; |
| | | ; |
| | | Utils.MP.blankToNull(pageWrap.getModel()); |
| | | pageWrap.getModel().setDeleted(Constants.ZERO); |
| | | queryWrapper.eq(pageWrap.getModel().getDeleted() != null, Orders::getDeleted, pageWrap.getModel().getDeleted()); |
| | | queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getCode()), Orders::getCode, pageWrap.getModel().getCode()); |
| | | queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getDepositShopName()), "s1.name", pageWrap.getModel().getDepositShopName()); |
| | | queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getTakeShopName()), "s2.name", pageWrap.getModel().getTakeShopName()); |
| | | queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getGoodsInfo()), Orders::getGoodsInfo, pageWrap.getModel().getGoodsInfo()); |
| | | queryWrapper.ge(pageWrap.getModel().getCreateStartTime() != null, Orders::getCreateTime, Utils.Date.getStart(pageWrap.getModel().getCreateStartTime())); |
| | | queryWrapper.ge(pageWrap.getModel().getCreateStartTime() != null, Orders::getCreateTime, pageWrap.getModel().getCreateStartTime()); |
| | | queryWrapper.le(pageWrap.getModel().getCreateEndTime() != null, Orders::getCreateTime, Utils.Date.getEnd(pageWrap.getModel().getCreateEndTime())); |
| | | queryWrapper.eq(pageWrap.getModel().getDepositShopId() != null, Orders::getDepositShopId, pageWrap.getModel().getDepositShopId()); |
| | | queryWrapper.eq(pageWrap.getModel().getType() != null, Orders::getType, pageWrap.getModel().getType()); |
| | |
| | | queryWrapper.and(pageWrap.getModel().getDriverKeyword() != null, i->i.like(DriverInfo::getName, pageWrap.getModel().getDriverKeyword()) |
| | | .or().like(DriverInfo::getTelephone, pageWrap.getModel().getDriverKeyword())); |
| | | queryWrapper.eq(pageWrap.getModel().getSettlementStatus() != null, Orders::getSettlementStatus, pageWrap.getModel().getSettlementStatus()); |
| | | for (PageWrap.SortData sortData : pageWrap.getSorts()) { |
| | | if (sortData.getDirection().equalsIgnoreCase(PageWrap.DESC)) { |
| | | queryWrapper.orderByDesc(sortData.getProperty()); |
| | | } else { |
| | | queryWrapper.orderByAsc(sortData.getProperty()); |
| | | queryWrapper.eq(pageWrap.getModel().getAcceptDriver() != null, Orders::getAcceptDriver, pageWrap.getModel().getAcceptDriver()); |
| | | queryWrapper.and(pageWrap.getModel().getShopId() != null, i -> i.eq(Orders::getDepositShopId, pageWrap.getModel().getShopId()) |
| | | .or().eq(Orders::getTakeShopId, pageWrap.getModel().getShopId())); |
| | | queryWrapper.orderByDesc(Orders::getId); |
| | | PageData<Orders> pageData = PageData.from(ordersMapper.selectJoinPage(page, Orders.class, queryWrapper)); |
| | | for (Orders o : pageData.getRecords()) { |
| | | if (o.getStatus() != null) { |
| | | Constants.OrderStatus os = Constants.OrderStatus.getByKey(o.getStatus()); |
| | | o.setStatusDesc(os != null ? os.getValue() : ""); |
| | | } |
| | | if(Constants.equalsInteger(o.getIsUrgent(),Constants.ZERO)){ |
| | | o.setUrgentAmount(Constants.ZERO.longValue()); |
| | | } |
| | | // 实付金额 = 支付金额 - 退款金额 + 逾期费用 |
| | | long pay = o.getPayAmount() != null ? o.getPayAmount() : 0L; |
| | | long refund = o.getRefundAmount() != null ? o.getRefundAmount() : 0L; |
| | | long overdue = o.getOverdueAmount() != null ? o.getOverdueAmount() : 0L; |
| | | o.setPayAmount(pay - refund + overdue); |
| | | } |
| | | return PageData.from(ordersMapper.selectJoinPage(page, Orders.class, queryWrapper)); |
| | | return pageData; |
| | | } |
| | | |
| | | @Override |
| | |
| | | queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getDepositShopName()), "s1.name", pageWrap.getModel().getDepositShopName()); |
| | | queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getTakeShopName()), "s2.name", pageWrap.getModel().getTakeShopName()); |
| | | queryWrapper.like(StringUtils.isNotBlank(pageWrap.getModel().getGoodsInfo()), Orders::getGoodsInfo, pageWrap.getModel().getGoodsInfo()); |
| | | queryWrapper.ge(pageWrap.getModel().getCreateStartTime() != null, Orders::getCreateTime, Utils.Date.getStart(pageWrap.getModel().getCreateStartTime())); |
| | | queryWrapper.le(pageWrap.getModel().getCreateEndTime() != null, Orders::getCreateTime, Utils.Date.getEnd(pageWrap.getModel().getCreateEndTime())); |
| | | queryWrapper.ge(pageWrap.getModel().getCreateStartTime() != null, Orders::getCreateTime, pageWrap.getModel().getCreateStartTime()); |
| | | queryWrapper.le(pageWrap.getModel().getCreateEndTime() != null, Orders::getCreateTime, pageWrap.getModel().getCreateEndTime()); |
| | | queryWrapper.eq(pageWrap.getModel().getDepositShopId() != null, Orders::getDepositShopId, pageWrap.getModel().getDepositShopId()); |
| | | queryWrapper.eq(pageWrap.getModel().getType() != null, Orders::getType, pageWrap.getModel().getType()); |
| | | queryWrapper.eq(pageWrap.getModel().getStatus() != null, Orders::getStatus, pageWrap.getModel().getStatus()); |
| | |
| | | queryWrapper.and(pageWrap.getModel().getDriverKeyword() != null, i->i.like(DriverInfo::getName, pageWrap.getModel().getDriverKeyword()) |
| | | .or().like(DriverInfo::getTelephone, pageWrap.getModel().getDriverKeyword())); |
| | | queryWrapper.eq(pageWrap.getModel().getSettlementStatus() != null, Orders::getSettlementStatus, pageWrap.getModel().getSettlementStatus()); |
| | | queryWrapper.eq(pageWrap.getModel().getAcceptDriver() != null, Orders::getAcceptDriver, pageWrap.getModel().getAcceptDriver()); |
| | | queryWrapper.and(pageWrap.getModel().getShopId() != null, i -> i.eq(Orders::getDepositShopId, pageWrap.getModel().getShopId()) |
| | | .or().eq(Orders::getTakeShopId, pageWrap.getModel().getShopId())); |
| | | |
| | | queryWrapper.select( |
| | | "IFNULL(SUM(t.total_amount), 0) as total_amount_sum", |
| | |
| | | for (Category c : categories) { |
| | | categoryNameMap.put(c.getId(), c.getName()); |
| | | categoryDetailMap.put(c.getId(), c.getDetail()); |
| | | categoryOtherFieldMap.put(c.getId(),c.getOtherField()); |
| | | categoryOtherFieldMap.put(c.getId(),c.getRemark()); |
| | | } |
| | | |
| | | // 计算每项物品费用:小计 = 单价 × 数量 × 天数 |
| | |
| | | result.setTotalPrice(totalPrice); |
| | | result.setDays(days); |
| | | result.setUrgentFee(0L); |
| | | resolveCoupon(result, dto.getMemberId(), dto.getCouponId(), totalPrice); |
| | | return result; |
| | | } |
| | | |
| | |
| | | for (Category c : categories) { |
| | | categoryNameMap.put(c.getId(), c.getName()); |
| | | categoryDetailMap.put(c.getId(), c.getDetail()); |
| | | categoryOtherFieldMap.put(c.getId(),c.getOtherField()); |
| | | categoryOtherFieldMap.put(c.getId(),c.getRemark()); |
| | | } |
| | | |
| | | // 3. 逐项计算运费:起步价 + 超出部分阶梯价 |
| | | List<ItemPriceVO> itemList = new ArrayList<>(); |
| | | long itemPriceTotal = 0L; |
| | | long maxExtraFeeTotal = 0L; // 最大超出距离费用(单价×数量) |
| | | |
| | | for (OrderItemDTO item : dto.getItems()) { |
| | | PricingRule rule = ruleMap.get(String.valueOf(item.getCategoryId())); |
| | |
| | | long extraPricePerUnit = Long.parseLong(rule.getFieldE()); |
| | | |
| | | // 阶梯计价:距离 ≤ 起步距离取起步价,超出按 ceil(超出距离/单位) × 单价累加 |
| | | long extraFee = 0L; |
| | | long unitPrice; |
| | | if (distanceKm.compareTo(startDistance) <= 0) { |
| | | unitPrice = startPrice; |
| | | } else { |
| | | BigDecimal extraKm = distanceKm.subtract(startDistance); |
| | | BigDecimal extraCount = extraKm.divide(extraDistanceUnit, 0, RoundingMode.CEILING); |
| | | unitPrice = startPrice + extraCount.longValue() * extraPricePerUnit; |
| | | extraFee = extraCount.longValue() * extraPricePerUnit; |
| | | unitPrice = startPrice + extraFee; |
| | | } |
| | | |
| | | long subtotal = unitPrice * item.getQuantity(); |
| | |
| | | vo.setExtraPrice(extraPricePerUnit); |
| | | itemList.add(vo); |
| | | |
| | | itemPriceTotal += subtotal; |
| | | // 所有物品的起步价×数量 累加 |
| | | itemPriceTotal += startPrice * item.getQuantity(); |
| | | // 记录最大的超出距离费用 |
| | | long extraFeeTotal = extraFee * item.getQuantity(); |
| | | if (extraFeeTotal > maxExtraFeeTotal) { |
| | | maxExtraFeeTotal = extraFeeTotal; |
| | | } |
| | | } |
| | | |
| | | // 多物品时只加最大的超出距离费用 |
| | | itemPriceTotal += maxExtraFeeTotal; |
| | | |
| | | // 4. 保价费用:报价金额 × 保价费率(字典 INSURANCE_RATE),元→分(保价金额>0时计费) |
| | | long insuranceFeeFen = 0L; |
| | |
| | | insuranceFeeFen = insuranceFeeYuan.multiply(new BigDecimal(100)).longValue(); |
| | | } |
| | | |
| | | // 5. 加急费用:物品价格 × 加急系数(字典 URGENT_COEFFICIENT) |
| | | // 5. 加急费用:物品价格 × (加急系数-1),如150%表示增加50% |
| | | long urgentFeeFen = 0L; |
| | | String urgentRateStr = systemDictDataBiz.queryByCode( |
| | | Constants.OPERATION_CONFIG, Constants.OP_URGENT_COEFFICIENT).getCode(); |
| | | BigDecimal urgentRate = new BigDecimal(urgentRateStr); |
| | | urgentFeeFen = new BigDecimal(itemPriceTotal).multiply(urgentRate) |
| | | BigDecimal urgentIncreaseRate = urgentRate.subtract(BigDecimal.ONE); |
| | | urgentFeeFen = new BigDecimal(itemPriceTotal).multiply(urgentIncreaseRate) |
| | | .setScale(0, RoundingMode.HALF_UP).longValue(); |
| | | |
| | | // 6. 总价格 = 物品价格 + 保价费用 + 加急费用(加急时才包含加急费) |
| | | long totalPrice = itemPriceTotal + insuranceFeeFen; |
| | | if (Boolean.TRUE.equals(dto.getUrgent())) { |
| | | totalPrice += urgentFeeFen; |
| | | // 加急时更新物品明细单价和小计 |
| | | for (ItemPriceVO vo : itemList) { |
| | | long adjustedUnitPrice = new BigDecimal(vo.getUnitPrice()).multiply(urgentRate) |
| | | .setScale(0, RoundingMode.HALF_UP).longValue(); |
| | | vo.setUnitPrice(adjustedUnitPrice); |
| | | vo.setSubtotal(adjustedUnitPrice * vo.getQuantity()); |
| | | } |
| | | } |
| | | |
| | | PriceCalculateVO result = new PriceCalculateVO(); |
| | |
| | | } |
| | | } |
| | | |
| | | resolveCoupon(result, dto.getMemberId(), dto.getCouponId(), totalPrice); |
| | | return result; |
| | | } |
| | | |
| | | private void resolveCoupon(PriceCalculateVO result, Integer memberId, Integer couponId, long totalPrice) { |
| | | if (memberId == null) { |
| | | result.setDeductionAmount(0L); |
| | | return; |
| | | } |
| | | // 查询可用优惠券:已领取、未过期、满足满额门槛 |
| | | Date now = new Date(); |
| | | List<MemberCoupon> availableCoupons = memberCouponMapper.selectList(new QueryWrapper<MemberCoupon>().lambda() |
| | | .eq(MemberCoupon::getMemberId, memberId) |
| | | .eq(MemberCoupon::getStatus, Constants.CouponStatus.claimed.getKey()) |
| | | .eq(MemberCoupon::getIsdeleted, Constants.ZERO) |
| | | .le(MemberCoupon::getLimitPrice, totalPrice) |
| | | .ge(MemberCoupon::getEndDate, now) |
| | | .orderByDesc(MemberCoupon::getPrice) |
| | | .orderByAsc(MemberCoupon::getEndDate)); |
| | | result.setAvailableCoupons(availableCoupons); |
| | | |
| | | if (couponId == null) { |
| | | result.setDeductionAmount(0L); |
| | | return; |
| | | } |
| | | |
| | | MemberCoupon selected; |
| | | if (couponId == -1) { |
| | | selected = availableCoupons.isEmpty() ? null : availableCoupons.get(0); |
| | | } else { |
| | | selected = availableCoupons.stream() |
| | | .filter(mc -> mc.getId().equals(couponId)) |
| | | .findFirst().orElse(null); |
| | | if (selected == null) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "优惠券无效或不可用"); |
| | | } |
| | | } |
| | | |
| | | if (selected != null) { |
| | | result.setDeductionAmount(selected.getPrice()); |
| | | result.setSelectedCoupon(selected); |
| | | } else { |
| | | result.setDeductionAmount(0L); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | |
| | | // 无取件门店,校验存件点与自选取件点是否在同一城市 |
| | | 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(); |
| | |
| | | priceDTO.setDepositEndTime(takeTime); |
| | | priceDTO.setItems(dto.getItems()); |
| | | priceDTO.setDeclaredAmount(dto.getDeclaredAmount()); |
| | | priceDTO.setMemberId(memberId); |
| | | priceDTO.setCouponId(dto.getCouponId()); |
| | | priceResult = calculateLocalPrice(priceDTO); |
| | | } else { |
| | | // 异地寄存 |
| | |
| | | priceDTO.setItems(dto.getItems()); |
| | | priceDTO.setDeclaredAmount(dto.getDeclaredAmount()); |
| | | priceDTO.setUrgent(Constants.ONE.equals(dto.getIsUrgent())); |
| | | priceDTO.setMemberId(memberId); |
| | | priceDTO.setCouponId(dto.getCouponId()); |
| | | priceResult = calculateRemotePrice(priceDTO); |
| | | } |
| | | |
| | |
| | | orders.setDeleted(Constants.ZERO); |
| | | orders.setCreateTime(now); |
| | | orders.setUpdateTime(now); |
| | | orders.setIsConverted(Constants.ZERO); |
| | | |
| | | // 寄件信息 |
| | | orders.setDepositShopId(dto.getDepositShopId()); |
| | |
| | | // 异地:取件点信息 |
| | | orders.setTakeShopId(dto.getTakeShopId()); |
| | | orders.setTakeLocation(takeLocationValue); |
| | | orders.setTakeLocationRemark(takeLocationValue); |
| | | orders.setTakeLat(takeLat); |
| | | orders.setTakeLgt(takeLgt); |
| | | orders.setIsUrgent(dto.getIsUrgent()); |
| | |
| | | // 就地:取件点同寄件店铺 |
| | | orders.setTakeShopId(dto.getDepositShopId()); |
| | | orders.setTakeLocation(depositShop.getAddress()); |
| | | orders.setTakeLocationRemark(depositShop.getAddress()); |
| | | orders.setTakeLat(BigDecimal.valueOf(depositShop.getLatitude())); |
| | | orders.setTakeLgt(BigDecimal.valueOf(depositShop.getLongitude())); |
| | | orders.setIsUrgent(Constants.ZERO); |
| | |
| | | // 费用信息(分) |
| | | orders.setBasicAmount(priceResult.getItemPrice()); |
| | | orders.setEstimatedAmount(priceResult.getTotalPrice()); |
| | | orders.setTotalAmount(priceResult.getTotalPrice()); |
| | | long deductionAmount = priceResult.getDeductionAmount() != null ? priceResult.getDeductionAmount() : 0L; |
| | | orders.setTotalAmount(priceResult.getTotalPrice() - deductionAmount); |
| | | orders.setUrgentAmount(priceResult.getUrgentFee()); |
| | | orders.setCouponId(dto.getCouponId()); |
| | | orders.setDeductionAmount(deductionAmount); |
| | | orders.setManualRefund(Constants.ZERO); |
| | | // 存储加急系数 |
| | | if (Constants.ONE.equals(dto.getType()) && Constants.ONE.equals(dto.getIsUrgent())) { |
| | | String urgentRateStr = systemDictDataBiz.queryByCode( |
| | | Constants.OPERATION_CONFIG, Constants.OP_URGENT_COEFFICIENT).getCode(); |
| | | orders.setUrgentRata(new BigDecimal(urgentRateStr)); |
| | | } |
| | | if (dto.getDeclaredAmount() != null && dto.getDeclaredAmount().compareTo(BigDecimal.ZERO) > 0) { |
| | | orders.setDeclaredAmount(dto.getDeclaredAmount().multiply(new BigDecimal(100)).longValue()); |
| | | } else { |
| | |
| | | ordersMapper.insert(orders); |
| | | Integer orderId = orders.getId(); |
| | | |
| | | // 标记优惠券已使用 |
| | | MemberCoupon selectedCoupon = priceResult.getSelectedCoupon(); |
| | | if (selectedCoupon != null) { |
| | | memberCouponMapper.update(new UpdateWrapper<MemberCoupon>().lambda() |
| | | .set(MemberCoupon::getStatus, Constants.CouponStatus.used.getKey()) |
| | | .set(MemberCoupon::getUseDate, new Date()) |
| | | .set(MemberCoupon::getOrderId, orderId) |
| | | .eq(MemberCoupon::getId, selectedCoupon.getId())); |
| | | } |
| | | |
| | | // 创建订单日志 |
| | | OrderLog createLog = new OrderLog(); |
| | | createLog.setOrderId(orderId); |
| | | createLog.setTitle(Constants.OrderLogType.createOrder.getTitle()); |
| | | createLog.setLogInfo(Constants.OrderLogType.createOrder.format(orderCode)); |
| | | createLog.setObjType(Constants.OrderLogType.createOrder.getStatus()); |
| | | createLog.setOrderStatus(orders.getStatus()); |
| | | createLog.setOptUserId(memberId); |
| | | createLog.setOptUserType(0); |
| | | createLog.setCreateTime(now); |
| | | createLog.setDeleted(Constants.ZERO); |
| | | orderLogService.create(createLog); |
| | | |
| | | // ========== 7. 创建订单明细 ========== |
| | | // 异地寄存极速达时,按加急比例计算明细行单价 |
| | | BigDecimal urgentIncreaseRate = null; |
| | | if (Constants.ONE.equals(dto.getType()) && Constants.ONE.equals(dto.getIsUrgent())) { |
| | | String urgentRateStr = systemDictDataBiz.queryByCode( |
| | | Constants.OPERATION_CONFIG, Constants.OP_URGENT_COEFFICIENT).getCode(); |
| | | urgentIncreaseRate = new BigDecimal(urgentRateStr).subtract(BigDecimal.ONE); |
| | | } |
| | | for (ItemPriceVO itemVO : priceResult.getItemList()) { |
| | | OrdersDetail detail = new OrdersDetail(); |
| | | detail.setOrderId(orderId); |
| | |
| | | detail.setLuggageName(itemVO.getCategoryName()); |
| | | detail.setLuggageDetail(itemVO.getDetail()); |
| | | detail.setNum(itemVO.getQuantity()); |
| | | detail.setUnitPrice(itemVO.getUnitPrice()); |
| | | long detailUnitPrice = itemVO.getUnitPrice(); |
| | | if (urgentIncreaseRate != null) { |
| | | detailUnitPrice = new BigDecimal(detailUnitPrice) |
| | | .multiply(BigDecimal.ONE.add(urgentIncreaseRate)) |
| | | .setScale(0, RoundingMode.HALF_UP).longValue(); |
| | | } |
| | | detail.setUnitPrice(detailUnitPrice); |
| | | detail.setStartDistance(itemVO.getStartDistance()); |
| | | detail.setStartPrice(itemVO.getStartPrice()); |
| | | if (urgentIncreaseRate != null && itemVO.getStartPrice() != null) { |
| | | detail.setStartPrice(new BigDecimal(itemVO.getStartPrice()) |
| | | .multiply(BigDecimal.ONE.add(urgentIncreaseRate)) |
| | | .setScale(0, RoundingMode.HALF_UP).longValue()); |
| | | } else { |
| | | detail.setStartPrice(itemVO.getStartPrice()); |
| | | } |
| | | detail.setExtraDistance(itemVO.getExtraDistance()); |
| | | detail.setExtraPrice(itemVO.getExtraPrice()); |
| | | if (urgentIncreaseRate != null && itemVO.getExtraPrice() != null) { |
| | | detail.setExtraPrice(new BigDecimal(itemVO.getExtraPrice()) |
| | | .multiply(BigDecimal.ONE.add(urgentIncreaseRate)) |
| | | .setScale(0, RoundingMode.HALF_UP).longValue()); |
| | | } else { |
| | | detail.setExtraPrice(itemVO.getExtraPrice()); |
| | | } |
| | | detail.setLocallyPrice(itemVO.getLocallyPrice()); |
| | | detail.setDeleted(Constants.ZERO); |
| | | detail.setCreateTime(now); |
| | |
| | | |
| | | OrderDetailVO vo = new OrderDetailVO(); |
| | | vo.setOrder(order); |
| | | vo.setDeductionAmount(order.getDeductionAmount()); |
| | | |
| | | // 订单状态描述 |
| | | if (order.getStatus() != null) { |
| | | Constants.OrderStatus os = Constants.OrderStatus.getByKey(order.getStatus()); |
| | | vo.setStatusDesc(os != null ? os.getValue() : ""); |
| | | } |
| | | |
| | | // 图片路径前缀 |
| | | String imgPrefix = getOrdersPrefix(); |
| | |
| | | |
| | | String optUserName = getCurrentUserName(); |
| | | |
| | | // 加急费日志 |
| | | OrderLog feeLog = new OrderLog(); |
| | | feeLog.setOrderId(order.getId()); |
| | | feeLog.setTitle(Constants.OrderLogType.urgent.getTitle()); |
| | | feeLog.setLogInfo(Constants.OrderLogType.urgent.format(dto.getUrgentFee().toPlainString())); |
| | | feeLog.setObjType(Constants.OrderLogType.urgent.getStatus()); |
| | | feeLog.setOrderStatus(order.getStatus()); |
| | | feeLog.setOptUserType(3); |
| | | feeLog.setOptUserName(optUserName); |
| | | feeLog.setCreateTime(new Date()); |
| | | feeLog.setDeleted(Constants.ZERO); |
| | | orderLogService.create(feeLog); |
| | | |
| | | // 加急费用 元→分 |
| | | long urgentFeeFen = dto.getUrgentFee().multiply(new BigDecimal(100)).longValue(); |
| | | |
| | |
| | | com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper<Orders> updateWrapper = |
| | | new UpdateWrapper<Orders>().lambda() |
| | | .eq(Orders::getId, order.getId()) |
| | | .set(Orders::getIsUrgent, Constants.ONE) |
| | | .set(Orders::getPlatformRewardAmount, urgentFeeFen) |
| | | .set(Orders::getPlatformSmsNotified, Constants.ZERO) // 重置通知状态为未通知 |
| | | .set(Orders::getPlatformSmsNotifiedTime, new Date()) // 重置通知基准时间为当前 |
| | |
| | | updateWrapper.set(Orders::getRemark, dto.getRemark()); |
| | | } |
| | | |
| | | // 指派司机(非必填) |
| | | // 日志:只存一条,有司机用 assignDriver,无司机用 urgent |
| | | if (dto.getDriverId() != null) { |
| | | updateWrapper.set(Orders::getAssignDriverId, dto.getDriverId()); |
| | | // 校验司机信息 |
| | | DriverInfo driverInfo = driverInfoMapper.selectOne(new QueryWrapper<DriverInfo>().lambda() |
| | | .eq(DriverInfo::getId, dto.getDriverId()) |
| | | .eq(DriverInfo::getVersionType, Constants.ZERO) |
| | | .eq(DriverInfo::getDeleted, Constants.ZERO) |
| | | .last("limit 1")); |
| | | if (driverInfo == null) { |
| | | throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "司机信息不存在"); |
| | | } |
| | | if (!Integer.valueOf(3).equals(driverInfo.getAuditStatus())) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "司机未支付押金,无法派单"); |
| | | } |
| | | if (!Constants.ZERO.equals(driverInfo.getStatus())) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "司机已被禁用,无法派单"); |
| | | } |
| | | |
| | | Member driver = memberMapper.selectById(dto.getDriverId()); |
| | | String driverName = driver != null ? driver.getName() : String.valueOf(dto.getDriverId()); |
| | | // 更新接单司机信息及订单状态 |
| | | Date now = new Date(); |
| | | updateWrapper.set(Orders::getAssignDriverId, dto.getDriverId()); |
| | | updateWrapper.set(Orders::getAcceptDriver, dto.getDriverId()); |
| | | updateWrapper.set(Orders::getAcceptTime, now); |
| | | updateWrapper.set(Orders::getAcceptType, 1); // 1=系统派单 |
| | | updateWrapper.set(Orders::getStatus, Constants.OrderStatus.accepted.getStatus()); |
| | | |
| | | OrderLog driverLog = new OrderLog(); |
| | | driverLog.setOrderId(order.getId()); |
| | | driverLog.setTitle(Constants.OrderLogType.assignDriver.getTitle()); |
| | | driverLog.setLogInfo(Constants.OrderLogType.assignDriver.format(driverName, dto.getUrgentFee().toPlainString())); |
| | | driverLog.setLogInfo(Constants.OrderLogType.assignDriver.format(driverInfo.getName(), dto.getUrgentFee().toPlainString())); |
| | | driverLog.setObjType(Constants.OrderLogType.assignDriver.getStatus()); |
| | | driverLog.setOrderStatus(order.getStatus()); |
| | | driverLog.setOrderStatus(Constants.OrderStatus.accepted.getStatus()); |
| | | driverLog.setOptUserType(3); |
| | | driverLog.setOptUserName(optUserName); |
| | | driverLog.setCreateTime(new Date()); |
| | | driverLog.setCreateTime(now); |
| | | driverLog.setDeleted(Constants.ZERO); |
| | | orderLogService.create(driverLog); |
| | | |
| | | // 短信通知指派司机(加急派单) |
| | | DriverInfo driverInfo = driverInfoMapper.selectOne(new QueryWrapper<DriverInfo>().lambda() |
| | | .eq(DriverInfo::getMemberId, dto.getDriverId()) |
| | | .eq(DriverInfo::getDeleted, Constants.ZERO) |
| | | .last("limit 1")); |
| | | if (driverInfo != null) { |
| | | String address1 = order.getDepositShopAddress() != null ? order.getDepositShopAddress() : order.getDepositShopName(); |
| | | String address2 = order.getTakeShopAddress() != null ? order.getTakeShopAddress() : |
| | | (order.getTakeLocation() != null ? order.getTakeLocation() : ""); |
| | | // 配送费 = 司机配送费 + 加急费 |
| | | long totalDriverFee = (order.getDriverFee() != null ? order.getDriverFee() : 0L) + urgentFeeFen; |
| | | sendSmsNotify(driverInfo.getTelephone(), |
| | | Constants.SmsNotify.DRIVER_URGENT_DISPATCH, |
| | | "orderNo", order.getCode(), |
| | | "address1", address1, |
| | | "address2", address2, |
| | | "money1", String.valueOf(totalDriverFee / 100.0), |
| | | "money2", String.valueOf(urgentFeeFen / 100.0)); |
| | | } |
| | | String address1 = order.getDepositLocationRemark(); |
| | | String address2 = order.getTakeLocationRemark(); |
| | | long totalDriverFee = (order.getDriverFee() != null ? order.getDriverFee() : 0L) + urgentFeeFen; |
| | | sendSmsNotify(driverInfo.getTelephone(), |
| | | Constants.SmsNotify.DRIVER_URGENT_DISPATCH, |
| | | "orderNo", order.getCode(), |
| | | "address1", address1, |
| | | "address2", address2, |
| | | "money1", String.valueOf(totalDriverFee / 100.0), |
| | | "money2", String.valueOf(urgentFeeFen / 100.0)); |
| | | } else { |
| | | // 未指派司机,只记录加急日志 |
| | | OrderLog feeLog = new OrderLog(); |
| | | feeLog.setOrderId(order.getId()); |
| | | feeLog.setTitle(Constants.OrderLogType.urgent.getTitle()); |
| | | feeLog.setLogInfo(Constants.OrderLogType.urgent.format(dto.getUrgentFee().toPlainString())); |
| | | feeLog.setObjType(Constants.OrderLogType.urgent.getStatus()); |
| | | feeLog.setOrderStatus(order.getStatus()); |
| | | feeLog.setOptUserType(3); |
| | | feeLog.setOptUserName(optUserName); |
| | | feeLog.setCreateTime(new Date()); |
| | | feeLog.setDeleted(Constants.ZERO); |
| | | orderLogService.create(feeLog); |
| | | } |
| | | |
| | | ordersMapper.update(updateWrapper); |
| | |
| | | } |
| | | return urls; |
| | | } |
| | | private List<Multifile> getFileUrlsByidList(List<Integer> orderId, int objType, String prefix) { |
| | | if(orderId ==null || orderId.size()==0){ |
| | | return null; |
| | | } |
| | | List<Multifile> files = multifileMapper.selectList( |
| | | new QueryWrapper<Multifile>().lambda() |
| | | .in(Multifile::getObjId, orderId) |
| | | .eq(Multifile::getObjType, objType) |
| | | .eq(Multifile::getIsdeleted, Constants.ZERO) |
| | | .orderByAsc(Multifile::getSortnum)); |
| | | if (files != null) { |
| | | for (Multifile f : files) { |
| | | if (StringUtils.isNotBlank(f.getFileurl())) { |
| | | f.setFileurl(prefix + f.getFileurl()); |
| | | } |
| | | } |
| | | } |
| | | return files; |
| | | } |
| | | |
| | | /** |
| | | * 计算并设置订单薪酬分配(司机、存件门店、取件门店) |
| | |
| | | return; |
| | | } |
| | | Integer cityId = Integer.valueOf(orders.getCityId()); |
| | | boolean isRemote = Constants.equalsInteger(orders.getType(), Constants.ONE); |
| | | boolean isCompany = Constants.equalsInteger(depositShop.getCompanyType(), Constants.ONE); |
| | | |
| | | // 司机占比:fieldA=4(配送员) |
| | | // 司机占比:fieldA=4(配送员),始终从城市配置取 |
| | | BigDecimal driverRata = getRevenueShareRata(cityId, Constants.FOUR); |
| | | // 寄件门店占比:fieldA=0(企业寄)/1(个人寄) |
| | | int depositFieldA = Constants.equalsInteger(depositShop.getCompanyType(), Constants.ONE) ? Constants.ZERO : Constants.ONE; |
| | | BigDecimal depositShopRata = getRevenueShareRata(cityId, depositFieldA); |
| | | // 取件门店占比:无取件门店时比例为0 |
| | | BigDecimal takeShopRata = BigDecimal.ZERO; |
| | | if (takeShop != null) { |
| | | int takeFieldA = Constants.equalsInteger(takeShop.getCompanyType(), Constants.ONE) ? Constants.TWO : Constants.THREE; |
| | | takeShopRata = getRevenueShareRata(cityId, takeFieldA); |
| | | |
| | | // 存件门店占比 |
| | | BigDecimal depositShopRata; |
| | | if (isRemote) { |
| | | int fallbackFieldA = isCompany ? Constants.ZERO : Constants.ONE; |
| | | depositShopRata = getShopRevenueShare(depositShop, "remoteDeposit", cityId, fallbackFieldA); |
| | | } else { |
| | | int fallbackFieldA = isCompany ? Constants.FIVE : Constants.SIX; |
| | | depositShopRata = getShopRevenueShare(depositShop, "localDeposit", cityId, fallbackFieldA); |
| | | } |
| | | |
| | | // 计算薪酬(分):totalAmount 为分,rata 为比例值(如 0.15 表示 15%) |
| | | // 取件门店占比 |
| | | BigDecimal takeShopRata = BigDecimal.ZERO; |
| | | if (isRemote && takeShop != null) { |
| | | boolean takeIsCompany = Constants.equalsInteger(takeShop.getCompanyType(), Constants.ONE); |
| | | int fallbackFieldA = takeIsCompany ? Constants.TWO : Constants.THREE; |
| | | takeShopRata = getShopRevenueShare(takeShop, "remoteTake", cityId, fallbackFieldA); |
| | | } |
| | | |
| | | // 计算薪酬(分) |
| | | long driverFee = new BigDecimal(totalAmount).multiply(driverRata).longValue(); |
| | | long depositShopFee = new BigDecimal(totalAmount).multiply(depositShopRata).longValue(); |
| | | long takeShopFee = new BigDecimal(totalAmount).multiply(takeShopRata).longValue(); |
| | |
| | | * 从 pricing_rule 表获取分成比例(type=4) |
| | | * |
| | | * @param cityId 城市主键 |
| | | * @param fieldA 类型:0=企业寄, 1=个人寄, 2=企业取, 3=个人取, 4=配送员 |
| | | * @param fieldA 类型:0=异地企业寄, 1=异地个人寄, 2=异地企业取, 3=异地个人取, 4=配送员, 5=就地企业存, 6=就地个人存 |
| | | * @return 分成比例(如 0.15 表示 15%) |
| | | */ |
| | | private BigDecimal getRevenueShareRata(Integer cityId, int fieldA) { |
| | |
| | | .eq(PricingRule::getFieldA, String.valueOf(fieldA)) |
| | | .last("limit 1")); |
| | | if (rule != null && StringUtils.isNotBlank(rule.getFieldB())) { |
| | | // fieldB 存储的是百分比整数(如15表示15%),转换为小数比例(0.15) |
| | | // fieldB 存储的是百分比数字(如15表示15%),转换为小数比例(0.15) |
| | | return new BigDecimal(rule.getFieldB()).divide(new BigDecimal("100"), 4, BigDecimal.ROUND_HALF_UP); |
| | | } |
| | | return BigDecimal.ZERO; |
| | | } |
| | | |
| | | /** |
| | | * 获取门店收益占比:优先从门店 revenueShareConfig 取,无则兜底城市 pricing_rule |
| | | * |
| | | * @param shop 门店信息 |
| | | * @param jsonKey revenueShareConfig 中的键名 |
| | | * @param cityId 城市主键(兜底用) |
| | | * @param fallbackFieldA 兜底 pricing_rule fieldA 值 |
| | | * @return 分成比例 |
| | | */ |
| | | private BigDecimal getShopRevenueShare(ShopInfo shop, String jsonKey, Integer cityId, int fallbackFieldA) { |
| | | if (shop != null && StringUtils.isNotBlank(shop.getRevenueShareConfig())) { |
| | | try { |
| | | JSONObject config = JSONObject.parseObject(shop.getRevenueShareConfig()); |
| | | if (config != null && config.containsKey(jsonKey)) { |
| | | Double value = config.getDouble(jsonKey); |
| | | if (value != null) { |
| | | return new BigDecimal(value).divide(new BigDecimal("100"), 4, BigDecimal.ROUND_HALF_UP); |
| | | } |
| | | } |
| | | } catch (Exception ignored) { |
| | | } |
| | | } |
| | | return getRevenueShareRata(cityId, fallbackFieldA); |
| | | } |
| | | |
| | | @Override |
| | |
| | | .select("s1.name", Orders::getDepositShopName) |
| | | .select("s1.link_name", Orders::getDepositShopLinkName) |
| | | .select("s1.link_phone", Orders::getDepositShopLinkPhone) |
| | | .select("s1.address", Orders::getDepositShopAddress) |
| | | .select("s2.name", Orders::getTakeShopName) |
| | | .select("s2.address", Orders::getTakeShopAddress) |
| | | .select("s2.link_phone", Orders::getTakeShopLinkPhone) |
| | |
| | | .eq(status != null, Orders::getStatus, status) |
| | | .in(statusList != null, Orders::getStatus, statusList) |
| | | .orderByDesc(Orders::getCreateTime); |
| | | // 可开票订单:增加月份限制 |
| | | if (model != null && model.getInvoiceStatus() != null && Constants.equalsInteger(model.getInvoiceStatus(), Constants.ONE)) { |
| | | wrapper.in(Orders::getInvoiceStatus, Arrays.asList(Constants.ONE, 99)); |
| | | String monthLimitStr = operationConfigBiz.getConfig().getInvoiceMonthLimit(); |
| | | if (StringUtils.isNotBlank(monthLimitStr)) { |
| | | int monthLimit = Integer.parseInt(monthLimitStr); |
| | | Calendar cal = Calendar.getInstance(); |
| | | cal.add(Calendar.MONTH, -monthLimit); |
| | | cal.set(Calendar.HOUR_OF_DAY, 0); |
| | | cal.set(Calendar.MINUTE, 0); |
| | | cal.set(Calendar.SECOND, 0); |
| | | cal.set(Calendar.MILLISECOND, 0); |
| | | wrapper.ge(Orders::getFinishTime, cal.getTime()); |
| | | } |
| | | } |
| | | // 关键词搜索:收件人/收件人电话模糊、订单号精准 |
| | | if (model != null && StringUtils.isNotBlank(model.getKeyword())) { |
| | | String kw = model.getKeyword().trim(); |
| | | wrapper.and(w -> w.like(Orders::getTakeUser, kw) |
| | | .or().like(Orders::getTakePhone, kw) |
| | | .or().eq(Orders::getCode, kw)); |
| | | } |
| | | |
| | | IPage<Orders> orderPage = ordersMapper.selectJoinPage(p, Orders.class, wrapper); |
| | | List<MyOrderVO> voList = new ArrayList<>(); |
| | | if (orderPage != null && orderPage.getRecords() != null) { |
| | | |
| | | if (orderPage != null && orderPage.getRecords() != null && orderPage.getRecords().size()>0) { |
| | | String imgPrefix = getOrdersPrefix(); |
| | | List<Integer> idList =new ArrayList<>(); |
| | | for (Orders o : orderPage.getRecords()) { |
| | | idList.add(o.getId()); |
| | | } |
| | | List<Multifile> files = getFileUrlsByidList(idList,Constants.FileType.ORDER_FILE.getKey(), imgPrefix); |
| | | for (Orders o : orderPage.getRecords()) { |
| | | MyOrderVO vo = new MyOrderVO(); |
| | | vo.setId(o.getId()); |
| | |
| | | vo.setDepositShopName(o.getDepositShopName()); |
| | | vo.setDepositShopLinkName(o.getDepositShopLinkName()); |
| | | vo.setDepositShopPhone(o.getDepositShopLinkPhone()); |
| | | |
| | | vo.setDepositShopAddress(o.getDepositShopAddress()); |
| | | // 取件信息:有取件门店取门店,无则取用户自选取件点 |
| | | if (o.getTakeShopId() != null) { |
| | | vo.setTakeShopId(o.getTakeShopId()); |
| | |
| | | |
| | | // 评价状态 |
| | | vo.setCommentStatus(o.getCommentStatus()); |
| | | //序号 |
| | | vo.setSortnum(Constants.formatIntegerNum(o.getDepositShopId())+"-"+o.getId()); |
| | | if(o.getTakeShopId()!=null){ |
| | | vo.setSortnumTake(Constants.formatIntegerNum(o.getTakeShopId())+"-"+o.getId()); |
| | | } |
| | | |
| | | // 查询物品明细(一次查询,同时用于物品列表和逾期计算) |
| | | List<OrdersDetail> details = ordersDetailMapper.selectList( |
| | | new QueryWrapper<OrdersDetail>().lambda() |
| | | .eq(OrdersDetail::getOrderId, o.getId()) |
| | | .eq(OrdersDetail::getDeleted, Constants.ZERO)); |
| | | |
| | | // 物品明细 |
| | | vo.setDetailList(buildDetailList(details)); |
| | | |
| | | vo.setOrderImages(getFileUrlsFromList(o.getId(),files)); |
| | | // 逾期状态 |
| | | fillOverdueStatus(vo, o, details); |
| | | // 优惠券抵扣金额 |
| | | vo.setDeductionAmount(o.getDeductionAmount()); |
| | | // 可开票金额(支付金额 - 退款金额) |
| | | if (model != null && model.getInvoiceStatus() != null && Constants.equalsInteger(model.getInvoiceStatus(), Constants.ONE)) { |
| | | long payAmt = o.getPayAmount() != null ? o.getPayAmount() : 0L; |
| | | long refundAmt = o.getRefundAmount() != null ? o.getRefundAmount() : 0L; |
| | | vo.setInvoiceAmount(payAmt - refundAmt); |
| | | } |
| | | voList.add(vo); |
| | | } |
| | | } |
| | |
| | | pageData.setPage(orderPage.getCurrent()); |
| | | pageData.setCapacity(orderPage.getSize()); |
| | | return pageData; |
| | | } |
| | | |
| | | private List<String> getFileUrlsFromList(Integer id, List<Multifile> files) { |
| | | List<String> urls = new ArrayList<>(); |
| | | try { |
| | | if(files!=null){ |
| | | for(Multifile f : files){ |
| | | if(Constants.equalsInteger(f.getObjId(),id)){ |
| | | urls.add(f.getFileurl()); |
| | | } |
| | | } |
| | | } |
| | | }catch (Exception e){ |
| | | } |
| | | |
| | | return urls; |
| | | } |
| | | |
| | | @Override |
| | |
| | | // 门店待处理订单:按业务环节区分门店角色 |
| | | if (combinedStatus != null && Constants.equalsInteger(combinedStatus, Constants.SEVEN)) { |
| | | wrapper.and(w -> w |
| | | .and(w1 -> w1.eq(Orders::getDepositShopId, shopId) |
| | | .and(w1 -> w1.eq(Orders::getType, Constants.ZERO).eq(Orders::getDepositShopId, shopId) |
| | | .in(Orders::getStatus, Constants.OrderStatus.waitDeposit.getStatus(), |
| | | Constants.OrderStatus.deposited.getStatus())) |
| | | .or(w2 -> w2.eq(Orders::getTakeShopId, shopId) |
| | | .in(Orders::getStatus, Constants.OrderStatus.delivering.getStatus(), |
| | | Constants.OrderStatus.arrived.getStatus())) |
| | | Constants.OrderStatus.arrived.getStatus()) |
| | | .or(w3-> w3.eq(Orders::getType, Constants.ONE).eq(Orders::getDepositShopId, shopId) |
| | | .eq(Orders::getStatus, Constants.OrderStatus.waitDeposit.getStatus())) |
| | | .or(w2 -> w2.eq(Orders::getType, Constants.ONE).eq(Orders::getTakeShopId, shopId) |
| | | .eq(Orders::getStatus, Constants.OrderStatus.arrived.getStatus()))) |
| | | ); |
| | | } else { |
| | | wrapper.and(w -> w.eq(Orders::getDepositShopId, shopId).or().eq(Orders::getTakeShopId, shopId)); |
| | | } |
| | | |
| | | wrapper.eq(status != null, Orders::getStatus, status) |
| | | .in(statusList != null && !Constants.equalsInteger(combinedStatus, Constants.SEVEN), Orders::getStatus, statusList) |
| | | .orderByDesc(Orders::getId); |
| | | .in(statusList != null && !Constants.equalsInteger(combinedStatus, Constants.SEVEN), Orders::getStatus, statusList); |
| | | if(Objects.nonNull(model.getCombinedStatus())&&Constants.equalsInteger(model.getCombinedStatus(),Constants.OrderCombinedStatus.finished.getKey())){ |
| | | wrapper.orderByDesc(Orders::getFinishTime); |
| | | }else{ |
| | | wrapper.orderByDesc(Orders::getId); |
| | | } |
| | | // 关键词搜索:收件人/收件人电话模糊、订单号精准 |
| | | MyOrderDTO shopModel = pageWrap.getModel(); |
| | | if (shopModel != null && StringUtils.isNotBlank(shopModel.getKeyword())) { |
| | | String kw = shopModel.getKeyword().trim(); |
| | | wrapper.and(w -> w.like(Orders::getTakeUser, kw) |
| | | .or().like(Orders::getTakePhone, kw) |
| | | .or().eq(Orders::getCode, kw)); |
| | | } |
| | | |
| | | IPage<Orders> orderPage = ordersMapper.selectJoinPage(p, Orders.class, wrapper); |
| | | List<MyOrderVO> voList = new ArrayList<>(); |
| | |
| | | .eq(OrdersDetail::getDeleted, Constants.ZERO)); |
| | | |
| | | vo.setDetailList(buildDetailList(details)); |
| | | |
| | | |
| | | // 逾期状态 |
| | | fillOverdueStatus(vo, o, details); |
| | | |
| | |
| | | if (Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.waitPay.getStatus())) { |
| | | vo.setPayCountdownMs(calcPayCountdownMs(order)); |
| | | } |
| | | |
| | | //序号 |
| | | vo.setSortnum(Constants.formatIntegerNum(order.getDepositShopId())+"-"+order.getId()); |
| | | if(order.getTakeShopId()!=null){ |
| | | String dateStr = new SimpleDateFormat("dd").format(order.getPayTime() != null ? order.getPayTime() : new Date()); |
| | | String autoNumStr = String.format("%03d", order.getAutoNum() != null ? order.getAutoNum() : 0); |
| | | String sort = order.getTakeShopId() + "-" + dateStr + "-" + autoNumStr; |
| | | vo.setSortnumTake(sort); |
| | | } |
| | | // 存件门店 |
| | | if (order.getDepositShopId() != null) { |
| | | ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId()); |
| | |
| | | vo.setMemberVerifyCode(order.getMemberVerifyCode()); |
| | | } |
| | | |
| | | // 异地寄存经纬度(就地寄存不返回) |
| | | if (Constants.ONE.equals(order.getType())) { |
| | | // status=3(已接单):返回存件门店经纬度 + 司机经纬度 |
| | | if (Constants.equalsInteger(status, Constants.OrderStatus.accepted.getStatus())) { |
| | | ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId()); |
| | | if (depositShop != null) { |
| | | vo.setDepositShopLng(depositShop.getLongitude()); |
| | | vo.setDepositShopLat(depositShop.getLatitude()); |
| | | } |
| | | if (order.getAcceptDriver() != null) { |
| | | DriverInfo driver = driverInfoMapper.selectById(order.getAcceptDriver()); |
| | | if (driver != null) { |
| | | vo.setDriverLng(driver.getLongitude()); |
| | | vo.setDriverLat(driver.getLatitude()); |
| | | } |
| | | } |
| | | } |
| | | // status=4(配送中):返回取件点经纬度 + 司机经纬度 |
| | | if (Constants.equalsInteger(status, Constants.OrderStatus.delivering.getStatus())) { |
| | | // 取件点经纬度(优先取件门店,否则订单上的取件坐标) |
| | | if (order.getTakeShopId() != null) { |
| | | ShopInfo takeShop = shopInfoMapper.selectById(order.getTakeShopId()); |
| | | if (takeShop != null) { |
| | | vo.setTakeLng(takeShop.getLongitude()); |
| | | vo.setTakeLat(takeShop.getLatitude()); |
| | | } |
| | | } else if (order.getTakeLgt() != null && order.getTakeLat() != null) { |
| | | vo.setTakeLng(order.getTakeLgt().doubleValue()); |
| | | vo.setTakeLat(order.getTakeLat().doubleValue()); |
| | | } |
| | | if (order.getAcceptDriver() != null) { |
| | | DriverInfo driver = driverInfoMapper.selectById(order.getAcceptDriver()); |
| | | if (driver != null) { |
| | | vo.setDriverLng(driver.getLongitude()); |
| | | vo.setDriverLat(driver.getLatitude()); |
| | | } |
| | | } |
| | | } |
| | | vo.setDepositShopLng(order.getDepositLgt().doubleValue()); |
| | | vo.setDepositShopLat(order.getDepositLat().doubleValue()); |
| | | // 取件点经纬度 |
| | | vo.setTakeLng(order.getTakeLgt().doubleValue()); |
| | | vo.setTakeLat(order.getTakeLat().doubleValue()); |
| | | //司机经纬度 |
| | | DriverInfo driver = driverInfoMapper.selectById(order.getAcceptDriver()); |
| | | if (driver != null) { |
| | | vo.setDriverLng(driver.getLongitude()); |
| | | vo.setDriverLat(driver.getLatitude()); |
| | | } |
| | | |
| | | // 评价信息 |
| | | vo.setCommentStatus(order.getCommentStatus()); |
| | | if (Constants.equalsInteger(order.getCommentStatus(), Constants.ONE)) { |
| | |
| | | order.setStatus(Constants.OrderStatus.cancelled.getStatus()); |
| | | order.setCancelTime(now); |
| | | ordersMapper.updateById(order); |
| | | restoreCoupon(order); |
| | | saveCancelLog(order, Constants.OrderLogType.memberCancel, "会员取消订单(待支付)", memberId); |
| | | // 短信通知会员:订单已取消 |
| | | Member cancelMember1 = memberMapper.selectById(memberId); |
| | |
| | | Constants.SmsNotify.MEMBER_CANCELLED, "orderNo", order.getCode()); |
| | | |
| | | // 调用微信退款V3,全额退款 |
| | | String outRefundNo = ID.nextGUID(); |
| | | com.wechat.pay.java.service.refund.model.Refund refundResult; |
| | | try { |
| | | refundResult = wxPayV3Service.refund(order.getOutTradeNo(), order.getPayAmount(), order.getPayAmount(), |
| | | refundResult = wxPayV3Service.refund(outRefundNo, order.getOutTradeNo(), order.getPayAmount(), order.getPayAmount(), |
| | | "订单退款", wxPayProperties.getV3RefundNotifyUrl()); |
| | | } catch (Exception e) { |
| | | log.error("待寄存订单退款调用异常, orderId={}", orderId, e); |
| | |
| | | refund.setDeleted(Constants.ZERO); |
| | | refund.setBeforeStatus(Constants.OrderStatus.waitDeposit.getStatus()); |
| | | refund.setRefundAmount(order.getPayAmount()); |
| | | refund.setRefundCode(refundResult.getOutRefundNo()); |
| | | refund.setRefundCode(outRefundNo); |
| | | |
| | | if (com.wechat.pay.java.service.refund.model.Status.SUCCESS.equals(refundStatus)) { |
| | | // 退款成功 |
| | |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "退款失败,请联系客服处理"); |
| | | } |
| | | ordersRefundMapper.insert(refund); |
| | | restoreCoupon(order); |
| | | return; |
| | | } |
| | | |
| | |
| | | DriverInfo originalDriver = originalDriverId != null ? driverInfoMapper.selectById(originalDriverId) : null; |
| | | |
| | | ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId()); |
| | | // 取件点信息更新为存件门店,使用 UpdateWrapper 确保 null 字段也能生效 |
| | | |
| | | // 计算就地寄存存件门店分成比例 |
| | | Integer cityId = Integer.valueOf(order.getCityId()); |
| | | boolean isCompany = depositShop != null && Constants.equalsInteger(depositShop.getCompanyType(), Constants.ONE); |
| | | int fallbackFieldA = isCompany ? Constants.FIVE : Constants.SIX; |
| | | BigDecimal localDepositRata = getShopRevenueShare(depositShop, "localDeposit", cityId, fallbackFieldA); |
| | | Long totalAmount = order.getTotalAmount() != null ? order.getTotalAmount() : 0L; |
| | | Long localDepositFee = new BigDecimal(totalAmount).multiply(localDepositRata) |
| | | .setScale(0, RoundingMode.HALF_UP).longValue(); |
| | | |
| | | // 取件点信息更新为存件门店,重算分成 |
| | | UpdateWrapper<Orders> updateWrapper = new UpdateWrapper<>(); |
| | | updateWrapper.lambda() |
| | | .eq(Orders::getId, order.getId()) |
| | | .set(Orders::getType, Constants.ZERO) |
| | | .set(Orders::getStatus, Constants.OrderStatus.arrived.getKey()) |
| | | .set(Orders::getIsConverted, Constants.ONE) |
| | | .set(Orders::getTakeShopId, order.getDepositShopId()) |
| | | .set(Orders::getTakeShopName, order.getDepositShopName()) |
| | | .set(Orders::getTakeShopAddress, order.getDepositShopAddress()) |
| | | .set(Orders::getTakeShopLinkPhone, order.getDepositShopLinkPhone()) |
| | | .set(Orders::getTakeLocation, order.getDepositLocation()) |
| | | .set(Orders::getTakeLocationRemark, order.getDepositLocationRemark()) |
| | | .set(Orders::getTakeLat, order.getDepositLat()) |
| | | .set(Orders::getTakeLgt, order.getDepositLgt()) |
| | | .set(Orders::getExpectedTakeTime, new Date()); |
| | | .set(Orders::getExpectedTakeTime, new Date()) |
| | | // 存件门店分成按就地寄存重算 |
| | | .set(Orders::getDepositShopFeeRata, localDepositRata) |
| | | .set(Orders::getDepositShopFee, localDepositFee) |
| | | // 司机、取件门店分成归零 |
| | | .set(Orders::getDriverFeeRata, BigDecimal.ZERO) |
| | | .set(Orders::getDriverFee, 0L) |
| | | .set(Orders::getTakeShopFeeRata, BigDecimal.ZERO) |
| | | .set(Orders::getTakeShopFee, 0L); |
| | | if (Constants.equalsInteger(status, Constants.OrderStatus.accepted.getStatus())) { |
| | | updateWrapper.lambda() |
| | | .set(Orders::getAcceptDriver, null) |
| | |
| | | /** |
| | | * 保存取消订单操作日志 |
| | | */ |
| | | private void restoreCoupon(Orders order) { |
| | | // if (order.getCouponId() == null || order.getDeductionAmount() == null || order.getDeductionAmount() <= 0) { |
| | | // return; |
| | | // } |
| | | // memberCouponMapper.update(new UpdateWrapper<MemberCoupon>().lambda() |
| | | // .set(MemberCoupon::getStatus, Constants.CouponStatus.claimed.getKey()) |
| | | // .set(MemberCoupon::getUseDate, null) |
| | | // .set(MemberCoupon::getOrderId, null) |
| | | // .eq(MemberCoupon::getId, order.getCouponId()) |
| | | // .eq(MemberCoupon::getOrderId, order.getId()) |
| | | // .eq(MemberCoupon::getStatus, Constants.CouponStatus.used.getKey())); |
| | | } |
| | | |
| | | private void saveCancelLog(Orders order, Constants.OrderLogType logType, String reason, Integer memberId) { |
| | | OrderLog log = new OrderLog(); |
| | | log.setOrderId(order.getId()); |
| | |
| | | log.setCreateTime(new Date()); |
| | | log.setDeleted(Constants.ZERO); |
| | | orderLogService.create(log); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void manualRefund(ManualRefundDTO dto, Integer userId) { |
| | | // 1. 校验订单 |
| | | Orders order = ordersMapper.selectById(dto.getOrderId()); |
| | | if (order == null || Constants.equalsInteger(order.getDeleted(), Constants.ONE)) { |
| | | throw new BusinessException(ResponseStatus.DATA_EMPTY); |
| | | } |
| | | if (!Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.finished.getStatus())) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "仅已完成订单可手动退款"); |
| | | } |
| | | if (Constants.equalsInteger(order.getManualRefund(), Constants.ONE)) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单已手动退款"); |
| | | } |
| | | // 异常订单不允许手动退款 |
| | | if (Constants.equalsInteger(order.getExceptionStatus(), Constants.ONE)) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "异常订单不支持手动退款"); |
| | | } |
| | | |
| | | // 2. 校验退款金额 |
| | | long payAmount = order.getPayAmount() != null ? order.getPayAmount() : 0L; |
| | | long existingRefund = order.getRefundAmount() != null ? order.getRefundAmount() : 0L; |
| | | long maxRefund = payAmount - existingRefund; |
| | | if (dto.getRefundAmount() <= 0) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "退款金额必须大于0"); |
| | | } |
| | | if (dto.getRefundAmount() > maxRefund) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "退款金额不能超过可退金额(" + maxRefund + "分)"); |
| | | } |
| | | |
| | | // 3. 校验并清理扣款金额(根据订单类型) |
| | | boolean isRemote = Constants.equalsInteger(order.getType(), Constants.ONE); |
| | | Long depositShopDeduct = dto.getDepositShopDeduct(); |
| | | Long takeShopDeduct = dto.getTakeShopDeduct(); |
| | | Long driverDeduct = dto.getDriverDeduct(); |
| | | |
| | | if (!isRemote) { |
| | | takeShopDeduct = null; |
| | | driverDeduct = null; |
| | | } else { |
| | | if (order.getTakeShopId() == null) { |
| | | takeShopDeduct = null; |
| | | } |
| | | } |
| | | if (depositShopDeduct != null && depositShopDeduct < 0) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "存件门店扣款金额不能为负"); |
| | | } |
| | | if (takeShopDeduct != null && takeShopDeduct < 0) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "取件门店扣款金额不能为负"); |
| | | } |
| | | if (driverDeduct != null && driverDeduct < 0) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "司机扣款金额不能为负"); |
| | | } |
| | | |
| | | // 4. 构建 deductInfo JSON |
| | | String deductInfo = null; |
| | | JSONObject deductJson = new JSONObject(); |
| | | if (depositShopDeduct != null) { |
| | | deductJson.put("depositShopDeduct", depositShopDeduct); |
| | | } |
| | | if (takeShopDeduct != null) { |
| | | deductJson.put("takeShopDeduct", takeShopDeduct); |
| | | } |
| | | if (driverDeduct != null) { |
| | | deductJson.put("driverDeduct", driverDeduct); |
| | | } |
| | | if (deductJson.size() > 0) { |
| | | deductInfo = deductJson.toJSONString(); |
| | | } |
| | | |
| | | // 5. 创建退款记录 |
| | | Date now = new Date(); |
| | | String outRefundNo = ID.nextGUID(); |
| | | OrdersRefund refund = new OrdersRefund(); |
| | | refund.setOrderId(order.getId()); |
| | | refund.setType(Constants.FOUR); // 手动退款 |
| | | refund.setRefundAmount(dto.getRefundAmount()); |
| | | refund.setDeductInfo(deductInfo); |
| | | refund.setRefundRemark(dto.getRemark()); |
| | | refund.setUserId(userId); |
| | | refund.setBeforeStatus(order.getStatus()); |
| | | refund.setRefundCode(outRefundNo); |
| | | refund.setCreateTime(now); |
| | | refund.setDeleted(Constants.ZERO); |
| | | ordersRefundMapper.insert(refund); |
| | | |
| | | // 6. 调用微信退款 |
| | | try { |
| | | Refund refundResult = wxPayV3Service.refund( |
| | | outRefundNo, order.getOutTradeNo(), payAmount, dto.getRefundAmount(), |
| | | "手动退款", wxPayProperties.getV3RefundNotifyUrl()); |
| | | |
| | | com.wechat.pay.java.service.refund.model.Status refundStatus = refundResult.getStatus(); |
| | | if (com.wechat.pay.java.service.refund.model.Status.SUCCESS.equals(refundStatus)) { |
| | | refund.setStatus(Constants.ONE); |
| | | refund.setRefundTime(now); |
| | | ordersRefundMapper.updateById(refund); |
| | | // 退款成功,执行扣款 |
| | | processManualRefundDeduction(order, depositShopDeduct, takeShopDeduct, driverDeduct); |
| | | } else if (com.wechat.pay.java.service.refund.model.Status.PROCESSING.equals(refundStatus)) { |
| | | refund.setStatus(Constants.ZERO); // 退款中,等回调 |
| | | ordersRefundMapper.updateById(refund); |
| | | } else { |
| | | refund.setStatus(Constants.TWO); |
| | | refund.setRefundRemark("微信退款失败: " + refundStatus.name()); |
| | | ordersRefundMapper.updateById(refund); |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "退款失败,请联系客服处理"); |
| | | } |
| | | } catch (BusinessException e) { |
| | | throw e; |
| | | } catch (Exception e) { |
| | | log.error("手动退款调用微信退款异常, orderId={}", order.getId(), e); |
| | | refund.setStatus(Constants.TWO); |
| | | ordersRefundMapper.updateById(refund); |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "退款调用异常,请稍后重试"); |
| | | } |
| | | |
| | | // 7. 更新订单:标记已手动退款,累加退款金额 |
| | | ordersMapper.update(new UpdateWrapper<Orders>().lambda() |
| | | .set(Orders::getManualRefund, Constants.ONE) |
| | | .setSql(" REFUND_AMOUNT = IFNULL(REFUND_AMOUNT, 0) + " + dto.getRefundAmount()) |
| | | .set(Orders::getUpdateTime, now) |
| | | .eq(Orders::getId, order.getId())); |
| | | } |
| | | |
| | | /** |
| | | * 手动退款成功后,根据扣款信息扣除对应方余额并生成收支记录 |
| | | */ |
| | | private void processManualRefundDeduction(Orders order, Long depositShopDeduct, Long takeShopDeduct, Long driverDeduct) { |
| | | Date now = new Date(); |
| | | |
| | | // 存件门店扣款 |
| | | if (depositShopDeduct != null && depositShopDeduct > 0 && order.getDepositShopId() != null) { |
| | | ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId()); |
| | | if (depositShop != null) { |
| | | shopInfoMapper.update(new UpdateWrapper<ShopInfo>().lambda() |
| | | .setSql(" BALANCE = IFNULL(BALANCE, 0) - " + depositShopDeduct) |
| | | .eq(ShopInfo::getId, depositShop.getId())); |
| | | revenueMapper.insert(buildDeductRevenue(depositShop.getId(), Constants.TWO, |
| | | depositShopDeduct, order.getId(), order.getCode(), now)); |
| | | } |
| | | } |
| | | |
| | | // 取件门店扣款 |
| | | if (takeShopDeduct != null && takeShopDeduct > 0 && order.getTakeShopId() != null) { |
| | | ShopInfo takeShop = shopInfoMapper.selectById(order.getTakeShopId()); |
| | | if (takeShop != null) { |
| | | shopInfoMapper.update(new UpdateWrapper<ShopInfo>().lambda() |
| | | .setSql(" BALANCE = IFNULL(BALANCE, 0) - " + takeShopDeduct) |
| | | .eq(ShopInfo::getId, takeShop.getId())); |
| | | revenueMapper.insert(buildDeductRevenue(takeShop.getId(), Constants.TWO, |
| | | takeShopDeduct, order.getId(), order.getCode(), now)); |
| | | } |
| | | } |
| | | |
| | | // 司机扣款 |
| | | if (driverDeduct != null && driverDeduct > 0 && order.getAcceptDriver() != null) { |
| | | DriverInfo driver = driverInfoMapper.selectById(order.getAcceptDriver()); |
| | | if (driver != null && driver.getMemberId() != null) { |
| | | driverInfoMapper.update(new UpdateWrapper<DriverInfo>().lambda() |
| | | .setSql(" BALANCE = IFNULL(BALANCE, 0) - " + driverDeduct) |
| | | .eq(DriverInfo::getId, driver.getId())); |
| | | revenueMapper.insert(buildDeductRevenue(driver.getMemberId(), Constants.ONE, |
| | | driverDeduct, order.getId(), order.getCode(), now)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private Revenue buildDeductRevenue(Integer memberId, Integer memberType, Long amount, Integer orderId, String orderNo, Date now) { |
| | | Revenue revenue = new Revenue(); |
| | | revenue.setMemberId(memberId); |
| | | revenue.setMemberType(memberType); |
| | | revenue.setType(Constants.FOUR); // 责任扣款 |
| | | revenue.setOptType(-Constants.ONE); // 支出 |
| | | revenue.setAmount(amount); |
| | | revenue.setVaildStatus(Constants.ONE); // 已入账 |
| | | revenue.setObjId(orderId); |
| | | revenue.setObjType(Constants.ZERO); // 订单业务 |
| | | revenue.setOrderNo(orderNo); |
| | | revenue.setStatus(Constants.ZERO); // 成功 |
| | | revenue.setDeleted(Constants.ZERO); |
| | | revenue.setCreateTime(now); |
| | | return revenue; |
| | | } |
| | | |
| | | @Override |
| | | public void processManualRefundCallback(OrdersRefund refundRecord) { |
| | | if (!Constants.equalsInteger(refundRecord.getType(), Constants.FOUR)) { |
| | | return; |
| | | } |
| | | if (StringUtils.isBlank(refundRecord.getDeductInfo())) { |
| | | return; |
| | | } |
| | | Orders order = ordersMapper.selectById(refundRecord.getOrderId()); |
| | | if (order == null) { |
| | | return; |
| | | } |
| | | JSONObject deductJson = JSONObject.parseObject(refundRecord.getDeductInfo()); |
| | | Long depositShopDeduct = deductJson.getLong("depositShopDeduct"); |
| | | Long takeShopDeduct = deductJson.getLong("takeShopDeduct"); |
| | | Long driverDeduct = deductJson.getLong("driverDeduct"); |
| | | processManualRefundDeduction(order, depositShopDeduct, takeShopDeduct, driverDeduct); |
| | | |
| | | // 标记订单已手动退款,累加退款金额 |
| | | ordersMapper.update(new UpdateWrapper<Orders>().lambda() |
| | | .set(Orders::getManualRefund, Constants.ONE) |
| | | .setSql(" REFUND_AMOUNT = IFNULL(REFUND_AMOUNT, 0) + " + refundRecord.getRefundAmount()) |
| | | .set(Orders::getUpdateTime, new Date()) |
| | | .eq(Orders::getId, order.getId())); |
| | | } |
| | | |
| | | /** |
| | |
| | | * 发送司机站内信通知 |
| | | */ |
| | | private void sendDriverNotice(Integer driverId, Constants.DriverOrderNotify notify, Integer orderId, String... params) { |
| | | sendDriverNotice(driverId, notify, orderId, 0, params); |
| | | } |
| | | |
| | | private void sendDriverNotice(Integer driverId, Constants.DriverOrderNotify notify, Integer objId, Integer objType, String... params) { |
| | | DriverInfo driver = driverInfoMapper.selectById(driverId); |
| | | if (driver == null || driver.getMemberId() == null) { |
| | | return; |
| | |
| | | notice.setUserId(driver.getMemberId()); |
| | | notice.setTitle(notify.getTitle()); |
| | | notice.setContent(notify.format(params)); |
| | | notice.setObjId(orderId); |
| | | notice.setObjType(0); // 0=订单 |
| | | notice.setObjId(objId); |
| | | notice.setObjType(objType); |
| | | notice.setStatus(0); // 0=未读 |
| | | notice.setIsdeleted(Constants.ZERO); |
| | | notice.setCreateDate(new Date()); |
| | |
| | | order.setWxExternalNo(wxTradeNo); |
| | | order.setPayAmount(order.getTotalAmount()); |
| | | order.setUpdateTime(now); |
| | | // 计算店铺订单序号:当前存件门店当天已支付订单数 + 1 |
| | | Date todayStart = DateUtil.getStartOfDay(now); |
| | | Long currentCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda() |
| | | .eq(Orders::getDepositShopId, order.getDepositShopId()) |
| | | .eq(Orders::getPayStatus, Constants.ONE) |
| | | .ge(Orders::getPayTime, todayStart) |
| | | .eq(Orders::getDeleted, Constants.ZERO)); |
| | | order.setAutoNum(currentCount + 1); |
| | | // 生成会员核销码 |
| | | order.setMemberVerifyCode(generateVerifyCode()); |
| | | // 异地寄存:计算预计送达时间 |
| | |
| | | } |
| | | } |
| | | ordersMapper.updateById(order); |
| | | |
| | | // 支付成功日志 |
| | | OrderLog payLog = new OrderLog(); |
| | | payLog.setOrderId(order.getId()); |
| | | payLog.setTitle(Constants.OrderLogType.payOrder.getTitle()); |
| | | payLog.setLogInfo(Constants.OrderLogType.payOrder.format( |
| | | String.valueOf(order.getTotalAmount() != null ? order.getTotalAmount() / 100.0 : 0))); |
| | | payLog.setObjType(Constants.OrderLogType.payOrder.getStatus()); |
| | | payLog.setOrderStatus(order.getStatus()); |
| | | payLog.setOptUserId(order.getMemberId()); |
| | | payLog.setOptUserType(0); |
| | | payLog.setCreateTime(now); |
| | | payLog.setDeleted(Constants.ZERO); |
| | | orderLogService.create(payLog); |
| | | |
| | | // 通知会员:订单待核验 |
| | | sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.WAIT_VERIFY, order.getId(), |
| | |
| | | public PayResponse payShopDeposit(Integer memberId) { |
| | | // 1. 查询门店信息 |
| | | ShopInfo shopInfo = shopInfoMapper.selectOne(new QueryWrapper<ShopInfo>().lambda() |
| | | .eq(ShopInfo::getDeleted,Constants.ZERO) |
| | | .eq(ShopInfo::getRegionMemberId,memberId)); |
| | | if (shopInfo == null) { |
| | | throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "门店不存在"); |
| | |
| | | otherOrders.setUpdateTime(now); |
| | | otherOrdersMapper.updateById(otherOrders); |
| | | |
| | | // 4. 查询门店信息(通过注册会员主键关联) |
| | | // 4. 查询变更版本门店信息(通过注册会员主键关联) |
| | | ShopInfo shopInfo = shopInfoMapper.selectOne(new QueryWrapper<ShopInfo>().lambda() |
| | | .eq(ShopInfo::getRegionMemberId, otherOrders.getMemberId()) |
| | | .eq(ShopInfo::getDeleted, Constants.ZERO) |
| | | .eq(ShopInfo::getVersionType, Constants.ONE) |
| | | .last("limit 1")); |
| | | if (shopInfo == null) { |
| | | throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "门店不存在"); |
| | | throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "门店变更记录不存在"); |
| | | } |
| | | // 5. 更新门店状态:已支付押金 |
| | | shopInfo.setAuditStatus(Constants.THREE); // 3=已支付押金 |
| | | // 5. 更新变更版本支付状态 |
| | | Member member = memberMapper.selectById(otherOrders.getMemberId()); |
| | | shopInfo.setAuditStatus(Constants.THREE); |
| | | shopInfo.setPayStatus(Constants.ONE); |
| | | shopInfo.setPayTime(now); |
| | | shopInfo.setWxExternalNo(wxTradeNo); |
| | | shopInfo.setCode(otherOrders.getCode()); |
| | | Member member = memberMapper.selectById(otherOrders.getMemberId()); |
| | | if (member != null) { |
| | | shopInfo.setPayMemberOpenId(member.getOpenid()); |
| | | } |
| | | shopInfo.setUpdateTime(now); |
| | | shopInfoMapper.updateById(shopInfo); |
| | | |
| | | // 6. 同步更新正式版本状态为已支付押金 |
| | | if (shopInfo.getRelationShopId() != null) { |
| | | ShopInfo officialShop = shopInfoMapper.selectById(shopInfo.getRelationShopId()); |
| | | if (officialShop != null) { |
| | | officialShop.setAuditStatus(Constants.THREE); |
| | | officialShop.setUpdateTime(now); |
| | | shopInfoMapper.updateById(officialShop); |
| | | } |
| | | } |
| | | |
| | | // 短信通知门店:成功入驻 |
| | | String rawPassword = shopInfo.getTelephone() != null && shopInfo.getTelephone().length() >= 6 |
| | |
| | | sendSmsNotify(shopInfo.getTelephone(), |
| | | Constants.SmsNotify.SHOP_AUTH_SUCCESS, |
| | | "storeName", shopInfo.getName(), |
| | | "phone", shopInfo.getTelephone() != null ? shopInfo.getTelephone() : "", |
| | | "password", rawPassword); |
| | | |
| | | // 6. 押金支付完成后,若城市未开通则自动开通 |
| | | if (shopInfo.getAreaId() != null) { |
| | | Areas shopArea = areasBiz.resolveArea(shopInfo.getAreaId()); |
| | | Areas shopArea = aareasMapper.selectById(shopInfo.getAreaId()); |
| | | if (shopArea != null && shopArea.getParentId() != null) { |
| | | Areas cityArea = areasBiz.resolveArea(shopArea.getParentId()); |
| | | Areas cityArea = aareasMapper.selectById(shopArea.getParentId()); |
| | | if (cityArea != null && !Constants.equalsInteger(cityArea.getStatus(), Constants.ONE)) { |
| | | cityArea.setStatus(Constants.ONE); |
| | | cityArea.setEditDate(now); |
| | | areasService.updateById(cityArea); |
| | | aareasMapper.updateById(cityArea); |
| | | areasService.cacheData(); |
| | | } |
| | | } |
| | |
| | | .eq(Orders::getDeleted, Constants.ZERO) |
| | | .eq(Orders::getStatus, Constants.OrderStatus.finished.getStatus()) |
| | | .eq(Orders::getSettlementStatus, Constants.ZERO) |
| | | .le(Orders::getFinishTime, deadline)); |
| | | .le(Orders::getFinishTime, deadline) |
| | | ); |
| | | if (ordersList == null || ordersList.isEmpty()) { |
| | | return; |
| | | } |
| | |
| | | } else if (Constants.equalsInteger(revenue.getMemberType(), Constants.TWO)) { |
| | | // 门店:通过 memberId 查 ShopInfo(regionMemberId),更新 balance / totalBalance |
| | | ShopInfo shop = shopInfoMapper.selectOne(new QueryWrapper<ShopInfo>().lambda() |
| | | .eq(ShopInfo::getRegionMemberId, revenue.getMemberId()) |
| | | .eq(ShopInfo::getId, revenue.getMemberId()) |
| | | .eq(ShopInfo::getDeleted, Constants.ZERO) |
| | | .last("limit 1")); |
| | | if (shop != null) { |
| | |
| | | } |
| | | order.setMemberVerifyCode(generateVerifyCode()); |
| | | ordersMapper.updateById(order); |
| | | // 寄存时图片必填 |
| | | // if (images == null || images.isEmpty()) { |
| | | // throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "请上传寄存图片"); |
| | | // } |
| | | // 保存寄存图片(obj_type=2 订单寄存图片,最多3张) |
| | | saveVerifyImages(order.getId(), images, Constants.FileType.ORDER_DEPOSIT.getKey(), shopId); |
| | | // 记录订单日志 |
| | |
| | | // 异地寄存 → 待抢单 |
| | | sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.WAIT_GRAB, order.getId(), |
| | | "orderNo", order.getCode()); |
| | | // 推送通知司机 |
| | | pushDriverNewOrder(order); |
| | | } else { |
| | | // 就地寄存 → 待取件提醒 |
| | | sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.WAIT_PICKUP_REMIND, order.getId(), |
| | |
| | | } |
| | | // 待取件(5) → 已完成(7) |
| | | order.setStatus(Constants.OrderStatus.finished.getStatus()); |
| | | order.setConfirmArriveTime(now); |
| | | order.setFinishTime(now); |
| | | order.setInvoiceStatus(Constants.ONE); |
| | | ordersMapper.updateById(order); |
| | | // 就地寄存(type=0)取件时图片不必填,其他类型取件必填 |
| | | if (!Constants.equalsInteger(order.getType(), Constants.ZERO)) { |
| | | if (images == null || images.isEmpty()) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "请上传取件图片"); |
| | | } |
| | | } |
| | | // 订单完成,释放核销码 |
| | | String verifyCode = order.getMemberVerifyCode(); |
| | | if (StringUtils.isNotBlank(verifyCode)) { |
| | |
| | | "orderNo", order.getCode(), |
| | | "settleDays", settleDays != null ? settleDays : "7"); |
| | | } |
| | | } else { |
| | | }else if(Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.delivering.getKey())){ |
| | | // 仅异地寄存 + 有取件门店 + 派送中(4) 可核销 |
| | | if (!Constants.equalsInteger(order.getType(), Constants.ONE)) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "仅异地寄存订单支持司机核销"); |
| | | } |
| | | if (order.getTakeShopId() == null) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单无取件门店,无需司机核销"); |
| | | } |
| | | if (!Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.delivering.getStatus())) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "当前订单状态不允许核销"); |
| | | } |
| | | // 校验取件门店与当前登录门店一致 |
| | | if (!shopId.equals(order.getTakeShopId())) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单不属于当前门店,无法核销"); |
| | | } |
| | | // 派送中(4) → 已到店(5) |
| | | order.setStatus(Constants.OrderStatus.arrived.getStatus()); |
| | | order.setArriveTime(new Date()); |
| | | if (StringUtils.isNotBlank(remark)) { |
| | | order.setRemark(remark); |
| | | } |
| | | ordersMapper.updateById(order); |
| | | |
| | | // 释放司机核销码 |
| | | releaseVerifyCode(order.getDriverVerifyCode()); |
| | | |
| | | // 保存附件(obj_type=3 门店入库图片,最多3张) |
| | | saveVerifyImages(order.getId(), images, Constants.FileType.ORDER_TAKE.getKey(), order.getAcceptDriver()); |
| | | |
| | | // 通知会员:订单已送达 |
| | | 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); |
| | | } |
| | | |
| | | // 短信通知会员:行李已送达 |
| | | Member arrivedMember = memberMapper.selectById(order.getMemberId()); |
| | | if (arrivedMember != null) { |
| | | sendSmsNotify(arrivedMember.getTelephone(), Constants.SmsNotify.MEMBER_ARRIVED, |
| | | "orderNo", order.getCode(), |
| | | "address", destination, |
| | | "code", order.getMemberVerifyCode() != null ? order.getMemberVerifyCode() : ""); |
| | | } |
| | | }else { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "当前订单状态不允许核销"); |
| | | } |
| | | } |
| | |
| | | // 异地寄存 → 待抢单 |
| | | sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.WAIT_GRAB, order.getId(), |
| | | "orderNo", order.getCode()); |
| | | // 推送通知司机 |
| | | pushDriverNewOrder(order); |
| | | } else { |
| | | // 就地寄存 → 待取件提醒 |
| | | sendOrderNotice(order.getMemberId(), Constants.MemberOrderNotify.WAIT_PICKUP_REMIND, order.getId(), |
| | | "orderNo", order.getCode(), "shopName", shopName); |
| | | } |
| | | // 异常订单寄存核销:标记原订单完成 |
| | | if (Constants.equalsInteger(order.getExceptionStatus(), Constants.ONE) && order.getRelationOrderId() != null) { |
| | | Orders originalOrder = ordersMapper.selectById(order.getRelationOrderId()); |
| | | if (originalOrder != null) { |
| | | originalOrder.setStatus(Constants.OrderStatus.finished.getStatus()); |
| | | originalOrder.setInvoiceStatus(Constants.ONE); |
| | | originalOrder.setFinishTime(now); |
| | | originalOrder.setUpdateTime(now); |
| | | ordersMapper.updateById(originalOrder); |
| | | // 触发原订单收益计算 |
| | | calculateAndSaveOrderFees(originalOrder.getId()); |
| | | generateRevenueRecords(originalOrder.getId()); |
| | | // 通知会员:订单已完成 |
| | | sendOrderNotice(originalOrder.getMemberId(), Constants.MemberOrderNotify.FINISHED, originalOrder.getId(), |
| | | "orderNo", originalOrder.getCode()); |
| | | // 通知司机:订单已完成 |
| | | if (originalOrder.getAcceptDriver() != null) { |
| | | String settleDays = operationConfigBiz.getConfig().getSettlementDate(); |
| | | sendDriverNotice(originalOrder.getAcceptDriver(), Constants.DriverOrderNotify.FINISHED, originalOrder.getId(), |
| | | "orderNo", originalOrder.getCode(), |
| | | "settleDays", settleDays != null ? settleDays : "7"); |
| | | } |
| | | } |
| | | } |
| | | } else if (Constants.equalsInteger(status, Constants.OrderStatus.arrived.getStatus())) { |
| | | // 异地寄存 + 无取件门店 → 无法核销(客户自取,无门店操作) |
| | |
| | | } |
| | | // 待取件(5) → 已完成(7) |
| | | order.setStatus(Constants.OrderStatus.finished.getStatus()); |
| | | order.setInvoiceStatus(Constants.ONE); |
| | | order.setConfirmArriveTime(now); |
| | | ordersMapper.updateById(order); |
| | | // 订单完成,释放核销码 |
| | |
| | | // 7. 更新订单状态为已完成 |
| | | Date now = new Date(); |
| | | order.setStatus(Constants.OrderStatus.finished.getStatus()); |
| | | order.setInvoiceStatus(Constants.ONE); |
| | | order.setFinishTime(now); |
| | | order.setUpdateTime(now); |
| | | ordersMapper.updateById(order); |
| | |
| | | ordersRefundMapper.insert(refundRecord); |
| | | |
| | | // 调用微信退款V3(放在最后,确保前置操作全部成功) |
| | | String outRefundNo2 = ID.nextGUID(); |
| | | Refund refundResult = wxPayV3Service.refund( |
| | | order.getOutTradeNo(), order.getPayAmount(), order.getRefundAmount(), |
| | | outRefundNo2, order.getOutTradeNo(), order.getPayAmount(), order.getRefundAmount(), |
| | | "订单退款", wxPayProperties.getV3RefundNotifyUrl()); |
| | | |
| | | // 退款成功后回填退款单号,标记退款中 |
| | | refundRecord.setRefundCode(refundResult.getOutRefundNo()); |
| | | refundRecord.setRefundCode(outRefundNo2); |
| | | refundRecord.setStatus(Constants.ZERO); // 退款中 |
| | | ordersRefundMapper.updateById(refundRecord); |
| | | } |
| | |
| | | if (!Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.arrived.getStatus())) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "当前订单状态不允许确认收货"); |
| | | } |
| | | // 5.1 校验异常状态 |
| | | if (Constants.equalsInteger(order.getExceptionStatus(), Constants.ONE)) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "订单异常,已被转存,请联系管理员或查看短信"); |
| | | } |
| | | |
| | | // 6. 更新订单状态为已完成 |
| | | Date now = new Date(); |
| | | order.setStatus(Constants.OrderStatus.finished.getStatus()); |
| | | order.setInvoiceStatus(Constants.ONE); |
| | | order.setFinishTime(now); |
| | | order.setUpdateTime(now); |
| | | ordersMapper.updateById(order); |
| | |
| | | BigDecimal depositRate = order.getDepositShopFeeRata() != null ? order.getDepositShopFeeRata() : BigDecimal.ZERO; |
| | | BigDecimal takeRate = order.getTakeShopFeeRata() != null ? order.getTakeShopFeeRata() : BigDecimal.ZERO; |
| | | BigDecimal driverRate = order.getDriverFeeRata() != null ? order.getDriverFeeRata() : BigDecimal.ZERO; |
| | | Long exceptionFeeVal = order.getExceptionFee() != null ? order.getExceptionFee() : 0L; |
| | | |
| | | //存件门店收益 |
| | | Long depositShopFee = new BigDecimal(totalAmount) |
| | |
| | | driverFee = new BigDecimal(totalAmount) |
| | | .multiply(driverRate) |
| | | .setScale(0, RoundingMode.HALF_UP) |
| | | .longValue() |
| | | + exceptionFeeVal; |
| | | .longValue(); |
| | | |
| | | // 异地寄存且有取件门店:加上取件门店收益 |
| | | if (order.getTakeShopId() != null) { |
| | |
| | | // 存件门店收益 |
| | | if (depositShopFee > 0 && order.getDepositShopId() != null) { |
| | | ShopInfo depositShop = shopInfoMapper.selectById(order.getDepositShopId()); |
| | | if (depositShop != null && depositShop.getRegionMemberId() != null) { |
| | | revenueMapper.insert(buildRevenue(depositShop.getRegionMemberId(), Constants.TWO, |
| | | if (depositShop != null && depositShop.getId() != null) { |
| | | revenueMapper.insert(buildRevenue(depositShop.getId(), Constants.TWO, |
| | | depositShopFee, orderId, order.getCode())); |
| | | } |
| | | } |
| | |
| | | // 取件门店收益(异地寄存且有取件门店) |
| | | if (takeShopFee > 0 && order.getTakeShopId() != null) { |
| | | ShopInfo takeShop = shopInfoMapper.selectById(order.getTakeShopId()); |
| | | if (takeShop != null && takeShop.getRegionMemberId() != null) { |
| | | revenueMapper.insert(buildRevenue(takeShop.getRegionMemberId(), Constants.TWO, |
| | | if (takeShop != null && takeShop.getId() != null) { |
| | | revenueMapper.insert(buildRevenue(takeShop.getId(), Constants.TWO, |
| | | takeShopFee, orderId, order.getCode())); |
| | | } |
| | | } |
| | |
| | | driverFee, orderId, order.getCode())); |
| | | } |
| | | } |
| | | |
| | | // 异常订单:司机异常补偿(异地 + 异常标记 + 有异常补偿金额) |
| | | if (Constants.equalsInteger(order.getType(), Constants.ONE) |
| | | && Constants.equalsInteger(order.getExceptionStatus(), Constants.ONE) |
| | | && order.getExceptionFee() != null && order.getExceptionFee() > 0 |
| | | && order.getAcceptDriver() != null) { |
| | | DriverInfo driver = driverInfoMapper.selectById(order.getAcceptDriver()); |
| | | if (driver != null && driver.getMemberId() != null) { |
| | | Revenue exRevenue = new Revenue(); |
| | | exRevenue.setMemberId(driver.getMemberId()); |
| | | exRevenue.setMemberType(Constants.ONE); // 1=司机 |
| | | exRevenue.setType(5); // 5=异常金额 |
| | | exRevenue.setOptType(Constants.ONE); // 1=收入 |
| | | exRevenue.setAmount(order.getExceptionFee()); |
| | | exRevenue.setVaildStatus(Constants.ZERO); |
| | | exRevenue.setObjId(orderId); |
| | | exRevenue.setObjType(Constants.ZERO); |
| | | exRevenue.setStatus(Constants.ZERO); |
| | | exRevenue.setOrderNo(order.getCode()); |
| | | exRevenue.setDeleted(Constants.ZERO); |
| | | exRevenue.setCreateTime(now); |
| | | revenueMapper.insert(exRevenue); |
| | | } |
| | | } |
| | | |
| | | // 司机奖励金(仅生成奖励记录,不直接入账) |
| | | generateDriverReward(order); |
| | | } |
| | | |
| | | /** |
| | | * 生成司机奖励记录 |
| | | * 规则: |
| | | * 已奖励次数 < registerRewardOrderCount → 注册奖励(type=0, amount=registerRewardAmount) |
| | | * registerRewardOrderCount <= 已奖励次数 < (registerRewardOrderCount + platformRewardOrderCount) → 平台奖励(type=1, amount=platformRewardAmount) |
| | | * 超出总名额不奖励 |
| | | */ |
| | | private void generateDriverReward(Orders order) { |
| | | if (order.getAcceptDriver() == null) return; |
| | | DriverInfo driver = driverInfoMapper.selectById(order.getAcceptDriver()); |
| | | if (driver == null || driver.getId() == null) return; |
| | | |
| | | // 读取奖励配置 |
| | | String registerCountStr = operationConfigBiz.getConfig().getRegisterRewardOrderCount(); |
| | | String registerAmountStr = operationConfigBiz.getConfig().getRegisterRewardAmount(); |
| | | String platformCountStr = operationConfigBiz.getConfig().getPlatformRewardOrderCount(); |
| | | String platformAmountStr = operationConfigBiz.getConfig().getPlatformRewardAmount(); |
| | | if (StringUtils.isAnyBlank(registerCountStr, registerAmountStr, platformCountStr, platformAmountStr)) return; |
| | | |
| | | int registerCount = Integer.parseInt(registerCountStr); |
| | | long registerAmount = new BigDecimal(registerAmountStr).multiply(BigDecimal.valueOf(100)).longValue(); |
| | | int platformCount = Integer.parseInt(platformCountStr); |
| | | long platformAmount = new BigDecimal(platformAmountStr).multiply(BigDecimal.valueOf(100)).longValue(); |
| | | int totalRewardSlots = registerCount + platformCount; // 总奖励名额 |
| | | |
| | | // 查询司机已奖励次数 |
| | | Long rewarded = rewardRecordMapper.selectCount(new QueryWrapper<RewardRecord>().lambda() |
| | | .eq(RewardRecord::getDriverId, driver.getId()) |
| | | .eq(RewardRecord::getDeleted, Constants.ZERO)); |
| | | int count = rewarded.intValue(); |
| | | if (count >= totalRewardSlots) return; // 已超出奖励名额 |
| | | |
| | | // 判断本次奖励类型和金额 |
| | | int rewardType; |
| | | long rewardAmount; |
| | | if (count < registerCount) { |
| | | // 注册奖励阶段 |
| | | rewardType = Constants.ZERO; |
| | | rewardAmount = registerAmount; |
| | | } else { |
| | | // 平台奖励阶段 |
| | | rewardType = Constants.ONE; |
| | | rewardAmount = platformAmount; |
| | | } |
| | | if (rewardAmount <= 0) return; |
| | | |
| | | // 创建奖励记录(状态=0待领取,后续由司机领取时入账) |
| | | RewardRecord record = new RewardRecord(); |
| | | record.setOrderId(order.getId()); |
| | | record.setDriverId(driver.getId()); |
| | | record.setStatus(Constants.ZERO); // 0=待领取 |
| | | record.setType(rewardType); // 0=注册奖励, 1=平台奖励 |
| | | record.setAmount(rewardAmount); |
| | | record.setDeleted(Constants.ZERO); |
| | | record.setCreateTime(new Date()); |
| | | rewardRecordMapper.insert(record); |
| | | } |
| | | |
| | | /** |
| | |
| | | new QueryWrapper<OrdersDetail>().lambda() |
| | | .eq(OrdersDetail::getOrderId, orderId) |
| | | .eq(OrdersDetail::getDeleted, Constants.ZERO)); |
| | | OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(order, details); |
| | | |
| | | Date now = new Date(); |
| | | |
| | | // 异常订单逾期逻辑 |
| | | if (Constants.equalsInteger(order.getExceptionStatus(), Constants.ONE) |
| | | && order.getRelationOrderId() != null) { |
| | | Orders originalOrder = ordersMapper.selectById(order.getRelationOrderId()); |
| | | long driverExceptionFee = (originalOrder != null && originalOrder.getExceptionFee() != null) |
| | | ? originalOrder.getExceptionFee() : 0L; |
| | | long shopExceptionFee = order.getShopCompensationAmount() != null ? order.getShopCompensationAmount() : 0L; |
| | | long totalExceptionFee = driverExceptionFee + shopExceptionFee; |
| | | |
| | | // 判断是否当天取件(按 expectedTakeTime 日期) |
| | | boolean sameDay = isSameDay(now, order.getExpectedTakeTime()); |
| | | |
| | | long overdueFee; |
| | | int overdueDays; |
| | | if (sameDay) { |
| | | // 当天取件:逾期费用 = 异常费用总和 |
| | | overdueFee = totalExceptionFee; |
| | | overdueDays = totalExceptionFee > 0 ? 1 : 0; |
| | | } else { |
| | | // 非当天取件:正常逾期计算 + 司机异常费用(不含门店异常费用) |
| | | OverdueFeeVO normalOverdue = calculateOverdueFeeInternal(order, details); |
| | | long normalFee = (normalOverdue != null && normalOverdue.getOverdueFee() != null) |
| | | ? normalOverdue.getOverdueFee() : 0L; |
| | | overdueFee = normalFee + driverExceptionFee; |
| | | overdueDays = (normalOverdue != null && normalOverdue.getOverdueDays() != null) |
| | | ? normalOverdue.getOverdueDays() : 0; |
| | | } |
| | | |
| | | order.setConfirmArriveTime(now); |
| | | order.setUpdateTime(now); |
| | | if (overdueFee > 0) { |
| | | order.setOverdueStatus(Constants.ONE); |
| | | order.setOverdueDays(overdueDays); |
| | | order.setOverdueAmount(overdueFee); |
| | | ordersMapper.updateById(order); |
| | | String overdueLogInfo = "门店【" + shopName + "】确认顾客到店,逾期" + overdueDays |
| | | + "天,逾期费用" + Constants.getFormatMoney(overdueFee) + "元"; |
| | | saveShopVerifyLog(order, Constants.OrderLogType.shopConfirmArriveOverdue, overdueLogInfo, null, shopId); |
| | | } else { |
| | | order.setOverdueStatus(Constants.ZERO); |
| | | ordersMapper.updateById(order); |
| | | saveShopVerifyLog(order, Constants.OrderLogType.shopConfirmArrive, |
| | | "门店【" + shopName + "】确认顾客到店,未逾期", null, shopId); |
| | | } |
| | | return; |
| | | } |
| | | |
| | | // 普通订单逾期计算 |
| | | OverdueFeeVO overdueInfo = calculateOverdueFeeInternal(order, details); |
| | | |
| | | if (overdueInfo.getOverdue() && overdueInfo.getOverdueDays() > 0) { |
| | | // 存在逾期:标记逾期状态,订单保持当前状态 |
| | |
| | | int actualDays = calcActualDepositDays(now, order.getDepositTime()); |
| | | order.setDepositDays(actualDays); |
| | | |
| | | int estimatedDays = order.getEstimatedDepositDays() != null ? order.getEstimatedDepositDays() : 1; |
| | | int refundDays = estimatedDays - actualDays; |
| | | if (refundDays > 0) { |
| | | // 退款金额 = 退款天数 × Σ(物品单价 × 数量) |
| | | long dailyBaseFee = 0L; |
| | | for (OrdersDetail d : details) { |
| | | dailyBaseFee += (d.getUnitPrice() != null ? d.getUnitPrice() : 0L) |
| | | * (d.getNum() != null ? d.getNum() : 0); |
| | | long dailyBaseFee = 0L; |
| | | for (OrdersDetail d : details) { |
| | | dailyBaseFee += (d.getLocallyPrice() != null ? d.getLocallyPrice() : 0L) |
| | | * (d.getNum() != null ? d.getNum() : 0); |
| | | } |
| | | |
| | | if (Constants.equalsInteger(order.getIsConverted(), Constants.ONE)) { |
| | | // 转换订单:实际费用与已付金额对比 |
| | | long actualFee = (long) actualDays * dailyBaseFee; |
| | | long payAmount = order.getPayAmount() != null ? order.getPayAmount() : 0L; |
| | | if (actualFee < payAmount) { |
| | | order.setRefundAmount(payAmount - actualFee); |
| | | } |
| | | long refundAmount = (long) refundDays * dailyBaseFee; |
| | | order.setRefundAmount(refundAmount); |
| | | } else { |
| | | // 普通订单:按预计天数与实际天数差计算退款 |
| | | int estimatedDays = order.getEstimatedDepositDays() != null ? order.getEstimatedDepositDays() : 1; |
| | | int refundDays = estimatedDays - actualDays; |
| | | if (refundDays > 0) { |
| | | order.setRefundAmount((long) refundDays * dailyBaseFee); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | return "司机已取件,正运往目的地"; |
| | | } |
| | | if (Constants.equalsInteger(status, Constants.OrderStatus.arrived.getStatus())) { |
| | | return "行李已送达服务点,请及时前往取件"; |
| | | if(Constants.equalsInteger(order.getType(),Constants.ZERO)){ |
| | | return "行李已寄存,请凭取件码前往指定门店取件~"; |
| | | }else{ |
| | | return "行李已送达服务点,请及时前往取件"; |
| | | } |
| | | |
| | | } |
| | | if (Constants.equalsInteger(status, Constants.OrderStatus.finished.getStatus())) { |
| | | if (Constants.equalsInteger(order.getCommentStatus(), Constants.ONE)) { |
| | |
| | | * 供分页等已查询明细的业务场景复用,避免重复查询 |
| | | */ |
| | | private OverdueFeeVO calculateOverdueFeeInternal(Orders order, List<OrdersDetail> details) { |
| | | if (CollectionUtils.isEmpty(details)) { |
| | | if (CollectionUtils.isEmpty(details)|| |
| | | Constants.equalsInteger(order.getStatus(),Constants.ZERO) |
| | | || Constants.equalsInteger(order.getStatus(),Constants.ONE) |
| | | ) { |
| | | OverdueFeeVO vo = new OverdueFeeVO(); |
| | | vo.setOverdue(false); |
| | | vo.setOverdueDays(0); |
| | |
| | | |
| | | if (Constants.equalsInteger(order.getType(), Constants.ZERO)) { |
| | | // ========== 就地寄存 ========== |
| | | |
| | | // 转换订单(异地转就地):按寄存时间到当前时间计算实际费用 |
| | | if (Constants.equalsInteger(order.getIsConverted(), Constants.ONE)) { |
| | | int actualDays = calcActualDepositDays(now, order.getDepositTime()); |
| | | long actualFee = (long) actualDays * dailyBaseFee; |
| | | long payAmount = order.getPayAmount() != null ? order.getPayAmount() : 0L; |
| | | |
| | | OverdueFeeVO vo = new OverdueFeeVO(); |
| | | vo.setDailyBaseFee(dailyBaseFee); |
| | | if (actualFee <= payAmount) { |
| | | // 实际费用 <= 已付金额:未逾期 |
| | | vo.setOverdue(false); |
| | | vo.setOverdueDays(0); |
| | | vo.setOverdueFee(0L); |
| | | } else { |
| | | // 实际费用 > 已付金额:逾期,差价为逾期费用 |
| | | int overDays = actualDays - (order.getEstimatedDepositDays() != null ? order.getEstimatedDepositDays() : 1); |
| | | vo.setOverdue(true); |
| | | vo.setOverdueDays(Math.max(overDays, 1)); |
| | | vo.setOverdueFee(actualFee - payAmount); |
| | | } |
| | | return vo; |
| | | } |
| | | |
| | | // 普通就地寄存 |
| | | overdueDays = calcLocalOverdueDays(now, order.getExpectedTakeTime()); |
| | | overdueFee = (long) overdueDays * dailyBaseFee; |
| | | |
| | |
| | | return Math.max(days, 1); |
| | | } |
| | | |
| | | private boolean isSameDay(Date d1, Date d2) { |
| | | if (d1 == null || d2 == null) return false; |
| | | Calendar c1 = Calendar.getInstance(); |
| | | c1.setTime(d1); |
| | | Calendar c2 = Calendar.getInstance(); |
| | | c2.setTime(d2); |
| | | return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR) |
| | | && c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR); |
| | | } |
| | | |
| | | /** |
| | | * 就地寄存逾期天数计算 |
| | | * 过了预计取件日期的次日(只比较年月日)后开始计逾期天数 |
| | |
| | | QueryWrapper<Orders> wrapper = new QueryWrapper<>(); |
| | | wrapper.eq("MEMBER_ID", memberId) |
| | | .in("STATUS", 0, 1, 2, 3, 4, 5) |
| | | .orderByDesc("CREATE_TIME") |
| | | .orderByAsc("CREATE_TIME") |
| | | .last("LIMIT 1"); |
| | | Orders order = ordersMapper.selectOne(wrapper); |
| | | if (order == null) { |
| | |
| | | "time", String.valueOf(noGrabMinutes)); |
| | | } |
| | | // 标记已通知 + 记录通知时间 |
| | | order.setPlatformSmsNotified(Constants.ONE); |
| | | order.setPlatformSmsNotifiedTime(now); |
| | | order.setUpdateTime(now); |
| | | ordersMapper.updateById(order); |
| | |
| | | |
| | | // 更新订单状态为已完成 |
| | | order.setStatus(Constants.OrderStatus.finished.getStatus()); |
| | | order.setInvoiceStatus(Constants.ONE); |
| | | order.setFinishTime(now); |
| | | order.setUpdateTime(now); |
| | | ordersMapper.updateById(order); |
| | |
| | | if (StringUtils.isBlank(phone)) { |
| | | return; |
| | | } |
| | | if (!smsNotify.isEnabled()) { |
| | | return; |
| | | } |
| | | String content = smsNotify.format(paramPairs); |
| | | try { |
| | | JSONObject templateParam = new JSONObject(); |
| | | for (int i = 0; i < paramPairs.length - 1; i += 2) { |
| | | templateParam.put(paramPairs[i], paramPairs[i + 1]); |
| | | } |
| | | boolean result = AliSmsService.sendSms(phone, smsNotify.getTemplateCode(), |
| | | String error = AliSmsService.sendSms(phone, smsNotify.getTemplateCode(), |
| | | templateParam.toJSONString()); |
| | | if (result) { |
| | | log.info("短信发送成功: phone={}, template={}", phone, smsNotify.name()); |
| | | } else { |
| | | log.warn("短信发送失败: phone={}, template={}", phone, smsNotify.name()); |
| | | } |
| | | // 存储短信记录 |
| | | Smsrecord record = new Smsrecord(); |
| | | record.setPhone(phone); |
| | | record.setContent(content); |
| | | record.setType(Constants.ONE); // 1=订单通知 |
| | | record.setStatus(result ? Constants.ONE : Constants.ZERO); // 1=已发送, 0=发送失败 |
| | | record.setType(Constants.ONE); |
| | | record.setStatus(error == null ? Constants.ONE : Constants.ZERO); |
| | | if (error != null) { |
| | | record.setRemark(error); |
| | | } |
| | | record.setCreateTime(new Date()); |
| | | record.setDeleted(Constants.ZERO); |
| | | smsrecordMapper.insert(record); |
| | | } catch (Exception e) { |
| | | log.error("短信发送异常: phone={}, template={}, error={}", phone, smsNotify.name(), e.getMessage()); |
| | | // 异常也记录 |
| | | try { |
| | | Smsrecord record = new Smsrecord(); |
| | | record.setPhone(phone); |
| | | record.setContent(content); |
| | | record.setType(Constants.ONE); |
| | | record.setStatus(Constants.ZERO); // 发送失败 |
| | | record.setStatus(Constants.ZERO); |
| | | record.setRemark(e.getMessage()); |
| | | record.setCreateTime(new Date()); |
| | | record.setDeleted(Constants.ZERO); |
| | | smsrecordMapper.insert(record); |
| | |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void printOrderLabel(Integer orderId, Integer shopId) { |
| | | Orders orders = ordersMapper.selectById(orderId); |
| | | if (orders == null) { |
| | | throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "订单不存在"); |
| | | } |
| | | if (!Constants.equalsInteger(orders.getDepositShopId(), shopId)) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "当前门店不是该订单的存件门店"); |
| | | } |
| | | if (orders.getStatus() < 1 || orders.getStatus() > 7) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "当前订单状态不允许打印"); |
| | | } |
| | | ShopInfo shop = shopInfoMapper.selectById(shopId); |
| | | if (shop == null) { |
| | | throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "存件门店不存在"); |
| | | } |
| | | if (StringUtils.isBlank(shop.getPrinterSn())) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "存件门店未绑定打印机"); |
| | | } |
| | | List<OrdersDetail> detailList = ordersDetailMapper.selectList( |
| | | new QueryWrapper<OrdersDetail>().lambda().eq(OrdersDetail::getOrderId, orderId)); |
| | | // 拼接客户信息 |
| | | String userInfo = ""; |
| | | if (StringUtils.isNotBlank(orders.getTakeUser())) { |
| | | String phone = orders.getTakePhone(); |
| | | userInfo = orders.getTakeUser(); |
| | | if (StringUtils.isNotBlank(phone) && phone.length() >= 4) { |
| | | userInfo += "(" + phone.substring(phone.length() - 4) + ")"; |
| | | } |
| | | } |
| | | |
| | | //序号:商铺ID-日期-自增长序号(如 32-06-001) |
| | | String dateStr = new SimpleDateFormat("dd").format(orders.getPayTime() != null ? orders.getPayTime() : new Date()); |
| | | String autoNumStr = String.format("%03d", orders.getAutoNum() != null ? orders.getAutoNum() : 0); |
| | | String sort = shopId + "-" + dateStr + "-" + autoNumStr; |
| | | // String content = printService.getPrintContent(shop.getName(), detailList, userInfo, orders.getCode(), orders.getRemark(), |
| | | String content = printService.getPrintContent(shop.getName(), detailList, userInfo, orders.getCode(), sort, |
| | | orders.getTakeLocationRemark(), |
| | | orders.getPayTime() != null ? new SimpleDateFormat("yyyy-MM-dd HH:mm").format(orders.getPayTime()) : "", |
| | | orders.getExpectedTakeTime() != null ? new SimpleDateFormat("yyyy-MM-dd HH:mm").format(orders.getExpectedTakeTime()) : ""); |
| | | if (StringUtils.isBlank(content)) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "打印内容为空"); |
| | | } |
| | | PrintRequest request = new PrintRequest(); |
| | | xyyConfig.createRequestHeader(request); |
| | | request.setSn(shop.getPrinterSn()); |
| | | request.setContent(content); |
| | | request.setCopies(1); |
| | | request.setVoice(2); |
| | | request.setMode(0); |
| | | com.doumee.config.xyy.vo.ObjectRestResponse<String> resp = printService.printLabel(request); |
| | | if (resp.getCode() != 0) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "打印失败:" + resp.getMsg()); |
| | | } |
| | | log.info("订单标签打印成功: orderId={}, orderIndex={}", orderId, resp.getData()); |
| | | } |
| | | |
| | | /** |
| | | * 异地订单寄存成功后推送通知给司机 |
| | | * 1、未加急:推送同城且门店配送范围内接单中的司机 |
| | | * 2、加急:推送同城所有接单中的司机 |
| | | * 城市匹配:司机AREA_ID关联areas表,逐级parent_id找到城市级,与订单cityId匹配 |
| | | */ |
| | | private void pushDriverNewOrder(Orders order) { |
| | | ShopInfo shop = shopInfoMapper.selectById(order.getDepositShopId()); |
| | | if (shop == null || shop.getLongitude() == null || shop.getLatitude() == null) { |
| | | return; |
| | | } |
| | | String orderCityId = order.getCityId(); |
| | | if (StringUtils.isBlank(orderCityId)) { |
| | | return; |
| | | } |
| | | String distSql = "(ST_Distance_Sphere(POINT(t.LONGITUDE, t.LATITUDE), POINT(" + shop.getLongitude() + ", " + shop.getLatitude() + ")) / 1000)"; |
| | | // MPJ关联查询:司机 → areas(区县) → areas_city(城市),匹配订单cityId |
| | | MPJLambdaWrapper<DriverInfo> wrapper = new MPJLambdaWrapper<>(); |
| | | wrapper.selectAll(DriverInfo.class) |
| | | .leftJoin(Areas.class,Areas::getId,DriverInfo::getAreaId) |
| | | .eq(DriverInfo::getAcceptingStatus, Constants.ONE) |
| | | .eq(DriverInfo::getStatus, Constants.ZERO) |
| | | .eq(DriverInfo::getDeleted, Constants.ZERO) |
| | | .eq(DriverInfo::getAuditStatus, Constants.THREE) |
| | | .eq(Areas::getParentId, orderCityId); |
| | | if (!Constants.equalsInteger(order.getIsUrgent(), Constants.ONE)) { |
| | | // 未加急:门店配送范围内 |
| | | double rangeKm = shop.getDeliveryArea() != null ? shop.getDeliveryArea().doubleValue() : 5.0; |
| | | wrapper.apply(distSql + " <= {0}", rangeKm); |
| | | } |
| | | List<DriverInfo> drivers = driverInfoMapper.selectJoinList(DriverInfo.class, wrapper); |
| | | if (drivers.isEmpty()) { |
| | | return; |
| | | } |
| | | List<String> aliases = new ArrayList<>(); |
| | | for (DriverInfo d : drivers) { |
| | | aliases.add(org.springframework.util.DigestUtils.md5DigestAsHex(d.getTelephone().getBytes())); |
| | | } |
| | | String title = Constants.equalsInteger(order.getIsUrgent(), Constants.ONE) ? "新加急订单" : "新订单"; |
| | | String content = "订单号:" + order.getCode(); |
| | | java.util.Map<String, String> extras = new java.util.HashMap<>(); |
| | | extras.put("orderId", String.valueOf(order.getId())); |
| | | extras.put("orderCode", order.getCode()); |
| | | extras.put("type", "new_order"); |
| | | JPushUtil.sendByAliases(aliases, title, content, extras); |
| | | } |
| | | |
| | | @Override |
| | | public int notifyArrivalPickUp() { |
| | | String timeStr = operationConfigBiz.getConfig().getArrivalPickUpTime(); |
| | | if (StringUtils.isBlank(timeStr)) { |
| | | return 0; |
| | | } |
| | | int minutes; |
| | | try { |
| | | minutes = Integer.parseInt(timeStr); |
| | | } catch (NumberFormatException e) { |
| | | log.warn("即将到达取件时间配置异常: {}", timeStr); |
| | | return 0; |
| | | } |
| | | if (minutes <= 0) { |
| | | return 0; |
| | | } |
| | | // 计算时间窗口:当前时间 + minutes 就是"即将到达"的临界点 |
| | | Date now = new Date(); |
| | | Date threshold = new Date(now.getTime() + (long) minutes * 60 * 1000); |
| | | // 查询:status=5、未通知、就地寄存 or 异地(有取件门店)、预计取件时间在 now~threshold 之间 |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | String nowStr = sdf.format(now); |
| | | String thresholdStr = sdf.format(threshold); |
| | | List<Orders> orders = ordersMapper.selectList(new QueryWrapper<Orders>().lambda() |
| | | .eq(Orders::getStatus, Constants.OrderStatus.arrived.getStatus()) |
| | | .eq(Orders::getPayStatus, Constants.ONE) |
| | | .eq(Orders::getDeleted, Constants.ZERO) |
| | | .and(w -> w |
| | | .eq(Orders::getType, Constants.ZERO) |
| | | .or(w2 -> w2.eq(Orders::getType, Constants.ONE).isNotNull(Orders::getTakeShopId)) |
| | | ) |
| | | .ne(Orders::getPickUpNotifyStatus, Constants.ONE) |
| | | .isNotNull(Orders::getExpectedTakeTime) |
| | | .apply("DATE_FORMAT(EXPECTED_TAKE_TIME, '%Y-%m-%d %H:%i:%s') >= {0}", nowStr) |
| | | .apply("DATE_FORMAT(EXPECTED_TAKE_TIME, '%Y-%m-%d %H:%i:%s') <= {0}", thresholdStr)); |
| | | if (orders == null || orders.isEmpty()) { |
| | | return 0; |
| | | } |
| | | int count = 0; |
| | | for (Orders order : orders) { |
| | | Member member = memberMapper.selectById(order.getMemberId()); |
| | | if (member != null && StringUtils.isNotBlank(member.getTelephone())) { |
| | | sendSmsNotify(member.getTelephone(), Constants.SmsNotify.MEMBER_TIME_OUT, |
| | | "orderNo", order.getCode()); |
| | | } |
| | | order.setPickUpNotifyStatus(Constants.ONE); |
| | | ordersMapper.updateById(order); |
| | | count++; |
| | | } |
| | | return count; |
| | | } |
| | | |
| | | @Override |
| | | public Boolean checkOperationRadius(Integer orderId, Integer userId, Integer userType, Double lng, Double lat) { |
| | | String radiusStr = operationConfigBiz.getConfig().getOperationRadius(); |
| | | if (StringUtils.isBlank(radiusStr)) { |
| | | return true; |
| | | } |
| | | double radiusM; |
| | | try { |
| | | radiusM = Double.parseDouble(radiusStr); |
| | | } catch (NumberFormatException e) { |
| | | return true; |
| | | } |
| | | if (radiusM <= 0) { |
| | | return true; |
| | | } |
| | | Orders order = ordersMapper.selectById(orderId); |
| | | if (order == null) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "订单不存在"); |
| | | } |
| | | BigDecimal targetLat; |
| | | BigDecimal targetLgt; |
| | | if (Constants.equalsInteger(userType, Constants.ZERO)) { |
| | | // 门店操作 |
| | | if (Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.waitDeposit.getStatus())) { |
| | | // status=1 门店寄存核验 |
| | | if (!Constants.equalsInteger(order.getDepositShopId(), userId)) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "无权操作该订单"); |
| | | } |
| | | targetLat = order.getDepositLat(); |
| | | targetLgt = order.getDepositLgt(); |
| | | } else if (Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.arrived.getStatus())) { |
| | | // status=5 门店完成核销 |
| | | if (Constants.equalsInteger(order.getType(), Constants.ZERO)) { |
| | | // 就地存取 → 对比存件门店 |
| | | if (!Constants.equalsInteger(order.getDepositShopId(), userId)) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "无权操作该订单"); |
| | | } |
| | | targetLat = order.getDepositLat(); |
| | | targetLgt = order.getDepositLgt(); |
| | | } else { |
| | | // 异地存取 → 对比取件门店 |
| | | if (!Constants.equalsInteger(order.getTakeShopId(), userId)) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "无权操作该订单"); |
| | | } |
| | | targetLat = order.getTakeLat(); |
| | | targetLgt = order.getTakeLgt(); |
| | | } |
| | | } else { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "订单状态不允许此操作"); |
| | | } |
| | | } else if (Constants.equalsInteger(userType, Constants.ONE)) { |
| | | // 司机操作 |
| | | if (Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.deposited.getStatus())) { |
| | | // status=2 司机取件 |
| | | if (!Constants.equalsInteger(order.getAcceptDriver(), userId)) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "无权操作该订单"); |
| | | } |
| | | targetLat = order.getDepositLat(); |
| | | targetLgt = order.getDepositLgt(); |
| | | } else if (Constants.equalsInteger(order.getStatus(), Constants.OrderStatus.delivering.getStatus())) { |
| | | // status=4 司机送达 |
| | | if (!Constants.equalsInteger(order.getAcceptDriver(), userId)) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "无权操作该订单"); |
| | | } |
| | | targetLat = order.getTakeLat(); |
| | | targetLgt = order.getTakeLgt(); |
| | | } else { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "订单状态不允许此操作"); |
| | | } |
| | | } else { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "用户类型不合法"); |
| | | } |
| | | if (targetLat == null || targetLgt == null) { |
| | | return true; |
| | | } |
| | | double distanceKm = GeoUtils.haversineDistance(lat, lng, targetLat.doubleValue(), targetLgt.doubleValue()); |
| | | return distanceKm * 1000 <= radiusM; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = {Exception.class, BusinessException.class}) |
| | | public void handleOrderException(HandleOrderExceptionDTO dto) { |
| | | // ========== A. 校验 ========== |
| | | Orders original = ordersMapper.selectById(dto.getOrderId()); |
| | | if (original == null || Constants.equalsInteger(original.getDeleted(), Constants.ONE)) { |
| | | throw new BusinessException(ResponseStatus.DATA_EMPTY.getCode(), "订单不存在"); |
| | | } |
| | | if (!Constants.equalsInteger(original.getType(), Constants.ONE)) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "仅支持异地订单异常处理"); |
| | | } |
| | | if (original.getTakeShopId() != null) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单已关联取件门店,不支持异常处理"); |
| | | } |
| | | if (!Constants.equalsInteger(original.getStatus(), Constants.FIVE)) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "仅支持已送达状态的订单"); |
| | | } |
| | | if (Constants.equalsInteger(original.getExceptionStatus(), Constants.ONE)) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "该订单已处理过异常,请勿重复操作"); |
| | | } |
| | | |
| | | ShopInfo newShop = shopInfoMapper.selectById(dto.getDepositShopId()); |
| | | if (newShop == null || Constants.equalsInteger(newShop.getStatus(), Constants.ONE)) { |
| | | throw new BusinessException(ResponseStatus.BAD_REQUEST.getCode(), "存放门店不存在或已禁用"); |
| | | } |
| | | |
| | | Date now = new Date(); |
| | | |
| | | // ========== B. 创建新订单(就地存取) ========== |
| | | String orderCode = "JC" + new java.text.SimpleDateFormat("yyyyMMddHHmmss").format(now) |
| | | + String.format("%04d", new java.util.Random().nextInt(10000)); |
| | | String orderTradeNo = generateOrderTradeNo(); |
| | | |
| | | // 当天23:30 |
| | | Calendar cal = Calendar.getInstance(); |
| | | cal.set(Calendar.HOUR_OF_DAY, 23); |
| | | cal.set(Calendar.MINUTE, 30); |
| | | cal.set(Calendar.SECOND, 0); |
| | | cal.set(Calendar.MILLISECOND, 0); |
| | | Date expectedTakeTime = cal.getTime(); |
| | | |
| | | // 计算基础单价 |
| | | Long price = 0L; |
| | | Long basicAmount = 0L; |
| | | // 查询原订单明细获取物品列表 |
| | | List<OrdersDetail> originalDetails = ordersDetailMapper.selectList( |
| | | new QueryWrapper<OrdersDetail>().lambda() |
| | | .eq(OrdersDetail::getOrderId, original.getId()) |
| | | .eq(OrdersDetail::getDeleted, Constants.ZERO)); |
| | | if (!CollectionUtils.isEmpty(originalDetails)) { |
| | | CalculateLocalPriceDTO priceDTO = new CalculateLocalPriceDTO(); |
| | | priceDTO.setCityId(Integer.valueOf(original.getCityId())); |
| | | priceDTO.setDepositStartTime(now); |
| | | priceDTO.setDepositEndTime(expectedTakeTime); |
| | | List<OrderItemDTO> items = new ArrayList<>(); |
| | | for (OrdersDetail d : originalDetails) { |
| | | OrderItemDTO item = new OrderItemDTO(); |
| | | item.setCategoryId(d.getLuggageId()); |
| | | item.setQuantity(d.getNum()); |
| | | items.add(item); |
| | | } |
| | | priceDTO.setItems(items); |
| | | PriceCalculateVO priceResult = calculateLocalPrice(priceDTO); |
| | | if (priceResult != null) { |
| | | price = priceResult.getItemPrice(); |
| | | basicAmount = priceResult.getItemPrice(); |
| | | } |
| | | } |
| | | |
| | | // 存件门店分成占比(就地存取) |
| | | Integer cityId = Integer.valueOf(original.getCityId()); |
| | | boolean isCompany = Constants.equalsInteger(newShop.getCompanyType(), Constants.ONE); |
| | | int fallbackFieldA = isCompany ? Constants.FIVE : Constants.SIX; |
| | | BigDecimal depositShopFeeRate = getShopRevenueShare(newShop, "localDeposit", cityId, fallbackFieldA); |
| | | |
| | | Orders newOrder = new Orders(); |
| | | newOrder.setCode(orderCode); |
| | | newOrder.setOutTradeNo(orderTradeNo); |
| | | newOrder.setMemberId(original.getMemberId()); |
| | | newOrder.setType(Constants.ZERO); // 就地存取 |
| | | newOrder.setCityId(original.getCityId()); |
| | | newOrder.setStatus(Constants.ONE); // 待寄存 |
| | | newOrder.setPayStatus(Constants.ONE); // 已支付 |
| | | newOrder.setPayTime(now); |
| | | newOrder.setCommentStatus(Constants.ZERO); |
| | | newOrder.setSettlementStatus(Constants.ZERO); |
| | | newOrder.setDeleted(Constants.ZERO); |
| | | newOrder.setCreateTime(now); |
| | | newOrder.setUpdateTime(now); |
| | | newOrder.setIsConverted(Constants.ZERO); |
| | | |
| | | // 存件信息(入参门店) |
| | | newOrder.setDepositShopId(dto.getDepositShopId()); |
| | | newOrder.setDepositLocation(newShop.getAddress()); |
| | | newOrder.setDepositLocationRemark(newShop.getAddress()); |
| | | newOrder.setDepositLat(BigDecimal.valueOf(newShop.getLatitude())); |
| | | newOrder.setDepositLgt(BigDecimal.valueOf(newShop.getLongitude())); |
| | | // 取件信息(就地=存件门店) |
| | | newOrder.setTakeShopId(dto.getDepositShopId()); |
| | | newOrder.setTakeLocation(newShop.getAddress()); |
| | | newOrder.setTakeLocationRemark(newShop.getAddress()); |
| | | newOrder.setTakeLat(BigDecimal.valueOf(newShop.getLatitude())); |
| | | newOrder.setTakeLgt(BigDecimal.valueOf(newShop.getLongitude())); |
| | | |
| | | // 时间 |
| | | newOrder.setExpectedDepositTime(now); |
| | | newOrder.setExpectedTakeTime(expectedTakeTime); |
| | | newOrder.setEstimatedDepositDays(1); |
| | | |
| | | // 物品信息 |
| | | newOrder.setGoodType(original.getGoodType()); |
| | | newOrder.setGoodLevel(original.getGoodLevel()); |
| | | newOrder.setGoodsInfo(original.getGoodsInfo()); |
| | | newOrder.setSupplement(original.getSupplement()); |
| | | newOrder.setSelfTake(original.getSelfTake()); |
| | | newOrder.setTakeUser(original.getTakeUser()); |
| | | newOrder.setTakePhone(original.getTakePhone()); |
| | | |
| | | // 异常标记 |
| | | newOrder.setExceptionStatus(Constants.ONE); |
| | | newOrder.setRelationOrderId(original.getId()); |
| | | newOrder.setExceptionInfo(dto.getRemark()); |
| | | |
| | | // 费用 |
| | | newOrder.setIsUrgent(Constants.ZERO); |
| | | newOrder.setUrgentRata(BigDecimal.ONE); |
| | | newOrder.setUrgentAmount(0L); |
| | | newOrder.setDeclaredAmount(0L); |
| | | newOrder.setDeclaredFee(0L); |
| | | newOrder.setPrice(price); |
| | | newOrder.setBasicAmount(basicAmount); |
| | | newOrder.setEstimatedAmount(basicAmount); |
| | | newOrder.setTotalAmount(0L); |
| | | newOrder.setPayAmount(0L); |
| | | newOrder.setManualRefund(Constants.ZERO); |
| | | |
| | | // 费用分配 |
| | | newOrder.setDriverFee(0L); |
| | | newOrder.setDriverFeeRata(BigDecimal.ZERO); |
| | | newOrder.setDepositShopFee(0L); |
| | | newOrder.setDepositShopFeeRata(depositShopFeeRate); |
| | | newOrder.setTakeShopFee(0L); |
| | | newOrder.setTakeShopFeeRata(BigDecimal.ZERO); |
| | | |
| | | // 门店补偿金额 |
| | | newOrder.setShopCompensationAmount(dto.getShopCompensation()); |
| | | |
| | | newOrder.setRemark(dto.getRemark()); |
| | | newOrder.setPlatformSmsNotified(Constants.ZERO); |
| | | newOrder.setMemberVerifyCode(generateVerifyCode()); |
| | | newOrder.setDriverVerifyCode(generateVerifyCode()); |
| | | // 计算店铺订单序号 |
| | | Date todayStart = DateUtil.getStartOfDay(now); |
| | | Long currentCount = ordersMapper.selectCount(new QueryWrapper<Orders>().lambda() |
| | | .eq(Orders::getDepositShopId, dto.getDepositShopId()) |
| | | .eq(Orders::getPayStatus, Constants.ONE) |
| | | .ge(Orders::getPayTime, todayStart) |
| | | .eq(Orders::getDeleted, Constants.ZERO)); |
| | | newOrder.setAutoNum(currentCount + 1); |
| | | ordersMapper.insert(newOrder); |
| | | |
| | | // ========== C. 创建新订单明细 ========== |
| | | if (!CollectionUtils.isEmpty(originalDetails)) { |
| | | for (OrdersDetail od : originalDetails) { |
| | | OrdersDetail detail = new OrdersDetail(); |
| | | detail.setOrderId(newOrder.getId()); |
| | | detail.setLuggageId(od.getLuggageId()); |
| | | detail.setLuggageName(od.getLuggageName()); |
| | | detail.setLuggageDetail(od.getLuggageDetail()); |
| | | detail.setNum(od.getNum()); |
| | | detail.setUnitPrice(od.getLocallyPrice()); |
| | | detail.setLocallyPrice(od.getLocallyPrice()); |
| | | detail.setDeleted(Constants.ZERO); |
| | | detail.setCreateTime(now); |
| | | ordersDetailMapper.insert(detail); |
| | | } |
| | | } |
| | | |
| | | // ========== D. 拷贝附件 ========== |
| | | List<Multifile> originalFiles = multifileMapper.selectList( |
| | | new QueryWrapper<Multifile>().lambda() |
| | | .eq(Multifile::getObjId, original.getId()) |
| | | .eq(Multifile::getObjType, Constants.FileType.ORDER_FILE.getKey()) |
| | | .eq(Multifile::getIsdeleted, Constants.ZERO)); |
| | | if (!CollectionUtils.isEmpty(originalFiles)) { |
| | | int sortNum = 1; |
| | | for (Multifile mf : originalFiles) { |
| | | Multifile newFile = new Multifile(); |
| | | newFile.setObjId(newOrder.getId()); |
| | | newFile.setObjType(Constants.FileType.ORDER_FILE.getKey()); |
| | | newFile.setType(mf.getType()); |
| | | newFile.setFileurl(mf.getFileurl()); |
| | | newFile.setIsdeleted(Constants.ZERO); |
| | | newFile.setCreateDate(now); |
| | | newFile.setSortnum(sortNum++); |
| | | multifileMapper.insert(newFile); |
| | | } |
| | | } |
| | | |
| | | // ========== E. 更新原订单 ========== |
| | | ordersMapper.update(new UpdateWrapper<Orders>().lambda() |
| | | .set(Orders::getExceptionStatus, Constants.ONE) |
| | | .set(Orders::getExceptionFee, dto.getDriverCompensation()) |
| | | .set(Orders::getUpdateTime, now) |
| | | .eq(Orders::getId, original.getId())); |
| | | |
| | | // ========== F. 发送短信通知 ========== |
| | | // 司机端-异常派单 |
| | | if (original.getAcceptDriver() != null) { |
| | | DriverInfo driver = driverInfoMapper.selectById(original.getAcceptDriver()); |
| | | if (driver != null && StringUtils.isNotBlank(driver.getTelephone())) { |
| | | sendSmsNotify(driver.getTelephone(), Constants.SmsNotify.EXCEPTION_DISPATCH, |
| | | "orderNo", original.getCode(), |
| | | "address", newShop.getAddress(), |
| | | "code", newOrder.getMemberVerifyCode()); |
| | | } |
| | | } |
| | | // 会员端-异常派单 |
| | | String memberPhone = StringUtils.isNotBlank(original.getTakePhone()) ? original.getTakePhone() : null; |
| | | if (memberPhone == null) { |
| | | Member member = memberMapper.selectById(original.getMemberId()); |
| | | if (member != null) { |
| | | memberPhone = member.getTelephone(); |
| | | } |
| | | } |
| | | if (StringUtils.isNotBlank(memberPhone)) { |
| | | sendSmsNotify(memberPhone, Constants.SmsNotify.MEMBER_EXCEPTION_DISPATCH, |
| | | "address", newShop.getAddress()); |
| | | } |
| | | } |
| | | |
| | | } |