| | |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.account.api.dto.SendMessageDTO; |
| | | import com.ruoyi.account.api.feignClient.*; |
| | | import com.ruoyi.account.api.model.*; |
| | | import com.ruoyi.account.api.vo.AppUserWhitelistMatchVO; |
| | |
| | | import com.ruoyi.order.util.mongodb.service.TransactionRecordService; |
| | | import com.ruoyi.order.util.mongodb.service.UploadRealTimeMonitoringDataService; |
| | | import com.ruoyi.order.vo.EndOfChargePageInfo; |
| | | import com.ruoyi.order.vo.ChargingSimulationVO; |
| | | import com.ruoyi.order.vo.IcbcAdminSubOrderVO; |
| | | import com.ruoyi.order.vo.OrderCountByDate; |
| | | import com.ruoyi.order.vo.StatisticsOfBatteryVO; |
| | | import com.ruoyi.other.api.domain.*; |
| | |
| | | import com.ruoyi.payment.api.vo.*; |
| | | import com.ruoyi.system.api.domain.SysUser; |
| | | import com.ruoyi.system.api.feignClient.SysUserClient; |
| | | import org.redisson.api.RBucket; |
| | | import org.redisson.api.RLock; |
| | | import org.redisson.api.RedissonClient; |
| | | import org.slf4j.Logger; |
| | |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.data.redis.core.RedisTemplate; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.PlatformTransactionManager; |
| | | import org.springframework.transaction.TransactionDefinition; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.transaction.support.TransactionSynchronization; |
| | | import org.springframework.transaction.support.TransactionSynchronizationManager; |
| | | import org.springframework.transaction.support.TransactionTemplate; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import javax.annotation.Resource; |
| | |
| | | private static final int WHITELIST_BENEFIT_ALL_FREE = 1; |
| | | private static final int WHITELIST_BENEFIT_ELECTRIC_FREE = 2; |
| | | private static final int WHITELIST_BENEFIT_SERVICE_FREE = 3; |
| | | private static final String SHARE_AFTER_COMMIT_RESOURCE_PREFIX = "chargingOrderShareAfterCommit:"; |
| | | private static final String SPECIAL_REFUND_TITLE = "特殊退款处理"; |
| | | |
| | | @Resource |
| | | private ChargingGunClient chargingGunClient; |
| | |
| | | private IcbcPaymentClient icbcPaymentClient; |
| | | @Resource |
| | | private TChargingOrderShareRecordMapper chargingOrderShareRecordMapper; |
| | | @Resource |
| | | private ChargingOrderSettlementExecutor chargingOrderSettlementExecutor; |
| | | @Resource |
| | | private PlatformTransactionManager transactionManager; |
| | | |
| | | @Resource |
| | | private AppCouponClient appCouponClient; |
| | |
| | | |
| | | @Resource |
| | | private SendMessageClient sendMessageClient; |
| | | @Resource |
| | | private WxSubscribeMessageService wxSubscribeMessageService; |
| | | |
| | | @Resource |
| | | private TOrderEvaluateService orderEvaluateService; |
| | |
| | | Map<String, Object> map = new HashMap<>(); |
| | | int size = this.baseMapper.getMyChargingOrderList(appUserId, 1, null, null).size(); |
| | | map.put("number", size); |
| | | query.setPageCurr((query.getPageCurr() - 1) * query.getPageSize()); |
| | | Integer pageCurr = query.getPageCurr() == null || query.getPageCurr() < 1 ? 1 : query.getPageCurr(); |
| | | Integer pageSize = query.getPageSize() == null || query.getPageSize() < 1 ? 10 : query.getPageSize(); |
| | | query.setPageCurr((pageCurr - 1) * pageSize); |
| | | query.setPageSize(pageSize); |
| | | List<TChargingOrder> list = this.baseMapper.getMyChargingOrderList(appUserId, query.getType(), query.getPageCurr(), query.getPageSize()); |
| | | List<MyChargingOrderList> orderLists = new ArrayList<>(); |
| | | for (TChargingOrder tChargingOrder : list) { |
| | | MyChargingOrderList myChargingOrderList = new MyChargingOrderList(); |
| | | myChargingOrderList.setId(tChargingOrder.getId().toString()); |
| | | myChargingOrderList.setSiteId(tChargingOrder.getSiteId()); |
| | | myChargingOrderList.setStatus(tChargingOrder.getStatus()); |
| | | Site site = siteClient.getSiteByIds(Arrays.asList(tChargingOrder.getSiteId())).getData().get(0); |
| | | myChargingOrderList.setTitle(site.getName()); |
| | | Site site = getOrderSite(tChargingOrder.getSiteId()); |
| | | myChargingOrderList.setTitle(site == null ? "未知电站" : site.getName()); |
| | | myChargingOrderList.setChargingDegree(tChargingOrder.getChargingCapacity()); |
| | | String name = chargingGunClient.getAllName(tChargingOrder.getChargingGunId()).getData(); |
| | | String name = getChargingGunName(tChargingOrder.getChargingGunId()); |
| | | myChargingOrderList.setChargingGunId(tChargingOrder.getChargingGunId()); |
| | | myChargingOrderList.setName(name); |
| | | myChargingOrderList.setEndMode(tChargingOrder.getEndMode()); |
| | | BigDecimal payMoney = tChargingOrder.getStatus() < 4 ? tChargingOrder.getRechargeAmount() : tChargingOrder.getPaymentAmount(); |
| | | Integer status = tChargingOrder.getStatus(); |
| | | BigDecimal payMoney = status != null && status < 4 ? tChargingOrder.getRechargeAmount() : tChargingOrder.getPaymentAmount(); |
| | | myChargingOrderList.setPayMoney(payMoney); |
| | | myChargingOrderList.setCreateTime(tChargingOrder.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); |
| | | if (tChargingOrder.getCreateTime() != null) { |
| | | myChargingOrderList.setCreateTime(tChargingOrder.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); |
| | | } |
| | | long count = orderEvaluateService.count(new LambdaQueryWrapper<TOrderEvaluate>().eq(TOrderEvaluate::getOrderType, 1) |
| | | .eq(TOrderEvaluate::getOrderId, tChargingOrder.getId()).eq(TOrderEvaluate::getDelFlag, 0)); |
| | | myChargingOrderList.setEvaluate(count + 1L); |
| | |
| | | map.put("list", orderLists); |
| | | map.put("totle", list.size()); |
| | | return map; |
| | | } |
| | | |
| | | private Site getOrderSite(Integer siteId) { |
| | | if (siteId == null) { |
| | | return null; |
| | | } |
| | | R<List<Site>> result = siteClient.getSiteByIds(Collections.singletonList(siteId)); |
| | | List<Site> sites = result == null ? Collections.emptyList() : result.getData(); |
| | | return sites == null || sites.isEmpty() ? null : sites.get(0); |
| | | } |
| | | |
| | | private String getChargingGunName(Integer chargingGunId) { |
| | | if (chargingGunId == null) { |
| | | return ""; |
| | | } |
| | | R<String> result = chargingGunClient.getAllName(chargingGunId); |
| | | return result == null || result.getData() == null ? "" : result.getData(); |
| | | } |
| | | |
| | | |
| | |
| | | for (TChargingOrder tChargingOrder : orderList) { |
| | | MyChargingOrderList myChargingOrderList = new MyChargingOrderList(); |
| | | myChargingOrderList.setId(tChargingOrder.getId().toString()); |
| | | myChargingOrderList.setSiteId(tChargingOrder.getSiteId()); |
| | | myChargingOrderList.setStatus(tChargingOrder.getStatus()); |
| | | Site site = siteClient.getSiteByIds(Arrays.asList(tChargingOrder.getSiteId())).getData().get(0); |
| | | myChargingOrderList.setTitle(site.getName()); |
| | |
| | | return amount == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : amount.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private void sendVipChargeFinishMessage(TAppUser appUser, TChargingOrder chargingOrder, Site site) { |
| | | try { |
| | | if (appUser == null || chargingOrder == null || appUser.getVipId() == null || !StringUtils.hasLength(appUser.getPhone())) { |
| | | return; |
| | | } |
| | | GetAppUserVipDetail query = new GetAppUserVipDetail(); |
| | | query.setAppUserId(appUser.getId()); |
| | | query.setVipId(appUser.getVipId()); |
| | | TAppUserVipDetail vipDetail = appUserVipDetailClient.getAppUserVipDetail(query).getData(); |
| | | if (vipDetail == null) { |
| | | return; |
| | | } |
| | | SendMessageDTO dto = new SendMessageDTO(); |
| | | dto.setType(4); |
| | | dto.setPhone(appUser.getPhone()); |
| | | dto.setSite(site == null ? "充电站" : site.getName()); |
| | | dto.setChargingCapacity(formatSmsNumber(chargingOrder.getChargingCapacity())); |
| | | BigDecimal fee = chargingOrder.getPaymentAmount() == null ? chargingOrder.getOrderAmount() : chargingOrder.getPaymentAmount(); |
| | | dto.setChargingFee(formatSmsNumber(fee)); |
| | | R<String> result = appUserClient.sendMessage(dto); |
| | | if (result == null || result.getCode() != 200) { |
| | | log.warn("VIP充电完成短信发送失败,orderCode={}, appUserId={}, result={}", chargingOrder.getCode(), appUser.getId(), result); |
| | | } |
| | | } catch (Exception e) { |
| | | log.warn("VIP充电完成短信发送异常,orderCode={}, appUserId={}", chargingOrder == null ? null : chargingOrder.getCode(), appUser == null ? null : appUser.getId(), e); |
| | | } |
| | | } |
| | | |
| | | private String formatSmsNumber(BigDecimal value) { |
| | | if (value == null) { |
| | | return "0"; |
| | | } |
| | | String text = value.setScale(0, RoundingMode.HALF_UP).toPlainString(); |
| | | return text.length() <= 5 ? text : text.substring(0, 5); |
| | | } |
| | | |
| | | /** |
| | | * 获取第三方实付金额,兼容早期未写入third_pay_amount的纯第三方支付订单。 |
| | | * |
| | |
| | | if (order == null || isWhitelistAllFreeOrder(order)) { |
| | | return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | BigDecimal thirdPayAmount = money(order.getThirdPayAmount()); |
| | | if (thirdPayAmount.compareTo(BigDecimal.ZERO) > 0) { |
| | | return thirdPayAmount; |
| | | } |
| | | if (Integer.valueOf(3).equals(order.getPayMethod())) { |
| | | return money(order.getRechargeAmount()); |
| | | } |
| | | return thirdPayAmount; |
| | | return ChargingSimulationSupport.thirdPayAmount(order); |
| | | } |
| | | |
| | | /** |
| | |
| | | return chargingOrder != null && "ICBC".equals(chargingOrder.getSourceName()); |
| | | } |
| | | |
| | | private BigDecimal resolveUpfrontCouponDiscount(Long appCouponId, Long userId, BigDecimal rechargeAmount) { |
| | | TAppCoupon appCoupon = appCouponClient.getAppCouponById(appCouponId).getData(); |
| | | if (appCoupon == null || !userId.equals(appCoupon.getAppUserId())) { |
| | | throw new ServiceException("优惠券不存在或不属于当前用户"); |
| | | } |
| | | if (!Integer.valueOf(1).equals(appCoupon.getStatus())) { |
| | | throw new ServiceException("优惠券已使用"); |
| | | } |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | if ((appCoupon.getStartTime() != null && appCoupon.getStartTime().isAfter(now)) |
| | | || (appCoupon.getEndTime() != null && appCoupon.getEndTime().isBefore(now))) { |
| | | throw new ServiceException("优惠券不在有效期内"); |
| | | } |
| | | try { |
| | | TCoupon coupon = JSON.parseObject(appCoupon.getCouponJson(), TCoupon.class); |
| | | return ChargingCouponSupport.calculateUpfrontDiscount(coupon, rechargeAmount); |
| | | } catch (IllegalArgumentException e) { |
| | | throw new ServiceException(e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 判断订单是否为白名单订单 |
| | | * |
| | |
| | | } |
| | | return new BigDecimal[]{electricDiscount, serviceDiscount}; |
| | | } |
| | | |
| | | private TVip getVipSnapshot(TAppUserVipDetail vipDetail, Integer vipId) { |
| | | if (vipDetail != null && StringUtils.hasLength(vipDetail.getVipJson())) { |
| | | return JSON.parseObject(vipDetail.getVipJson(), TVip.class); |
| | | } |
| | | return vipId == null ? null : vipClient.getInfo1(vipId).getData(); |
| | | } |
| | | @Override |
| | | public AjaxResult paymentChargingOrder(AddChargingOrder addChargingOrder) { |
| | | Long userId = tokenService.getLoginUserApplet().getUserId(); |
| | |
| | | BigDecimal rechargeAmount = money(addChargingOrder.getPaymentAmount()); |
| | | TChargingGun tChargingGun = chargingGunClient.getChargingGunById(addChargingOrder.getId()).getData(); |
| | | TChargingPile chargingPile = chargingPileClient.getChargingPileById(tChargingGun.getChargingPileId()).getData(); |
| | | TAppUserCar selectedCar = null; |
| | | if (addChargingOrder.getAppUserCarId() != null) { |
| | | selectedCar = appUserCarClient.getCarById(addChargingOrder.getAppUserCarId().toString()).getData(); |
| | | } |
| | | String selectedVin = selectedCar == null ? null : normalizeVin(selectedCar.getVin()); |
| | | if (Integer.valueOf(1).equals(chargingPile.getVinFlag()) |
| | | && (!StringUtils.hasLength(selectedVin) || !selectedVin.matches("[A-HJ-NPR-Z0-9]{17}"))) { |
| | | return AjaxResult.error("该充电桩需要17位有效车辆VIN,请先完善所选车辆VIN"); |
| | | } |
| | | AppUserWhitelistMatchVO whitelist = appUserWhitelistClient.matchWhitelist(userId, tChargingGun.getSiteId()).getData(); |
| | | boolean whitelistMatched = whitelist != null && Boolean.TRUE.equals(whitelist.getMatched()); |
| | | if (whitelistMatched) { |
| | | payMethod = 3; |
| | | addChargingOrder.setPayMethod(3); |
| | | addChargingOrder.setAppUserCouponId(null); |
| | | } |
| | | boolean whitelistAllFree = whitelistMatched |
| | | && Integer.valueOf(WHITELIST_BENEFIT_ALL_FREE).equals(whitelist.getBenefitType()); |
| | | BigDecimal userBalance = money(appUser.getBalance()); |
| | | BigDecimal balancePayAmount = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal thirdPayAmount = rechargeAmount; |
| | | BigDecimal upfrontCouponDiscount = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | if (payMethod == 2 && userBalance.compareTo(rechargeAmount) >= 0) { |
| | | payMethod = 1; |
| | | addChargingOrder.setPayMethod(1); |
| | | } |
| | | if (isBalancePayMode(payMethod)) { |
| | | addChargingOrder.setAppUserCouponId(null); |
| | | if (payMethod == 1 && userBalance.compareTo(rechargeAmount) < 0) { |
| | | } else if (!whitelistMatched && addChargingOrder.getAppUserCouponId() != null) { |
| | | upfrontCouponDiscount = resolveUpfrontCouponDiscount( |
| | | addChargingOrder.getAppUserCouponId(), userId, rechargeAmount); |
| | | } |
| | | BigDecimal payableAmount = whitelistAllFree |
| | | ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) |
| | | : rechargeAmount.subtract(upfrontCouponDiscount).max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal thirdPayAmount = payableAmount; |
| | | if (isBalancePayMode(payMethod)) { |
| | | if (payMethod == 1 && userBalance.compareTo(payableAmount) < 0) { |
| | | return AjaxResult.error("余额不足,请选择组合支付或第三方支付"); |
| | | } |
| | | balancePayAmount = payMethod == 1 ? rechargeAmount : userBalance.min(rechargeAmount).setScale(2, RoundingMode.HALF_UP); |
| | | thirdPayAmount = rechargeAmount.subtract(balancePayAmount).setScale(2, RoundingMode.HALF_UP); |
| | | balancePayAmount = payMethod == 1 ? payableAmount : userBalance.min(payableAmount).setScale(2, RoundingMode.HALF_UP); |
| | | thirdPayAmount = payableAmount.subtract(balancePayAmount).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | if (whitelistMatched) { |
| | | balancePayAmount = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | thirdPayAmount = Integer.valueOf(WHITELIST_BENEFIT_ALL_FREE).equals(whitelist.getBenefitType()) |
| | | ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) |
| | | : rechargeAmount; |
| | | } |
| | | //查询待支付状态的订单,没有支付则删除订单 |
| | | List<TChargingOrder> list = this.list(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getAppUserId, userId) |
| | | .eq(TChargingOrder::getRechargePaymentStatus, 1).eq(TChargingOrder::getDelFlag, 0)); |
| | | for (TChargingOrder tChargingOrder : list) { |
| | | Integer rechargePaymentType = tChargingOrder.getRechargePaymentType(); |
| | | if ("ICBC".equals(tChargingOrder.getSourceName())) { |
| | | IcbcPaymentResp data = icbcPaymentClient.query(tChargingOrder.getCode()).getData(); |
| | | if (null != data && isIcbcRemovableStatus(data.getStatus(), tChargingOrder)) { |
| | | this.removeById(tChargingOrder.getId()); |
| | | } |
| | | } |
| | | if (!"ICBC".equals(tChargingOrder.getSourceName()) && 1 == rechargePaymentType) { |
| | | NotifyV3PayDecodeRespBody data = wxPaymentClient.queryOrderInfo(tChargingOrder.getCode()).getData(); |
| | | if (null != data) { |
| | | String trade_state = data.getTrade_state(); |
| | | //支付失败,删除无效的订单 |
| | | if (trade_state.equals("REFUND") || trade_state.equals("NOTPAY") || trade_state.equals("REVOKED") || trade_state.equals("PAYERROR")) { |
| | | this.removeById(tChargingOrder.getId()); |
| | | } |
| | | } |
| | | } |
| | | if (!"ICBC".equals(tChargingOrder.getSourceName()) && 2 == rechargePaymentType) { |
| | | AliQueryOrder data = aliPaymentClient.query(tChargingOrder.getCode()).getData(); |
| | | if (null != data) { |
| | | //支付失败,删除无效的订单 |
| | | String tradeStatus = data.getTradeStatus(); |
| | | if (null != tradeStatus && tradeStatus.equals("TRADE_CLOSED")) { |
| | | this.removeById(tChargingOrder.getId()); |
| | | } |
| | | } |
| | | } |
| | | if (!"ICBC".equals(tChargingOrder.getSourceName()) && 3 == rechargePaymentType) { |
| | | AliQueryOrder data = h5AliPaymentClient.query(tChargingOrder.getCode()).getData(); |
| | | if (null != data) { |
| | | //支付失败,删除无效的订单 |
| | | String tradeStatus = data.getTradeStatus(); |
| | | if (null != tradeStatus && tradeStatus.equals("TRADE_CLOSED")) { |
| | | this.removeById(tChargingOrder.getId()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | payMethod = ChargingPaymentSupport.resolvePayMethod(balancePayAmount, thirdPayAmount, payMethod); |
| | | addChargingOrder.setPayMethod(payMethod); |
| | | //检查当前枪是否是正在使用中 |
| | | TChargingOrder one = this.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getChargingGunId, addChargingOrder.getId()) |
| | | .in(TChargingOrder::getStatus, Arrays.asList(3)).eq(TChargingOrder::getDelFlag, 0)); |
| | |
| | | } |
| | | //查询当前充电枪是否有正在支付的订单 |
| | | one = this.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getChargingGunId, addChargingOrder.getId()) |
| | | .eq(TChargingOrder::getAppUserCarId, userId).eq(TChargingOrder::getRechargePaymentStatus, 1).eq(TChargingOrder::getDelFlag, 0)); |
| | | .eq(TChargingOrder::getAppUserId, userId).eq(TChargingOrder::getRechargePaymentStatus, 1) |
| | | .eq(TChargingOrder::getDelFlag, 0).last("limit 1")); |
| | | if (null != one) { |
| | | //查询三方支付数据,支付中直接结束三方订单 |
| | | if ("ICBC".equals(one.getSourceName())) { |
| | |
| | | chargingOrder.setOrderClassification(1); |
| | | chargingOrder.setAppUserId(userId); |
| | | chargingOrder.setAppUserCarId(addChargingOrder.getAppUserCarId()); |
| | | if (null != addChargingOrder.getAppUserCarId()) { |
| | | TAppUserCar userCar = appUserCarClient.getCarById(addChargingOrder.getAppUserCarId().toString()).getData(); |
| | | if (null != userCar) { |
| | | chargingOrder.setPlateNum(userCar.getLicensePlate()); |
| | | } |
| | | if (selectedCar != null) { |
| | | chargingOrder.setPlateNum(selectedCar.getLicensePlate()); |
| | | chargingOrder.setExpectedVin(selectedVin); |
| | | } |
| | | chargingOrder.setReservationTime(addChargingOrder.getReservationTime()); |
| | | chargingOrder.setSiteId(tChargingGun.getSiteId()); |
| | | chargingOrder.setChargingPileId(tChargingGun.getChargingPileId()); |
| | | chargingOrder.setChargingGunId(addChargingOrder.getId()); |
| | |
| | | chargingOrder.setThirdRefundAmount(BigDecimal.ZERO); |
| | | chargingOrder.setRechargeAmount(rechargeAmount); |
| | | chargingOrder.setAppCouponId(addChargingOrder.getAppUserCouponId()); |
| | | chargingOrder.setCouponDiscountAmount(upfrontCouponDiscount); |
| | | chargingOrder.setIsWhitelist(whitelistMatched ? 1 : 0); |
| | | chargingOrder.setWhitelistId(whitelistMatched ? whitelist.getWhitelistId() : null); |
| | | chargingOrder.setWhitelistBenefitType(whitelistMatched ? whitelist.getBenefitType() : null); |
| | |
| | | chargingOrder.setWhitelistServiceRefundAmount(BigDecimal.ZERO); |
| | | chargingOrder.setVipDiscountAmount(BigDecimal.ZERO); |
| | | chargingOrder.setOrderSource(1); |
| | | if (thirdPayAmount.compareTo(BigDecimal.ZERO) > 0) { |
| | | if (thirdPayAmount.compareTo(BigDecimal.ZERO) > 0 && Integer.valueOf(1).equals(addChargingOrder.getPaymentType())) { |
| | | chargingOrder.setSourceName("ICBC"); |
| | | } |
| | | chargingOrder.setTitle("【充电桩充电】" + chargingPile.getNumber() + "号桩/" + tChargingGun.getCode() + "号枪"); |
| | | |
| | | Site site = siteClient.getSiteByIds(Arrays.asList(tChargingGun.getSiteId())).getData().get(0); |
| | | AjaxResult reservationValidation = validateReservation(site, addChargingOrder.getReservationTime()); |
| | | if (reservationValidation != null) { |
| | | return reservationValidation; |
| | | } |
| | | Integer accountingStrategyId = tChargingGun.getAccountingStrategyId(); |
| | | if (null == accountingStrategyId) { |
| | | //查询站点上面的计费策略 |
| | |
| | | } |
| | | TAccountingStrategy accountingStrategy = accountingStrategyClient.getAccountingStrategyById(accountingStrategyId).getData(); |
| | | |
| | | //直营站点才可以享受会员折扣 |
| | | if (!whitelistMatched && !isBalancePayMode(payMethod) && null != appUser.getVipId() && 1 == site.getBusinessCategory()) { |
| | | TVip vip = vipClient.getInfo1(appUser.getVipId()).getData(); |
| | | BigDecimal discount = null; |
| | | if (1 == vip.getType()) { |
| | | //普通会员折扣使用积分策略上的折扣,且有最高优惠金额 |
| | | discount = accountingStrategy.getDiscount(); |
| | | } else { |
| | | //内部会员使用会员信息设置的折扣,没有最高优惠金额 |
| | | discount = vip.getDiscount(); |
| | | } |
| | | //会员折扣支持直营网点和加盟商站点 |
| | | if (!whitelistMatched && Integer.valueOf(1).equals(site.getMemberDiscountFlag()) |
| | | && !isBalancePayMode(payMethod) && null != appUser.getVipId()) { |
| | | GetAppUserVipDetail getAppUserVipDetail = new GetAppUserVipDetail(); |
| | | getAppUserVipDetail.setAppUserId(appUser.getId()); |
| | | getAppUserVipDetail.setVipId(appUser.getVipId()); |
| | | TAppUserVipDetail vipDetail = appUserVipDetailClient.getAppUserVipDetail(getAppUserVipDetail).getData(); |
| | | if (null != vipDetail) { |
| | | TVip vip = getVipSnapshot(vipDetail, appUser.getVipId()); |
| | | if (null != vip && null != vipDetail) { |
| | | BigDecimal discount = null; |
| | | if (Integer.valueOf(1).equals(vip.getType())) { |
| | | //普通会员折扣使用积分策略上的折扣,且有最高优惠金额 |
| | | discount = accountingStrategy.getDiscount(); |
| | | } else { |
| | | //内部会员使用会员信息设置的折扣,没有最高优惠金额 |
| | | discount = vip.getDiscount(); |
| | | } |
| | | Integer chargeNum = vipDetail.getChargeNum(); |
| | | if (0 < chargeNum) { |
| | | if (null != discount && null != chargeNum && 0 < chargeNum) { |
| | | chargingOrder.setVipDiscount(discount); |
| | | } |
| | | } |
| | |
| | | } |
| | | //会员优惠折扣将其计入增加充电时长(增加总充电金额) |
| | | //如果充电总金额未使用完,则需要退回费用=(原金额/总金额)*(总金额-实际充电金额) |
| | | IcbcPaymentReq req = new IcbcPaymentReq(); |
| | | req.setOutTradeNo(chargingOrder.getCode()); |
| | | req.setAmount(thirdPayAmount); |
| | | req.setSubject("充电充值"); |
| | | req.setBody("充电充值"); |
| | | req.setPaymentType(addChargingOrder.getPaymentType()); |
| | | req.setNotifyUrl("/payment/icbc/callBack"); |
| | | req.setReturnUrl(addChargingOrder.getReturnUrl()); |
| | | req.setOpenId(Integer.valueOf(1).equals(addChargingOrder.getPaymentType()) ? appUser.getWxOpenid() : appUser.getAliOpenid()); |
| | | req.setProfitSharing(buildIcbcProfitSharing(site)); |
| | | IcbcPaymentResp data = icbcPaymentClient.payment(req).getData(); |
| | | if (isIcbcPaymentCreated(data)) { |
| | | return AjaxResult.success(data); |
| | | if (Integer.valueOf(1).equals(addChargingOrder.getPaymentType())) { |
| | | IcbcPaymentReq req = new IcbcPaymentReq(); |
| | | req.setOutTradeNo(chargingOrder.getCode()); |
| | | req.setAmount(thirdPayAmount); |
| | | req.setSubject("充电充值"); |
| | | req.setBody("充电充值"); |
| | | req.setPaymentType(1); |
| | | req.setNotifyUrl("/payment/icbc/callBack"); |
| | | req.setReturnUrl(addChargingOrder.getReturnUrl()); |
| | | req.setOpenId(appUser.getWxOpenid()); |
| | | req.setProfitSharing(buildIcbcProfitSharing(site)); |
| | | R<IcbcPaymentResp> result = icbcPaymentClient.payment(req); |
| | | if (result == null || R.isError(result)) { |
| | | String reason = result == null ? "工行支付服务无响应" : firstNotBlank(result.getMsg(), "工行支付服务调用失败"); |
| | | log.error("调起工行支付失败,订单号:{},原因:{}", chargingOrder.getCode(), reason); |
| | | throw new RuntimeException("调起工行支付失败:" + reason); |
| | | } |
| | | IcbcPaymentResp data = result.getData(); |
| | | if (isIcbcPaymentCreated(data)) { |
| | | if (StringUtils.hasLength(data.getTradeNo())) { |
| | | chargingOrder.setRechargeSerialNumber(data.getTradeNo()); |
| | | this.updateById(chargingOrder); |
| | | } |
| | | return AjaxResult.success(data); |
| | | } |
| | | String reason = buildIcbcPaymentFailReason(data); |
| | | log.error("调起工行支付失败,订单号:{},工行返回:{}", chargingOrder.getCode(), reason); |
| | | throw new RuntimeException("调起工行支付失败:" + reason); |
| | | } |
| | | throw new RuntimeException("调起工行支付失败"); |
| | | if (Integer.valueOf(2).equals(addChargingOrder.getPaymentType())) { |
| | | AliPaymentReq req = new AliPaymentReq(); |
| | | req.setOutTradeNo(chargingOrder.getCode()); |
| | | req.setTotalAmount(thirdPayAmount.toString()); |
| | | req.setSubject("充电充值"); |
| | | req.setBuyerOpenId(appUser.getAliOpenid()); |
| | | req.setBody("充电充值"); |
| | | req.setNotifyUrl("/payment/ali/callBack"); |
| | | AliPaymentResp data = aliPaymentClient.payment(req).getData(); |
| | | if (data != null) { |
| | | return AjaxResult.success(data); |
| | | } |
| | | throw new RuntimeException("调起支付宝支付失败"); |
| | | } |
| | | if (Integer.valueOf(3).equals(addChargingOrder.getPaymentType())) { |
| | | AliPaymentReq req = new AliPaymentReq(); |
| | | req.setOutTradeNo(chargingOrder.getCode()); |
| | | req.setTotalAmount(thirdPayAmount.toString()); |
| | | req.setSubject("充电充值"); |
| | | req.setBuyerOpenId(appUser.getAliOpenid()); |
| | | req.setBody("充电充值"); |
| | | req.setNotifyUrl("/payment/aliH5/callBack"); |
| | | req.setReturnUrl(addChargingOrder.getReturnUrl()); |
| | | String data = h5AliPaymentClient.payment(req).getData(); |
| | | if (StringUtils.hasLength(data)) { |
| | | return AjaxResult.success("操作成功", data); |
| | | } |
| | | } |
| | | throw new RuntimeException("无效的支付方式"); |
| | | } |
| | | |
| | | @Override |
| | | public ChargingSimulationVO getCurrentSimulationOrder(Integer gunId) { |
| | | if (gunId == null) { |
| | | throw new ServiceException("充电枪不能为空"); |
| | | } |
| | | Long userId = tokenService.getLoginUserApplet().getUserId(); |
| | | return buildSimulationVO(findSimulationOrder(userId, gunId)); |
| | | } |
| | | |
| | | @Override |
| | | public ChargingSimulationVO completeSimulation(ChargingSimulationReq request) { |
| | | if (request == null || request.getGunId() == null) { |
| | | throw new ServiceException("充电枪不能为空"); |
| | | } |
| | | Long userId = tokenService.getLoginUserApplet().getUserId(); |
| | | TChargingOrder order = findSimulationOrder(userId, request.getGunId()); |
| | | List<UploadRealTimeMonitoringData> monitoringData = uploadRealTimeMonitoringDataService.getDataByOrderCode(order.getCode()); |
| | | if (monitoringData != null && !monitoringData.isEmpty()) { |
| | | throw new ServiceException("订单已存在真实充电监控数据,禁止模拟结算"); |
| | | } |
| | | BigDecimal electricityFee = money(request.getElectricityFee()); |
| | | BigDecimal serviceFee = money(request.getServiceFee()); |
| | | BigDecimal simulationTotal = electricityFee.add(serviceFee).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal availableAmount = ChargingSimulationSupport.availableAmount(order); |
| | | if (simulationTotal.compareTo(availableAmount) > 0) { |
| | | throw new ServiceException("模拟消费金额不能超过订单可消费金额" + availableAmount.toPlainString() + "元"); |
| | | } |
| | | |
| | | List<AccountingStrategyDetailOrder> tariffs = accountingStrategyDetailOrderService.list( |
| | | new LambdaQueryWrapper<AccountingStrategyDetailOrder>() |
| | | .eq(AccountingStrategyDetailOrder::getChargingOrderId, order.getId()) |
| | | .orderByAsc(AccountingStrategyDetailOrder::getStartTime)); |
| | | ChargingSimulationSupport.PreparedSimulation prepared; |
| | | try { |
| | | prepared = ChargingSimulationSupport.prepare(order.getCode(), tariffs, electricityFee, serviceFee, LocalDateTime.now()); |
| | | } catch (IllegalArgumentException e) { |
| | | throw new ServiceException(e.getMessage()); |
| | | } |
| | | accountingStrategyDetailOrderService.updateById(prepared.getTariff()); |
| | | |
| | | TChargingGun gun = chargingGunClient.getChargingGunById(order.getChargingGunId()).getData(); |
| | | TChargingPile pile = chargingPileClient.getChargingPileById(order.getChargingPileId()).getData(); |
| | | TransactionRecordMessageVO record = prepared.getRecord(); |
| | | record.setCharging_gun_code(gun == null ? null : gun.getCode()); |
| | | record.setCharging_pile_code(pile == null ? null : pile.getCode()); |
| | | if (Integer.valueOf(2).equals(order.getStatus())) { |
| | | TChargingOrder charging = new TChargingOrder(); |
| | | charging.setId(order.getId()); |
| | | charging.setStatus(3); |
| | | charging.setStartTime(LocalDateTime.now().minusSeconds(1)); |
| | | this.updateById(charging); |
| | | } |
| | | R settlement = chargingOrderSettlementExecutor.settle(record); |
| | | if (R.isError(settlement)) { |
| | | throw new ServiceException(StringUtils.hasLength(settlement.getMsg()) ? settlement.getMsg() : "模拟结算失败"); |
| | | } |
| | | return buildSimulationVO(this.getById(order.getId())); |
| | | } |
| | | |
| | | private TChargingOrder findSimulationOrder(Long userId, Integer gunId) { |
| | | TChargingOrder order = this.getOne(new LambdaQueryWrapper<TChargingOrder>() |
| | | .eq(TChargingOrder::getAppUserId, userId) |
| | | .eq(TChargingOrder::getChargingGunId, gunId) |
| | | .eq(TChargingOrder::getRechargePaymentStatus, 2) |
| | | .in(TChargingOrder::getStatus, Arrays.asList(2, 3, 4)) |
| | | .eq(TChargingOrder::getDelFlag, 0) |
| | | .isNull(TChargingOrder::getRefundStatus) |
| | | .orderByDesc(TChargingOrder::getId) |
| | | .last("limit 1")); |
| | | if (order == null) { |
| | | throw new ServiceException("当前枪没有属于你的可模拟已支付订单"); |
| | | } |
| | | return order; |
| | | } |
| | | |
| | | private ChargingSimulationVO buildSimulationVO(TChargingOrder order) { |
| | | ChargingSimulationVO result = new ChargingSimulationVO(); |
| | | result.setOrderId(order.getId()); |
| | | result.setOrderCode(order.getCode()); |
| | | result.setOrderStatus(order.getStatus()); |
| | | result.setPaymentMode(ChargingSimulationSupport.paymentMode(order)); |
| | | result.setPayMethod(order.getPayMethod()); |
| | | result.setRechargePaymentType(order.getRechargePaymentType()); |
| | | result.setSourceName(order.getSourceName()); |
| | | result.setRechargeAmount(order.getRechargeAmount()); |
| | | result.setChargeAmount(order.getChargeAmount()); |
| | | result.setAvailableAmount(ChargingSimulationSupport.availableAmount(order)); |
| | | result.setBalancePayAmount(order.getBalancePayAmount()); |
| | | result.setThirdPayAmount(order.getThirdPayAmount()); |
| | | result.setElectricityFee(order.getElectrovalence()); |
| | | result.setServiceFee(order.getServiceCharge()); |
| | | result.setPaymentAmount(order.getPaymentAmount()); |
| | | result.setRefundAmount(order.getRefundAmount()); |
| | | result.setBalanceRefundAmount(order.getBalanceRefundAmount()); |
| | | result.setThirdRefundAmount(order.getThirdRefundAmount()); |
| | | result.setRefundStatus(order.getRefundStatus()); |
| | | result.setIcbcOrderId(order.getRechargeSerialNumber()); |
| | | TChargingOrderShareRecord shareRecord = chargingOrderShareRecordMapper.selectOne( |
| | | new LambdaQueryWrapper<TChargingOrderShareRecord>() |
| | | .eq(TChargingOrderShareRecord::getChargingOrderId, order.getId()) |
| | | .eq(TChargingOrderShareRecord::getPayChannel, "ICBC") |
| | | .eq(TChargingOrderShareRecord::getDelFlag, false) |
| | | .orderByDesc(TChargingOrderShareRecord::getId) |
| | | .last("limit 1")); |
| | | if (shareRecord == null) { |
| | | shareRecord = chargingOrderShareRecordMapper.selectOne( |
| | | new LambdaQueryWrapper<TChargingOrderShareRecord>() |
| | | .eq(TChargingOrderShareRecord::getChargingOrderId, order.getId()) |
| | | .eq(TChargingOrderShareRecord::getDelFlag, false) |
| | | .orderByDesc(TChargingOrderShareRecord::getId) |
| | | .last("limit 1")); |
| | | } |
| | | if (shareRecord != null) { |
| | | result.setShareAmount(shareRecord.getShareAmount()); |
| | | result.setShareStatus(shareRecord.getOnlineShareStatus()); |
| | | result.setSubOrderId(shareRecord.getSubOrderId()); |
| | | result.setSubOrderSeq(shareRecord.getSubOrderSeq()); |
| | | result.setSubOrderTrxid(shareRecord.getSubOrderTrxid()); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | public IcbcPaymentResp queryIcbcAdminPayment(String orderCode) { |
| | | TChargingOrder order = loadIcbcAdminOrder(orderCode, false); |
| | | return requireFeignData(icbcPaymentClient.query(order.getCode()), "工行支付查询失败"); |
| | | } |
| | | |
| | | @Transactional |
| | | @Override |
| | | public AjaxResult confirmIcbcPayment(String orderCode) { |
| | | if (!StringUtils.hasLength(orderCode)) { |
| | | return AjaxResult.error("订单号不能为空"); |
| | | } |
| | | Long userId = tokenService.getLoginUserApplet().getUserId(); |
| | | TChargingOrder order = this.getOne(new LambdaQueryWrapper<TChargingOrder>() |
| | | .eq(TChargingOrder::getCode, orderCode) |
| | | .eq(TChargingOrder::getAppUserId, userId) |
| | | .eq(TChargingOrder::getDelFlag, 0) |
| | | .last("limit 1")); |
| | | if (order == null) { |
| | | return AjaxResult.error("支付订单不存在"); |
| | | } |
| | | if (Integer.valueOf(2).equals(order.getRechargePaymentStatus())) { |
| | | return AjaxResult.success(Collections.singletonMap("confirmed", true)); |
| | | } |
| | | if (!isIcbcOrder(order)) { |
| | | return AjaxResult.error("当前订单不是工行支付订单"); |
| | | } |
| | | |
| | | R<IcbcPaymentResp> queryResult = icbcPaymentClient.query(orderCode); |
| | | IcbcPaymentResp payment = queryResult == null ? null : queryResult.getData(); |
| | | if (!IcbcAdminFundOperationSupport.isBankPaid(payment) |
| | | || payment == null |
| | | || !orderCode.equals(payment.getOrderId()) |
| | | || !StringUtils.hasLength(payment.getTradeNo())) { |
| | | return AjaxResult.success(Collections.singletonMap("confirmed", false)); |
| | | } |
| | | AjaxResult callbackResult = chargingOrderCallback(1, orderCode, payment.getTradeNo(), "1"); |
| | | if (!Integer.valueOf(200).equals(callbackResult.get("code"))) { |
| | | return callbackResult; |
| | | } |
| | | return AjaxResult.success(Collections.singletonMap("confirmed", true)); |
| | | } |
| | | |
| | | @Override |
| | | public IcbcRefundResp refundIcbcAdminPayment(IcbcAdminFundOperationReq request) { |
| | | if (request == null) { |
| | | throw new ServiceException("请求参数不能为空"); |
| | | } |
| | | TChargingOrder order = loadIcbcAdminOrder(request.getOrderCode(), false); |
| | | BigDecimal amount; |
| | | try { |
| | | amount = IcbcAdminFundOperationSupport.requireAmount(request.getAmount()); |
| | | } catch (IllegalArgumentException e) { |
| | | throw new ServiceException(e.getMessage()); |
| | | } |
| | | BigDecimal paidAmount = money(order.getThirdPayAmount()).compareTo(BigDecimal.ZERO) > 0 |
| | | ? money(order.getThirdPayAmount()) : money(order.getRechargeAmount()); |
| | | if (amount.compareTo(paidAmount) > 0) { |
| | | throw new ServiceException("退款金额不能超过订单第三方支付金额" + paidAmount.toPlainString() + "元"); |
| | | } |
| | | RLock lock = redissonClient.getLock("icbcAdminFund:order:" + order.getCode()); |
| | | if (!lock.tryLock()) { |
| | | throw new ServiceException("该订单正在执行工行资金操作,请勿重复提交"); |
| | | } |
| | | try { |
| | | IcbcPaymentResp payment = requireFeignData(icbcPaymentClient.query(order.getCode()), "工行支付查询失败"); |
| | | if (!IcbcAdminFundOperationSupport.isBankPaid(payment)) { |
| | | throw new ServiceException("工行查询未确认该订单支付成功,禁止退款"); |
| | | } |
| | | String orderId = extractIcbcOrderId(payment); |
| | | order.setRechargeSerialNumber(orderId); |
| | | // 仅用于本次请求通过退款参数校验,不更新本地订单状态。 |
| | | order.setRechargePaymentStatus(2); |
| | | RBucket<String> pendingRefund = redissonClient.getBucket("icbcAdminFund:pending:main:" + order.getCode()); |
| | | String generatedRefundNo = "ICBCADMINREFUND" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")); |
| | | String pendingValue = pendingRefund.get(); |
| | | boolean reusingPending = StringUtils.hasLength(pendingValue); |
| | | String refundNo; |
| | | try { |
| | | refundNo = IcbcAdminFundOperationSupport.resolveRefundNo(pendingValue, amount, generatedRefundNo); |
| | | } catch (IllegalArgumentException e) { |
| | | throw new ServiceException(e.getMessage()); |
| | | } |
| | | IcbcRefundReq refundRequest = IcbcAdminFundOperationSupport.buildRefundRequest(order, amount, refundNo); |
| | | if (!reusingPending) { |
| | | pendingRefund.set(IcbcAdminFundOperationSupport.buildPendingRefundValue(refundNo, amount)); |
| | | } |
| | | IcbcRefundResp response = requireFeignData(icbcPaymentClient.refund(refundRequest), "工行退款调用失败"); |
| | | if (IcbcAdminFundOperationSupport.shouldClearPendingRefund( |
| | | reusingPending, response.getStatus(), isDefinitiveIcbcResponse(response.getRawResponse()))) { |
| | | pendingRefund.delete(); |
| | | } |
| | | return response; |
| | | } finally { |
| | | if (lock.isHeldByCurrentThread()) { |
| | | lock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public List<IcbcAdminSubOrderVO> queryIcbcAdminSubOrders(String orderCode) { |
| | | TChargingOrder order = loadIcbcAdminOrder(orderCode, true); |
| | | List<TChargingOrderShareRecord> records = chargingOrderShareRecordMapper.selectList( |
| | | new LambdaQueryWrapper<TChargingOrderShareRecord>() |
| | | .eq(TChargingOrderShareRecord::getChargingOrderId, order.getId()) |
| | | .eq(TChargingOrderShareRecord::getPayChannel, "ICBC") |
| | | .eq(TChargingOrderShareRecord::getDelFlag, false) |
| | | .orderByAsc(TChargingOrderShareRecord::getId)); |
| | | List<IcbcAdminSubOrderVO> result = new ArrayList<>(); |
| | | for (TChargingOrderShareRecord record : records) { |
| | | IcbcAdminSubOrderVO item = buildIcbcAdminSubOrderVO(record); |
| | | if (!Boolean.TRUE.equals(record.getNeedOnlineShare())) { |
| | | item.setQueryStatus("SKIP"); |
| | | item.setErrorMessage(record.getFailReason()); |
| | | result.add(item); |
| | | continue; |
| | | } |
| | | try { |
| | | IcbcSubOrderQueryReq query = IcbcAdminFundOperationSupport.buildSubOrderQueryRequest(order, record); |
| | | item.applyQuery(requireFeignData(icbcPaymentClient.querySubOrder(query), "工行子订单查询失败")); |
| | | } catch (Exception e) { |
| | | item.setQueryStatus("FAIL"); |
| | | item.setErrorMessage(e.getMessage()); |
| | | } |
| | | result.add(item); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | public IcbcSubOrderRefundResp refundIcbcAdminSubOrder(IcbcAdminFundOperationReq request) { |
| | | if (request == null || request.getShareRecordId() == null) { |
| | | throw new ServiceException("请选择需要退款的分账子订单"); |
| | | } |
| | | TChargingOrder order = loadIcbcAdminOrder(request.getOrderCode(), true); |
| | | TChargingOrderShareRecord shareRecord = chargingOrderShareRecordMapper.selectById(request.getShareRecordId()); |
| | | if (shareRecord == null || !order.getId().equals(shareRecord.getChargingOrderId())) { |
| | | throw new ServiceException("分账记录不属于当前订单"); |
| | | } |
| | | BigDecimal amount; |
| | | try { |
| | | amount = IcbcAdminFundOperationSupport.requireAmount(request.getAmount()); |
| | | } catch (IllegalArgumentException e) { |
| | | throw new ServiceException(e.getMessage()); |
| | | } |
| | | if (shareRecord.getShareAmount() != null && amount.compareTo(shareRecord.getShareAmount()) > 0) { |
| | | throw new ServiceException("分账退款金额不能超过分账金额" + shareRecord.getShareAmount().toPlainString() + "元"); |
| | | } |
| | | RLock lock = redissonClient.getLock("icbcAdminFund:order:" + order.getCode()); |
| | | if (!lock.tryLock()) { |
| | | throw new ServiceException("该分账子订单正在退款,请勿重复提交"); |
| | | } |
| | | try { |
| | | String suffix = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")); |
| | | RBucket<String> pendingRefund = redissonClient.getBucket("icbcAdminFund:pending:sub:" + shareRecord.getId()); |
| | | String generatedToken = "ICBCSUBREFUND" + suffix + ":ICBCSUBREFUNDNO" + suffix; |
| | | String pendingValue = pendingRefund.get(); |
| | | boolean reusingPending = StringUtils.hasLength(pendingValue); |
| | | String operationToken; |
| | | try { |
| | | operationToken = IcbcAdminFundOperationSupport.resolveRefundNo(pendingValue, amount, generatedToken); |
| | | } catch (IllegalArgumentException e) { |
| | | throw new ServiceException(e.getMessage()); |
| | | } |
| | | String[] refundNumbers = operationToken.split(":", 2); |
| | | if (refundNumbers.length != 2) { |
| | | throw new ServiceException("工行分账退款幂等记录格式错误"); |
| | | } |
| | | IcbcSubOrderRefundReq refundRequest; |
| | | try { |
| | | refundRequest = IcbcAdminFundOperationSupport.buildSubOrderRefundRequest( |
| | | order, shareRecord, amount, refundNumbers[0], refundNumbers[1]); |
| | | } catch (IllegalArgumentException e) { |
| | | throw new ServiceException(e.getMessage()); |
| | | } |
| | | if (!reusingPending) { |
| | | pendingRefund.set(IcbcAdminFundOperationSupport.buildPendingRefundValue(operationToken, amount)); |
| | | } |
| | | IcbcSubOrderRefundResp response = requireFeignData( |
| | | icbcPaymentClient.refundSubOrder(refundRequest), "工行分账退款调用失败"); |
| | | if (IcbcAdminFundOperationSupport.shouldClearPendingRefund( |
| | | reusingPending, response.getStatus(), isDefinitiveIcbcResponse(response.getRawResponse()))) { |
| | | pendingRefund.delete(); |
| | | } |
| | | return response; |
| | | } finally { |
| | | if (lock.isHeldByCurrentThread()) { |
| | | lock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private TChargingOrder loadIcbcAdminOrder(String orderCode, boolean requireLocalPaid) { |
| | | if (!StringUtils.hasLength(orderCode)) { |
| | | throw new ServiceException("订单号不能为空"); |
| | | } |
| | | TChargingOrder order = this.getOne(new LambdaQueryWrapper<TChargingOrder>() |
| | | .eq(TChargingOrder::getCode, orderCode) |
| | | .eq(TChargingOrder::getDelFlag, 0) |
| | | .last("limit 1")); |
| | | try { |
| | | if (requireLocalPaid) { |
| | | IcbcAdminFundOperationSupport.validateOrder(order); |
| | | } else { |
| | | IcbcAdminFundOperationSupport.validateIcbcOrder(order); |
| | | } |
| | | } catch (IllegalArgumentException e) { |
| | | throw new ServiceException(e.getMessage()); |
| | | } |
| | | return order; |
| | | } |
| | | |
| | | private String extractIcbcOrderId(IcbcPaymentResp payment) { |
| | | try { |
| | | JSONObject response = JSON.parseObject(payment.getRawResponse()); |
| | | String orderId = response == null ? null : response.getString("order_id"); |
| | | if (StringUtils.hasLength(orderId)) { |
| | | return orderId; |
| | | } |
| | | } catch (Exception ignored) { |
| | | } |
| | | throw new ServiceException("工行支付查询未返回真实order_id,禁止退款"); |
| | | } |
| | | |
| | | private boolean isDefinitiveIcbcResponse(String rawResponse) { |
| | | if (!StringUtils.hasLength(rawResponse)) { |
| | | return false; |
| | | } |
| | | try { |
| | | JSONObject response = JSON.parseObject(rawResponse); |
| | | return response != null && (response.containsKey("returnCode") || response.containsKey("return_code")); |
| | | } catch (Exception ignored) { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | private IcbcAdminSubOrderVO buildIcbcAdminSubOrderVO(TChargingOrderShareRecord record) { |
| | | IcbcAdminSubOrderVO item = new IcbcAdminSubOrderVO(); |
| | | item.setShareRecordId(record.getId()); |
| | | item.setOrderCode(record.getChargingOrderCode()); |
| | | item.setShareAmount(record.getShareAmount()); |
| | | item.setOnlineShareStatus(record.getOnlineShareStatus()); |
| | | item.setSubOrderId(record.getSubOrderId()); |
| | | item.setSubMerId(record.getSubMerId()); |
| | | item.setSubMerPrtclNo(record.getSubMerPrtclNo()); |
| | | item.setOriTrxDate(record.getOriTrxDate()); |
| | | item.setBankOrderId(record.getIcbcOrderId()); |
| | | item.setSubOrderSeq(record.getSubOrderSeq()); |
| | | item.setSubOrderTrxid(record.getSubOrderTrxid()); |
| | | return item; |
| | | } |
| | | |
| | | private <T> T requireFeignData(R<T> result, String message) { |
| | | if (result == null || R.isError(result) || result.getData() == null) { |
| | | throw new ServiceException(result != null && StringUtils.hasLength(result.getMsg()) ? result.getMsg() : message); |
| | | } |
| | | return result.getData(); |
| | | } |
| | | |
| | | |
| | |
| | | @Override |
| | | public AjaxResult chargingOrderCallback(Integer paymentType, String out_trade_no, String transaction_id, String attach) { |
| | | TChargingOrder chargingOrder = this.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getCode, out_trade_no)); |
| | | if (chargingOrder == null) { |
| | | return AjaxResult.error("支付回调订单不存在"); |
| | | } |
| | | if (chargingOrder.getRechargePaymentStatus() == 2) { |
| | | if (isIcbcOrder(chargingOrder) && StringUtils.hasLength(transaction_id) |
| | | && !transaction_id.equals(chargingOrder.getRechargeSerialNumber())) { |
| | | chargingOrder.setRechargeSerialNumber(transaction_id); |
| | | this.updateById(chargingOrder); |
| | | } |
| | | return AjaxResult.success(); |
| | | } |
| | | LocalDateTime callbackPayTime = LocalDateTime.now(); |
| | | int claimed = this.baseMapper.update(null, new LambdaUpdateWrapper<TChargingOrder>() |
| | | .eq(TChargingOrder::getId, chargingOrder.getId()) |
| | | .eq(TChargingOrder::getRechargePaymentStatus, 1) |
| | | .set(TChargingOrder::getRechargePaymentStatus, 2) |
| | | .set(TChargingOrder::getRechargeSerialNumber, transaction_id) |
| | | .set(TChargingOrder::getStatus, 2) |
| | | .set(TChargingOrder::getPayTime, callbackPayTime)); |
| | | if (claimed == 0) { |
| | | return AjaxResult.success(); |
| | | } |
| | | if (isBalancePayMode(chargingOrder.getPayMethod()) |
| | |
| | | chargingOrder.setRechargePaymentStatus(2); |
| | | chargingOrder.setRechargeSerialNumber(transaction_id); |
| | | chargingOrder.setStatus(2); |
| | | chargingOrder.setPayTime(LocalDateTime.now()); |
| | | chargingOrder.setPayTime(callbackPayTime); |
| | | |
| | | //添加安全检测数据到缓存中,每步安全检测完成后需要更新缓存数据 |
| | | PreChargeCheck preChargeCheck = new PreChargeCheck(); |
| | |
| | | preChargeCheck.setInsulationTesting(false); |
| | | preChargeCheck.setSecureConnectionDetection(false); |
| | | preChargeCheck.setStartupSuccess(1); |
| | | preChargeCheck.setStartCommandSent(false); |
| | | preChargeCheck.setAddTime(chargingOrder.getReservationTime() != null ? chargingOrder.getReservationTime() : LocalDateTime.now()); |
| | | String key = "AQJC_" + chargingOrder.getChargingGunId(); |
| | | redisService.setCacheObject(key, preChargeCheck, 24L, TimeUnit.HOURS); |
| | | |
| | |
| | | |
| | | if (discountAmount.compareTo(BigDecimal.ZERO) >= 0) { |
| | | //计算会员最大优惠金额 |
| | | TVip vip = vipClient.getInfo1(appUser.getVipId()).getData(); |
| | | BigDecimal maximumDeduction = vip.getMaximumDeduction(); |
| | | TVip vip = getVipSnapshot(data, appUser.getVipId()); |
| | | BigDecimal maximumDeduction = vip == null ? null : vip.getMaximumDeduction(); |
| | | //普通会员有最高优惠限制 |
| | | if (vip.getType() == 1 && discountAmount.compareTo(maximumDeduction) > 0) { |
| | | if (vip != null && Integer.valueOf(1).equals(vip.getType()) && maximumDeduction != null && discountAmount.compareTo(maximumDeduction) > 0) { |
| | | discountAmount = maximumDeduction; |
| | | } |
| | | } |
| | |
| | | this.updateById(chargingOrder); |
| | | |
| | | |
| | | TChargingPile chargingPile = chargingPileClient.getChargingPileById(tChargingGun.getChargingPileId()).getData(); |
| | | //调用远程启动充电消息 |
| | | PlatformStartCharging platformStartCharging = new PlatformStartCharging(); |
| | | platformStartCharging.setTransaction_serial_number(chargingOrder.getCode()); |
| | | platformStartCharging.setCharging_pile_code(chargingPile.getCode()); |
| | | platformStartCharging.setCharging_gun_code(tChargingGun.getCode()); |
| | | //使用订单id作为逻辑卡号 |
| | | platformStartCharging.setCard_number(chargingOrder.getId().toString()); |
| | | platformStartCharging.setAccount_balance(account_balance); |
| | | |
| | | log.info(chargingOrder.getCode() + ":-------------------远程调起开始充电请求-------------------" + platformStartCharging.toString()); |
| | | sendMessageClient.platformStartCharging(platformStartCharging); |
| | | |
| | | ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); |
| | | threadPoolExecutor.execute(()->{ |
| | | //推送成都监管 |
| | | NotificationConnectorChargeStatusVo vo = new NotificationConnectorChargeStatusVo(); |
| | | vo.setOrderCode(chargingOrder.getCode()); |
| | | vo.setChargingGunId(chargingOrder.getChargingGunId()); |
| | | chengDuJianGuanClient.notificationConnectorChargeStatus(vo); |
| | | }); |
| | | |
| | | |
| | | //异步线程检测远程启动的应答结果。如果失败,则需要全额退款 |
| | | Long id = chargingOrder.getId(); |
| | | //执行10分钟的定时任务检测 |
| | | ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); |
| | | scheduler.scheduleAtFixedRate(()->{ |
| | | if(timingDetection(id)){ |
| | | scheduler.shutdown(); |
| | | } |
| | | }, 10, 1, TimeUnit.SECONDS); |
| | | scheduleReservationStart(chargingOrder); |
| | | return AjaxResult.success(); |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | private AjaxResult validateReservation(Site site, LocalDateTime reservationTime) { |
| | | if (reservationTime == null) { |
| | | return null; |
| | | } |
| | | if (!Integer.valueOf(1).equals(site.getSupportOrder())) { |
| | | return AjaxResult.error("该电站暂不支持预约充电"); |
| | | } |
| | | if (reservationTime.isBefore(LocalDateTime.now().minusMinutes(1))) { |
| | | return AjaxResult.error("预约时间不能早于当前时间"); |
| | | } |
| | | if (site.getReservationAheadMinute() != null && site.getReservationAheadMinute() >= 0 |
| | | && reservationTime.isAfter(LocalDateTime.now().plusMinutes(site.getReservationAheadMinute()))) { |
| | | return AjaxResult.error("预约时间不能超过最早可提前预约时限"); |
| | | } |
| | | if (!StringUtils.hasLength(site.getReservationStartTime()) |
| | | || !StringUtils.hasLength(site.getReservationEndTime())) { |
| | | return AjaxResult.error("电站尚未配置预约时段"); |
| | | } |
| | | LocalTime start = LocalTime.parse(site.getReservationStartTime(), DateTimeFormatter.ofPattern("HH:mm")); |
| | | LocalTime end = LocalTime.parse(site.getReservationEndTime(), DateTimeFormatter.ofPattern("HH:mm")); |
| | | if (!isTimeInWindow(reservationTime.toLocalTime(), start, end)) { |
| | | return AjaxResult.error("预约时间不在允许预约时段内"); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private boolean isTimeInWindow(LocalTime value, LocalTime start, LocalTime end) { |
| | | if (start.equals(end)) { |
| | | return true; |
| | | } |
| | | if (start.isBefore(end)) { |
| | | return !value.isBefore(start) && !value.isAfter(end); |
| | | } |
| | | return !value.isBefore(start) || !value.isAfter(end); |
| | | } |
| | | |
| | | private boolean isSafetyCheckPassed(PreChargeCheck check) { |
| | | return check != null |
| | | && Boolean.TRUE.equals(check.getElectronicLockLock()) |
| | | && Boolean.TRUE.equals(check.getInsulationTesting()) |
| | | && Boolean.TRUE.equals(check.getSecureConnectionDetection()); |
| | | } |
| | | |
| | | private String normalizeVin(String vin) { |
| | | return StringUtils.hasLength(vin) ? vin.replaceAll("\\s+", "").toUpperCase(Locale.ROOT) : null; |
| | | } |
| | | |
| | | private boolean vinMatches(String expectedVin, String actualVin) { |
| | | if (!StringUtils.hasLength(expectedVin) || !StringUtils.hasLength(actualVin)) { |
| | | return false; |
| | | } |
| | | return expectedVin.equals(actualVin) |
| | | || expectedVin.equals(new StringBuilder(actualVin).reverse().toString()); |
| | | } |
| | | |
| | | private void scheduleReservationStart(TChargingOrder chargingOrder) { |
| | | if (chargingOrder.getReservationTime() == null || !chargingOrder.getReservationTime().isAfter(LocalDateTime.now())) { |
| | | return; |
| | | } |
| | | long delay = Math.max(0L, Duration.between(LocalDateTime.now(), chargingOrder.getReservationTime()).toMillis()); |
| | | ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); |
| | | scheduler.schedule(() -> { |
| | | try { |
| | | TChargingOrder latest = this.getById(chargingOrder.getId()); |
| | | if (latest == null || latest.getStatus() != 2) { |
| | | return; |
| | | } |
| | | PreChargeCheck check = redisService.getCacheObject("AQJC_" + latest.getChargingGunId()); |
| | | if (isSafetyCheckPassed(check)) { |
| | | startChargingAfterSafety(latest, check); |
| | | } |
| | | } finally { |
| | | scheduler.shutdown(); |
| | | } |
| | | }, delay, TimeUnit.MILLISECONDS); |
| | | } |
| | | |
| | | private void startChargingAfterSafety(TChargingOrder chargingOrder, PreChargeCheck check) { |
| | | RLock lock = redissonClient.getLock("START_AFTER_SAFETY:" + chargingOrder.getId()); |
| | | try { |
| | | if (!lock.tryLock(1, 10, TimeUnit.SECONDS)) { |
| | | return; |
| | | } |
| | | TChargingOrder latest = this.getById(chargingOrder.getId()); |
| | | if (latest == null || latest.getStatus() != 2 || Boolean.TRUE.equals(check.getStartCommandSent())) { |
| | | return; |
| | | } |
| | | if (latest.getReservationTime() != null && latest.getReservationTime().isAfter(LocalDateTime.now())) { |
| | | return; |
| | | } |
| | | TChargingGun gun = chargingGunClient.getChargingGunById(latest.getChargingGunId()).getData(); |
| | | TChargingPile pile = chargingPileClient.getChargingPileById(gun.getChargingPileId()).getData(); |
| | | String actualVin = normalizeVin(check.getActualVin()); |
| | | String expectedVin = normalizeVin(latest.getExpectedVin()); |
| | | if (Integer.valueOf(1).equals(pile.getVinFlag()) |
| | | && !vinMatches(expectedVin, actualVin)) { |
| | | check.setStartupSuccess(3); |
| | | check.setFailureCause(9); |
| | | latest.setActualVin(actualVin); |
| | | this.updateById(latest); |
| | | redisService.setCacheObject("AQJC_" + latest.getChargingGunId(), check, 24L, TimeUnit.HOURS); |
| | | refund(latest.getCode()); |
| | | return; |
| | | } |
| | | PlatformStartCharging command = new PlatformStartCharging(); |
| | | command.setTransaction_serial_number(latest.getCode()); |
| | | command.setCharging_pile_code(pile.getCode()); |
| | | command.setCharging_gun_code(gun.getCode()); |
| | | command.setCard_number(latest.getId().toString()); |
| | | command.setAccount_balance(latest.getChargeAmount()); |
| | | check.setStartCommandSent(true); |
| | | latest.setActualVin(actualVin); |
| | | this.updateById(latest); |
| | | redisService.setCacheObject("AQJC_" + latest.getChargingGunId(), check, 24L, TimeUnit.HOURS); |
| | | log.info(latest.getCode() + ":安全检测及VIN校验通过,下发启动充电命令:" + command); |
| | | sendMessageClient.platformStartCharging(command); |
| | | ScheduledExecutorService resultScheduler = Executors.newScheduledThreadPool(1); |
| | | resultScheduler.scheduleAtFixedRate(() -> { |
| | | if (timingDetection(latest.getId())) { |
| | | resultScheduler.shutdown(); |
| | | } |
| | | }, 10, 1, TimeUnit.SECONDS); |
| | | } catch (InterruptedException e) { |
| | | Thread.currentThread().interrupt(); |
| | | throw new ServiceException("启动充电锁等待被中断"); |
| | | } finally { |
| | | if (lock.isHeldByCurrentThread()) { |
| | | lock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 定时检测mongodb数据库数据 |
| | | * |
| | |
| | | TChargingGun chargingGun = chargingGunClient.getChargingGunByCode(code).getData(); |
| | | PreChargeCheck preChargeCheck1 = redisService.getCacheObject("AQJC_" + chargingGun.getId()); |
| | | if (null != preChargeCheck1) { |
| | | preChargeCheck1.setElectronicLockLock(true); |
| | | preChargeCheck1.setInsulationTesting(true); |
| | | preChargeCheck1.setSecureConnectionDetection(securityDetection.getSecure_connection() == 1); |
| | | preChargeCheck1.setElectronicLockLock(Integer.valueOf(1).equals(securityDetection.getElectronic_lock_lock())); |
| | | preChargeCheck1.setInsulationTesting(Integer.valueOf(0).equals(securityDetection.getNsulation_detection())); |
| | | preChargeCheck1.setSecureConnectionDetection(Integer.valueOf(1).equals(securityDetection.getSecure_connection())); |
| | | preChargeCheck1.setActualVin(normalizeVin(securityDetection.getVin())); |
| | | redisService.setCacheObject("AQJC_" + chargingGun.getId(), preChargeCheck1, 24L, TimeUnit.HOURS); |
| | | if (isSafetyCheckPassed(preChargeCheck1)) { |
| | | TChargingOrder pendingOrder = this.getOne(new LambdaQueryWrapper<TChargingOrder>() |
| | | .eq(TChargingOrder::getChargingGunId, chargingGun.getId()) |
| | | .eq(TChargingOrder::getRechargePaymentStatus, 2) |
| | | .eq(TChargingOrder::getStatus, 2) |
| | | .eq(TChargingOrder::getDelFlag, 0) |
| | | .orderByDesc(TChargingOrder::getCreateTime) |
| | | .last("limit 1")); |
| | | if (pendingOrder != null) { |
| | | startChargingAfterSafety(pendingOrder, preChargeCheck1); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | //推送状态给三方平台 |
| | | tcecClient.pushChargingGunStatus(chargingGun.getFullNumber(), 4); |
| | | } |
| | | wxSubscribeMessageService.sendChargeStart(order, site, chargingGun); |
| | | } |
| | | redisService.setCacheObject("AQJC_" + order.getChargingGunId(), preChargeCheck1, 24L, TimeUnit.HOURS); |
| | | |
| | |
| | | * @param chargingOrder |
| | | */ |
| | | public void editUserTag(TChargingOrder chargingOrder) { |
| | | if (chargingOrder == null || chargingOrder.getAppUserId() == null) { |
| | | return; |
| | | } |
| | | //处理用户标签数据 |
| | | List<TUserTag> data = userTagClient.getAllUserTag().getData(); |
| | | R<List<TUserTag>> tagResult = userTagClient.getAllUserTag(); |
| | | List<TUserTag> data = tagResult == null ? null : tagResult.getData(); |
| | | if (data == null || data.isEmpty()) { |
| | | return; |
| | | } |
| | | //累计充电次数 |
| | | List<TUserTag> userTagList1 = data.stream().filter(s -> s.getStandardCondition() == 1).collect(Collectors.toList()); |
| | | List<TUserTag> userTagList1 = data.stream().filter(s -> Integer.valueOf(1).equals(s.getStandardCondition())).collect(Collectors.toList()); |
| | | for (TUserTag tUserTag : userTagList1) { |
| | | Integer times = JSON.parseObject(tUserTag.getConditions()).getInteger("times"); |
| | | JSONObject conditions = JSON.parseObject(tUserTag.getConditions()); |
| | | Integer times = conditions == null ? null : conditions.getInteger("times"); |
| | | if (times == null || times <= 0 || tUserTag.getCreateTime() == null) { |
| | | continue; |
| | | } |
| | | TAppUserTag appUserTag = new TAppUserTag(); |
| | | appUserTag.setAppUserId(chargingOrder.getAppUserId()); |
| | | appUserTag.setUserTagId(tUserTag.getId()); |
| | |
| | | } |
| | | } |
| | | //充电频率 |
| | | List<TUserTag> userTagList2 = data.stream().filter(s -> s.getStandardCondition() == 2).collect(Collectors.toList()); |
| | | List<TUserTag> userTagList2 = data.stream().filter(s -> Integer.valueOf(2).equals(s.getStandardCondition())).collect(Collectors.toList()); |
| | | for (TUserTag tUserTag : userTagList2) { |
| | | TAppUserTag appUserTag = new TAppUserTag(); |
| | | appUserTag.setAppUserId(chargingOrder.getAppUserId()); |
| | | appUserTag.setUserTagId(tUserTag.getId()); |
| | | TAppUserTag data1 = appUserTagClient.getUserTag(appUserTag).getData(); |
| | | |
| | | Integer day = JSON.parseObject(tUserTag.getConditions()).getInteger("day"); |
| | | Integer times = JSON.parseObject(tUserTag.getConditions()).getInteger("times"); |
| | | JSONObject conditions = JSON.parseObject(tUserTag.getConditions()); |
| | | Integer day = conditions == null ? null : conditions.getInteger("day"); |
| | | Integer times = conditions == null ? null : conditions.getInteger("times"); |
| | | if (day == null || day <= 0 || times == null || times <= 0) { |
| | | continue; |
| | | } |
| | | LocalDateTime endTime = LocalDateTime.now(); |
| | | endTime.plusDays(day); |
| | | String start = tUserTag.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); |
| | | String start = endTime.minusDays(day).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); |
| | | String end = endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); |
| | | long count1 = this.count(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getAppUserId, chargingOrder.getAppUserId()) |
| | | .eq(TChargingOrder::getRechargePaymentStatus, 2).between(TChargingOrder::getStartTime, start, end).eq(TChargingOrder::getDelFlag, 0)); |
| | |
| | | |
| | | TAppUser appUser1 = appUserClient.getUserById(appUser.getInviteUserId()).getData(); |
| | | if (null != appUser1.getVipId()) { |
| | | TVip vip = vipClient.getInfo1(appUser1.getVipId()).getData(); |
| | | Integer doubleIntegration = vip.getDoubleIntegration(); |
| | | GetAppUserVipDetail getAppUserVipDetail = new GetAppUserVipDetail(); |
| | | getAppUserVipDetail.setAppUserId(appUser1.getId()); |
| | | getAppUserVipDetail.setVipId(appUser1.getVipId()); |
| | | TAppUserVipDetail vipDetail = appUserVipDetailClient.getAppUserVipDetail(getAppUserVipDetail).getData(); |
| | | TVip vip = getVipSnapshot(vipDetail, appUser1.getVipId()); |
| | | Integer doubleIntegration = vip == null ? null : vip.getDoubleIntegration(); |
| | | //双倍积分 |
| | | if (1 == doubleIntegration) { |
| | | if (Integer.valueOf(1).equals(doubleIntegration)) { |
| | | num1 *= 2; |
| | | } |
| | | |
| | |
| | | |
| | | @Override |
| | | public TCharingOrderVO chargingOrder(ChargingOrderQuery dto) { |
| | | TCharingOrderVO summary = chargingOrderSummary(dto); |
| | | summary.setList(chargingOrderList(dto)); |
| | | return summary; |
| | | } |
| | | |
| | | @Override |
| | | public PageInfo<ChargingOrderVO> chargingOrderList(ChargingOrderQuery dto) { |
| | | String[] timeRange = parseChargingOrderTimeRange(dto); |
| | | String startTime1 = timeRange[0]; |
| | | String startTime2 = timeRange[1]; |
| | | String endTime1 = timeRange[2]; |
| | | String endTime2 = timeRange[3]; |
| | | PageInfo<ChargingOrderVO> pageInfo = new PageInfo<>(dto.getPageCurr(), dto.getPageSize()); |
| | | if (StringUtils.hasLength(dto.getPhone())) { |
| | | List<Long> data = appUserClient.getUserIdsByPhone(dto.getPhone()).getData(); |
| | | if (data == null || data.isEmpty()) { |
| | | return pageInfo; |
| | | } |
| | | dto.setUserIds(data); |
| | | } |
| | | Long userId = tokenService.getLoginUser().getUserid(); |
| | | SysUser sysUser = sysUserClient.getSysUser(userId).getData(); |
| | | Integer roleType = sysUser.getRoleType(); |
| | | setChargingOrderAuthSiteIds(dto, sysUser, userId, roleType); |
| | | List<ChargingOrderVO> list = this.baseMapper.chargingOrder(pageInfo, dto, startTime1, startTime2, endTime1, endTime2); |
| | | fillChargingOrderBalanceIncome(list); |
| | | fillChargingOrderShareIncome(list); |
| | | fillChargingOrderPageExtra(list, dto.getSiteIds(), sysUser, roleType); |
| | | pageInfo.setRecords(list); |
| | | return pageInfo; |
| | | } |
| | | |
| | | @Override |
| | | public TCharingOrderVO chargingOrderSummary(ChargingOrderQuery dto) { |
| | | TCharingOrderVO tCharingOrderVO = new TCharingOrderVO(); |
| | | List<TChargingGun> allGun = chargingGunClient.getAllGun().getData(); |
| | | List<TChargingPile> allPile = chargingGunClient.getAllPile().getData(); |
| | | List<Site> data9 = siteClient.getSiteAll().getData(); |
| | | List<TAppUser> data6 = appUserClient.getAllUser().getData(); |
| | | List<TAppUserCar> data4 = appUserCarClient.getAllCar().getData(); |
| | | fillEmptyChargingOrderSummary(tCharingOrderVO); |
| | | String[] timeRange = parseChargingOrderTimeRange(dto); |
| | | String startTime1 = timeRange[0]; |
| | | String startTime2 = timeRange[1]; |
| | | String endTime1 = timeRange[2]; |
| | | String endTime2 = timeRange[3]; |
| | | if (StringUtils.hasLength(dto.getPhone())) { |
| | | List<Long> data = appUserClient.getUserIdsByPhone(dto.getPhone()).getData(); |
| | | if (data == null || data.isEmpty()) { |
| | | return tCharingOrderVO; |
| | | } |
| | | dto.setUserIds(data); |
| | | } |
| | | Long userId = tokenService.getLoginUser().getUserid(); |
| | | SysUser sysUser = sysUserClient.getSysUser(userId).getData(); |
| | | setChargingOrderAuthSiteIds(dto, sysUser, userId, sysUser.getRoleType()); |
| | | Map<String, Object> map = this.baseMapper.chargingOrderBaseSummary(dto, startTime1, startTime2, endTime1, endTime2); |
| | | if (null != map) { |
| | | tCharingOrderVO.setTotal(getSummaryDecimal(map, "total")); |
| | | tCharingOrderVO.setTime(getSummaryLong(map, "time")); |
| | | tCharingOrderVO.setIncome(getSummaryDecimal(map, "income")); |
| | | tCharingOrderVO.setElectronicMoney(getSummaryDecimal(map, "electronicMoney")); |
| | | tCharingOrderVO.setServiceMoney(getSummaryDecimal(map, "serviceMoney")); |
| | | tCharingOrderVO.setWechatIncome(getSummaryDecimal(map, "wechatIncome")); |
| | | tCharingOrderVO.setAlipayIncome(getSummaryDecimal(map, "alipayIncome")); |
| | | tCharingOrderVO.setWhitelistChargingIncome(getSummaryDecimal(map, "whitelistChargingIncome")); |
| | | tCharingOrderVO.setWhitelistRefund(getSummaryDecimal(map, "whitelistRefund")); |
| | | } |
| | | List<Long> summaryOrderIds = this.baseMapper.chargingOrderSummaryOrderIds(dto, startTime1, startTime2, endTime1, endTime2); |
| | | Map<String, Object> shareIncomeMap = getChargingOrderShareIncomeSummary(summaryOrderIds); |
| | | tCharingOrderVO.setPlatformIncomeTotal(getSummaryDecimal(shareIncomeMap, "platformIncomeTotal")); |
| | | tCharingOrderVO.setPartnerIncomeTotal(getSummaryDecimal(shareIncomeMap, "partnerIncomeTotal")); |
| | | Map<String, Object> balanceMap = getExchangeCodeBalanceSummary(summaryOrderIds); |
| | | tCharingOrderVO.setBalanceIncome(getSummaryDecimal(balanceMap, "balanceIncome")); |
| | | tCharingOrderVO.setBalanceGiftIncome(getSummaryDecimal(balanceMap, "balanceGiftIncome")); |
| | | return tCharingOrderVO; |
| | | } |
| | | |
| | | private String[] parseChargingOrderTimeRange(ChargingOrderQuery dto) { |
| | | String startTime1 = null; |
| | | String startTime2 = null; |
| | | String endTime1 = null; |
| | | String endTime2 = null; |
| | | |
| | | if (StringUtils.hasLength(dto.getStartTime())) { |
| | | String[] split = dto.getStartTime().split(" - "); |
| | | startTime1 = split[0]; |
| | |
| | | endTime1 = split[0]; |
| | | endTime2 = split[1]; |
| | | } |
| | | PageInfo<ChargingOrderVO> pageInfo = new PageInfo<>(dto.getPageCurr(), dto.getPageSize()); |
| | | |
| | | if (StringUtils.hasLength(dto.getPhone())) { |
| | | List<Long> data = appUserClient.getUserIdsByPhone(dto.getPhone()).getData(); |
| | | if (data.isEmpty()) { |
| | | tCharingOrderVO.setList(pageInfo); |
| | | return tCharingOrderVO; |
| | | } |
| | | dto.setUserIds(data); |
| | | return new String[]{startTime1, startTime2, endTime1, endTime2}; |
| | | } |
| | | |
| | | private void fillChargingOrderPageExtra(List<ChargingOrderVO> list, List<Integer> siteIds, SysUser sysUser, Integer roleType) { |
| | | if (list == null || list.isEmpty()) { |
| | | return; |
| | | } |
| | | Long userId = tokenService.getLoginUser().getUserid(); |
| | | // 查询当前登陆人按钮权限 |
| | | SysUser sysUser = sysUserClient.getSysUser(tokenService.getLoginUser().getUserid()).getData(); |
| | | Integer roleType = sysUser.getRoleType(); |
| | | setChargingOrderAuthSiteIds(dto, sysUser, userId, roleType); |
| | | List<Integer> siteIds = dto.getSiteIds(); |
| | | List<ChargingOrderVO> list = this.baseMapper.chargingOrder(pageInfo, dto, startTime1, startTime2, endTime1, endTime2); |
| | | fillChargingOrderBalanceIncome(list); |
| | | fillChargingOrderShareIncome(list); |
| | | List<TChargingOrderAccountingStrategy> list3 = chargingOrderAccountingStrategyService.list(); |
| | | List<Long> orderIds = list.stream().map(ChargingOrderVO::getId).filter(Objects::nonNull).collect(Collectors.toList()); |
| | | List<Integer> pageSiteIds = list.stream().map(ChargingOrderVO::getSiteId).filter(Objects::nonNull).distinct().collect(Collectors.toList()); |
| | | List<Integer> pageGunIds = list.stream().map(ChargingOrderVO::getChargingGunId).filter(Objects::nonNull).distinct().collect(Collectors.toList()); |
| | | List<TChargingGun> pageGuns = pageGunIds.isEmpty() ? new ArrayList<>() : Optional.ofNullable(chargingGunClient.getChargingGunByIds(pageGunIds).getData()).orElse(new ArrayList<>()); |
| | | Map<Integer, TChargingGun> gunMap = pageGuns.stream() |
| | | .filter(item -> item.getId() != null) |
| | | .collect(Collectors.toMap(TChargingGun::getId, item -> item, (a, b) -> a)); |
| | | List<TChargingPile> pagePiles = pageSiteIds.isEmpty() ? new ArrayList<>() : Optional.ofNullable(chargingPileClient.getChargingPileBySiteIds(pageSiteIds).getData()).orElse(new ArrayList<>()); |
| | | Map<Integer, TChargingPile> pileMap = pagePiles.stream() |
| | | .filter(item -> item.getId() != null) |
| | | .collect(Collectors.toMap(TChargingPile::getId, item -> item, (a, b) -> a)); |
| | | List<Site> pageSites = pageSiteIds.isEmpty() ? new ArrayList<>() : Optional.ofNullable(siteClient.getSiteByIds(pageSiteIds).getData()).orElse(new ArrayList<>()); |
| | | Map<Integer, Site> siteMap = pageSites.stream() |
| | | .filter(item -> item.getId() != null) |
| | | .collect(Collectors.toMap(Site::getId, item -> item, (a, b) -> a)); |
| | | Map<Long, List<TChargingOrderAccountingStrategy>> strategyMap = orderIds.isEmpty() ? new HashMap<>() : |
| | | chargingOrderAccountingStrategyService.lambdaQuery() |
| | | .in(TChargingOrderAccountingStrategy::getChargingOrderId, orderIds) |
| | | .list() |
| | | .stream() |
| | | .collect(Collectors.groupingBy(TChargingOrderAccountingStrategy::getChargingOrderId)); |
| | | Map<Integer, Boolean> authRecordMap = new HashMap<>(); |
| | | if (roleType == 2 && siteIds != null) { |
| | | for (Integer siteId : siteIds) { |
| | | List<Boolean> permission = partnerClient.getChargingOrderMenu(sysUser.getObjectId(), siteId).getData(); |
| | | authRecordMap.put(siteId, permission != null && !permission.isEmpty() && Boolean.TRUE.equals(permission.get(0))); |
| | | } |
| | | } |
| | | for (ChargingOrderVO chargingOrderVO : list) { |
| | | if (roleType == 2) { |
| | | for (Integer siteId : siteIds) { |
| | | // 校验有没有这个站点的权限 |
| | | List<Boolean> t1 = partnerClient.getChargingOrderMenu(sysUser.getObjectId(), siteId).getData(); |
| | | chargingOrderVO.setAuthRecord(t1.get(0)); |
| | | } |
| | | if (roleType == 2 && chargingOrderVO.getSiteId() != null) { |
| | | chargingOrderVO.setAuthRecord(Boolean.TRUE.equals(authRecordMap.get(chargingOrderVO.getSiteId()))); |
| | | } |
| | | chargingOrderVO.setCommissionAmount(chargingOrderVO.getRechargeAmount() != null ? chargingOrderVO.getRechargeAmount().multiply(new BigDecimal("0.006")) : new BigDecimal("0")); |
| | | chargingOrderVO.setPlatFormMoney(chargingOrderVO.getRechargeAmount() != null ? chargingOrderVO.getRechargeAmount().multiply(new BigDecimal("0.006")).setScale(2, BigDecimal.ROUND_DOWN) : new BigDecimal("0")); |
| | | chargingOrderVO.setUid(chargingOrderVO.getId() + ""); |
| | | TChargingGun data3 = allGun.stream().filter(e -> e.getId().equals(chargingOrderVO.getChargingGunId())).findFirst().orElse(null); |
| | | TChargingPile data2 = allPile.stream().filter(e -> e.getId().equals(chargingOrderVO.getChargingPileId())).findFirst().orElse(null); |
| | | if (chargingOrderVO.getSiteId() != null) { |
| | | List<Integer> integers = new ArrayList<>(); |
| | | integers.add(chargingOrderVO.getSiteId()); |
| | | Site site = data9.stream().filter(e -> e.getId().equals(chargingOrderVO.getSiteId())).findFirst().orElse(null); |
| | | if (site != null) chargingOrderVO.setSiteName(site.getName()); |
| | | Site site = siteMap.get(chargingOrderVO.getSiteId()); |
| | | if (site != null) { |
| | | chargingOrderVO.setSiteName(site.getName()); |
| | | } |
| | | if (data2 != null && data3 != null) { |
| | | chargingOrderVO.setTerminalName(data2.getName() + "-" + data3.getName()); |
| | | TChargingGun gun = gunMap.get(chargingOrderVO.getChargingGunId()); |
| | | TChargingPile pile = pileMap.get(chargingOrderVO.getChargingPileId()); |
| | | if (pile != null && gun != null) { |
| | | chargingOrderVO.setTerminalName(pile.getName() + "-" + gun.getName()); |
| | | } |
| | | // 充电订单 明细记录 |
| | | List<TChargingOrderAccountingStrategy> chargingOrderId = list3.stream().filter(e -> e.getChargingOrderId().equals(chargingOrderVO.getId())).collect(Collectors.toList()); |
| | | List<TChargingOrderAccountingStrategy> strategies = strategyMap.getOrDefault(chargingOrderVO.getId(), new ArrayList<>()); |
| | | if (chargingOrderVO.getStartTime() != null && chargingOrderVO.getEndTime() != null) { |
| | | LocalDateTime startTime = chargingOrderVO.getStartTime(); |
| | | LocalDateTime endTime = chargingOrderVO.getEndTime(); |
| | | // 计算时间差 单位秒 |
| | | long between = ChronoUnit.SECONDS.between(startTime, endTime); |
| | | long between = ChronoUnit.SECONDS.between(chargingOrderVO.getStartTime(), chargingOrderVO.getEndTime()); |
| | | chargingOrderVO.setChargingSecond(between); |
| | | |
| | | BigDecimal electronic_reduce = chargingOrderId.stream().map(TChargingOrderAccountingStrategy::getPeriodElectricPrice).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | chargingOrderVO.setElectrovalence(electronic_reduce); |
| | | BigDecimal service_reduce = chargingOrderId.stream().map(TChargingOrderAccountingStrategy::getPeriodServicePrice).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | if (!chargingOrderVO.getOrderSource().equals(2)) { |
| | | chargingOrderVO.setServiceCharge(service_reduce); |
| | | BigDecimal electronicReduce = strategies.stream().map(TChargingOrderAccountingStrategy::getPeriodElectricPrice).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | chargingOrderVO.setElectrovalence(electronicReduce); |
| | | BigDecimal serviceReduce = strategies.stream().map(TChargingOrderAccountingStrategy::getPeriodServicePrice).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | if (!Objects.equals(chargingOrderVO.getOrderSource(), 2)) { |
| | | chargingOrderVO.setServiceCharge(serviceReduce); |
| | | chargingOrderVO.setSharingAmount(BigDecimal.ZERO); |
| | | } else { |
| | | chargingOrderVO.setServiceCharge(service_reduce.multiply(new BigDecimal("0.8"))); |
| | | chargingOrderVO.setSharingAmount(service_reduce.multiply(new BigDecimal("0.2"))); |
| | | chargingOrderVO.setServiceCharge(serviceReduce.multiply(new BigDecimal("0.8"))); |
| | | chargingOrderVO.setSharingAmount(serviceReduce.multiply(new BigDecimal("0.2"))); |
| | | } |
| | | } |
| | | // 充电时段数 |
| | | int size = chargingOrderId.size(); |
| | | chargingOrderVO.setCount(size); |
| | | // 用户手机号 |
| | | TAppUser data = data6.stream().filter(e -> e.getId().equals(chargingOrderVO.getAppUserId())).findFirst().orElse(null); |
| | | if (data != null) { |
| | | // 车牌号 |
| | | chargingOrderVO.setPhone(data.getPhone()); |
| | | List<Long> longs = new ArrayList<>(); |
| | | if (chargingOrderVO.getAppUserCarId() != null) { |
| | | longs.add(chargingOrderVO.getAppUserCarId()); |
| | | TAppUserCar tAppUserCar = data4.stream().filter(e -> e.getId().equals(chargingOrderVO.getAppUserCarId())).findFirst().orElse(null); |
| | | if (tAppUserCar != null) { |
| | | chargingOrderVO.setLicensePlate(tAppUserCar.getLicensePlate()); |
| | | } |
| | | } else { |
| | | chargingOrderVO.setLicensePlate(chargingOrderVO.getPlateNum()); |
| | | } |
| | | |
| | | } |
| | | chargingOrderVO.setCount(strategies.size()); |
| | | chargingOrderVO.setLicensePlate(chargingOrderVO.getPlateNum()); |
| | | } |
| | | fillEmptyChargingOrderSummary(tCharingOrderVO); |
| | | Map<String, Object> map = this.baseMapper.chargingOrderNolimit(dto, startTime1, startTime2, endTime1, endTime2); |
| | | if (null != map) { |
| | | tCharingOrderVO.setTotal(getSummaryDecimal(map, "total")); |
| | | tCharingOrderVO.setTime(getSummaryLong(map, "time")); |
| | | tCharingOrderVO.setIncome(getSummaryDecimal(map, "income")); |
| | | tCharingOrderVO.setElectronicMoney(getSummaryDecimal(map, "electronicMoney")); |
| | | tCharingOrderVO.setServiceMoney(getSummaryDecimal(map, "serviceMoney")); |
| | | } |
| | | Map<String, Object> fundMap = this.baseMapper.chargingOrderFundSummary(dto, startTime1, startTime2, endTime1, endTime2); |
| | | if (null != fundMap) { |
| | | tCharingOrderVO.setWechatIncome(getSummaryDecimal(fundMap, "wechatIncome")); |
| | | tCharingOrderVO.setAlipayIncome(getSummaryDecimal(fundMap, "alipayIncome")); |
| | | tCharingOrderVO.setWhitelistChargingIncome(getSummaryDecimal(fundMap, "whitelistChargingIncome")); |
| | | tCharingOrderVO.setWhitelistRefund(getSummaryDecimal(fundMap, "whitelistRefund")); |
| | | } |
| | | Map<String, Object> shareIncomeMap = getChargingOrderShareIncomeSummary(dto, startTime1, startTime2, endTime1, endTime2); |
| | | tCharingOrderVO.setPlatformIncomeTotal(getSummaryDecimal(shareIncomeMap, "platformIncomeTotal")); |
| | | tCharingOrderVO.setPartnerIncomeTotal(getSummaryDecimal(shareIncomeMap, "partnerIncomeTotal")); |
| | | Map<String, Object> balanceMap = getExchangeCodeBalanceSummary(dto, startTime1, startTime2, endTime1, endTime2); |
| | | tCharingOrderVO.setBalanceIncome(getSummaryDecimal(balanceMap, "balanceIncome")); |
| | | tCharingOrderVO.setBalanceGiftIncome(getSummaryDecimal(balanceMap, "balanceGiftIncome")); |
| | | pageInfo.setRecords(list); |
| | | tCharingOrderVO.setList(pageInfo); |
| | | return tCharingOrderVO; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 填充充电桩订单列表余额收入,余额收入按真实充值余额统计,赠送余额单独展示。 |
| | |
| | | /** |
| | | * 汇总充电桩订单余额收入,兑换码使用记录的usedAmount包含赠送余额,SQL中需扣除giftUsedAmount。 |
| | | */ |
| | | private Map<String, Object> getExchangeCodeBalanceSummary(ChargingOrderQuery dto, String startTime1, String startTime2, String endTime1, String endTime2) { |
| | | private Map<String, Object> getExchangeCodeBalanceSummary(List<Long> orderIds) { |
| | | Map<String, Object> totalMap = new HashMap<>(); |
| | | totalMap.put("balanceIncome", BigDecimal.ZERO); |
| | | totalMap.put("balanceGiftIncome", BigDecimal.ZERO); |
| | | List<Long> orderIds = this.baseMapper.chargingOrderSummaryOrderIds(dto, startTime1, startTime2, endTime1, endTime2); |
| | | if (orderIds == null || orderIds.isEmpty()) { |
| | | return totalMap; |
| | | } |
| | |
| | | return totalMap; |
| | | } |
| | | |
| | | private Map<String, Object> getChargingOrderShareIncomeSummary(ChargingOrderQuery dto, String startTime1, String startTime2, String endTime1, String endTime2) { |
| | | private Map<String, Object> getChargingOrderShareIncomeSummary(List<Long> orderIds) { |
| | | Map<String, Object> totalMap = new HashMap<>(); |
| | | totalMap.put("platformIncomeTotal", BigDecimal.ZERO); |
| | | totalMap.put("partnerIncomeTotal", BigDecimal.ZERO); |
| | | List<Long> orderIds = this.baseMapper.chargingOrderSummaryOrderIds(dto, startTime1, startTime2, endTime1, endTime2); |
| | | if (orderIds == null || orderIds.isEmpty()) { |
| | | return totalMap; |
| | | } |
| | |
| | | |
| | | @Override |
| | | public R<PageInfo<PayOrderDto>> payOrderQuery(PayOrderQueryDto payOrderQueryDto) { |
| | | if (payOrderQueryDto == null) { |
| | | payOrderQueryDto = new PayOrderQueryDto(); |
| | | } |
| | | PageInfo<PayOrderDto> pageInfo = new PageInfo<>(payOrderQueryDto.getPageCurr(), payOrderQueryDto.getPageSize()); |
| | | List<PayOrderDto> list = this.baseMapper.payOrderQuery(pageInfo, payOrderQueryDto); |
| | | if (list == null || list.isEmpty()) { |
| | | pageInfo.setRecords(Collections.emptyList()); |
| | | return R.ok(pageInfo); |
| | | } |
| | | for (PayOrderDto payOrderDto : list) { |
| | | if (payOrderDto.getType() == 2 || payOrderDto.getType() == 3) { |
| | | if (payOrderDto == null) { |
| | | continue; |
| | | } |
| | | if (Integer.valueOf(2).equals(payOrderDto.getType()) || Integer.valueOf(3).equals(payOrderDto.getType())) { |
| | | payOrderDto.setFinalAmount(payOrderDto.getOrderAmount()); |
| | | } |
| | | if (payOrderDto.getType() == 1 && payOrderDto.getStatus() == 3) { |
| | | if (Integer.valueOf(1).equals(payOrderDto.getType()) && Integer.valueOf(3).equals(payOrderDto.getStatus())) { |
| | | payOrderDto.setFinalAmount(payOrderDto.getOrderAmount()); |
| | | payOrderDto.setOrderAmount(payOrderDto.getOrderAmount()); |
| | | payOrderDto.setPaymentAmount(payOrderDto.getOrderAmount()); |
| | |
| | | pageInfo.setRecords(list); |
| | | return R.ok(pageInfo); |
| | | } |
| | | |
| | | @Override |
| | | public R<PageInfo<EnterpriseChargingAnalysisVO>> enterpriseChargingAnalysis(EnterpriseChargingAnalysisQuery query) { |
| | | PageInfo<EnterpriseChargingAnalysisVO> pageInfo = new PageInfo<>(query.getPageCurr(), query.getPageSize()); |
| | | List<EnterpriseChargingAnalysisVO> list = this.baseMapper.enterpriseChargingAnalysis(pageInfo, query); |
| | | for (EnterpriseChargingAnalysisVO row : list) { |
| | | EnterpriseChargingAnalysisQuery rowQuery = buildEnterpriseRowQuery(query, row); |
| | | List<Map<String, Object>> topSites = this.baseMapper.enterpriseTopSites(rowQuery); |
| | | String topSiteNames = topSites.stream() |
| | | .map(item -> Objects.toString(item.get("siteName"), "")) |
| | | .filter(org.springframework.util.StringUtils::hasText) |
| | | .collect(Collectors.joining("、")); |
| | | row.setTopSiteNames(topSiteNames); |
| | | row.setAverageChargingInterval(calculateAverageChargingInterval(this.baseMapper.enterpriseChargingTimes(rowQuery))); |
| | | } |
| | | pageInfo.setRecords(list); |
| | | return R.ok(pageInfo); |
| | | } |
| | | |
| | | @Override |
| | | public R<PageInfo<EnterpriseChargingDetailVO>> enterpriseChargingDetail(EnterpriseChargingAnalysisQuery query) { |
| | | PageInfo<EnterpriseChargingDetailVO> pageInfo = new PageInfo<>(query.getPageCurr(), query.getPageSize()); |
| | | List<EnterpriseChargingDetailVO> list = this.baseMapper.enterpriseChargingDetail(pageInfo, query); |
| | | pageInfo.setRecords(list); |
| | | return R.ok(pageInfo); |
| | | } |
| | | |
| | | @Override |
| | | public List<EnterpriseChargingDetailVO> enterpriseChargingDetailExport(EnterpriseChargingAnalysisQuery query) { |
| | | return this.baseMapper.enterpriseChargingDetailExport(query); |
| | | } |
| | | |
| | | private EnterpriseChargingAnalysisQuery buildEnterpriseRowQuery(EnterpriseChargingAnalysisQuery source, EnterpriseChargingAnalysisVO row) { |
| | | EnterpriseChargingAnalysisQuery rowQuery = new EnterpriseChargingAnalysisQuery(); |
| | | BeanUtils.copyProperties(source, rowQuery); |
| | | rowQuery.setCompanyName(null); |
| | | rowQuery.setExactCompanyName(row.getCompanyName()); |
| | | rowQuery.setSocialCreditCode(row.getSocialCreditCode()); |
| | | return rowQuery; |
| | | } |
| | | |
| | | private BigDecimal calculateAverageChargingInterval(List<LocalDateTime> times) { |
| | | if (times == null || times.size() < 2) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | long totalMinutes = 0L; |
| | | for (int i = 1; i < times.size(); i++) { |
| | | long minutes = ChronoUnit.MINUTES.between(times.get(i - 1), times.get(i)); |
| | | if (minutes > 0) { |
| | | totalMinutes += minutes; |
| | | } |
| | | } |
| | | return BigDecimal.valueOf(totalMinutes) |
| | | .divide(BigDecimal.valueOf(times.size() - 1L), 4, RoundingMode.HALF_UP) |
| | | .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | @Override |
| | | public R<PageInfo<TChargingOrderRefund>> getRefundList(ChargingRefundDto chargingRefundDto) { |
| | | PageInfo<TChargingOrderRefund> pageInfo = new PageInfo<>(chargingRefundDto.getPageCurr(), chargingRefundDto.getPageSize()); |
| | |
| | | @Transactional |
| | | public R endChargeBillingCharge(TransactionRecordMessageVO vo) { |
| | | RLock lock = redissonClient.getLock("endOrder:" + vo.getTransaction_serial_number()); |
| | | boolean locked = false; |
| | | try { |
| | | boolean tryLock = lock.tryLock(60, 60, TimeUnit.SECONDS); |
| | | locked = lock.tryLock(60, 60, TimeUnit.SECONDS); |
| | | if (!locked) { |
| | | return R.fail("订单正在结算,请勿重复操作"); |
| | | } |
| | | //添加redis缓存数据,过期时间,用于防止处理缓慢且硬件重复发送报文导致的重复退款 |
| | | Boolean hasKey = redisService.hasKey("END_ORDER:" + vo.getTransaction_serial_number()); |
| | | if(hasKey){ |
| | | return R.fail("不能重复退款"); |
| | | } |
| | | redisService.setCacheObject("END_ORDER:" + vo.getTransaction_serial_number(), System.currentTimeMillis() + "", 10L, TimeUnit.MINUTES); |
| | | if(tryLock){ |
| | | if(locked){ |
| | | TChargingOrder chargingOrder = this.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getCode, vo.getTransaction_serial_number())); |
| | | if (null == chargingOrder) { |
| | | log.error("订单不存在,订单号:{}", vo.getTransaction_serial_number()); |
| | |
| | | } |
| | | |
| | | //计算优惠券 |
| | | BigDecimal couponDiscount = BigDecimal.ZERO; |
| | | boolean couponAppliedUpfront = money(chargingOrder.getCouponDiscountAmount()).compareTo(BigDecimal.ZERO) > 0; |
| | | BigDecimal couponDiscount = couponAppliedUpfront |
| | | ? money(chargingOrder.getCouponDiscountAmount()) : BigDecimal.ZERO; |
| | | if (!isWhitelistOrder(chargingOrder) && !isBalancePayMode(chargingOrder.getPayMethod()) && null != chargingOrder.getAppCouponId()) { |
| | | //判断实际充电金额是否满足优惠券使用条件,如果不满足则不适用优惠券。 |
| | | TAppCoupon appCoupon = appCouponClient.getAppCouponById(chargingOrder.getAppCouponId()).getData(); |
| | | String couponJson = appCoupon.getCouponJson(); |
| | | TCoupon tCoupon = JSON.parseObject(couponJson, TCoupon.class); |
| | | Integer preferentialMode = tCoupon.getPreferentialMode(); |
| | | if (1 == preferentialMode) { |
| | | if (couponAppliedUpfront) { |
| | | appCoupon.setStatus(2); |
| | | appCouponClient.updateAppCoupon(appCoupon); |
| | | } else if (1 == preferentialMode) { |
| | | //满减 |
| | | if (payAmount.compareTo(tCoupon.getMeetTheConditions()) >= 0) { |
| | | BigDecimal couponDiscountAmount = tCoupon.getDiscountAmount(); |
| | |
| | | chargingOrder.setCouponDiscountAmount(BigDecimal.ZERO); |
| | | } |
| | | } |
| | | if (2 == preferentialMode) { |
| | | if (!couponAppliedUpfront && 2 == preferentialMode) { |
| | | //抵扣 |
| | | if (payAmount.compareTo(tCoupon.getMeetTheConditions()) >= 0) { |
| | | //折扣金额 |
| | |
| | | } |
| | | //优惠券优惠金额 |
| | | couponDiscount = couponDiscount.setScale(4, RoundingMode.HALF_EVEN); |
| | | //退款金额+优惠券 |
| | | refundAmount = refundAmount.add(couponDiscount); |
| | | //实际支付金额-优惠券 |
| | | payAmount = payAmount.subtract(couponDiscount); |
| | | if (serviceCharge_total.compareTo(BigDecimal.ZERO) > 0) { |
| | | serviceCharge_total = serviceCharge_total.subtract(couponDiscount); |
| | | //历史订单在结算时计算优惠;新订单已在预付时少收优惠金额,不能再次增加退款。 |
| | | if (!couponAppliedUpfront) { |
| | | refundAmount = refundAmount.add(couponDiscount); |
| | | } |
| | | //实际支付金额-优惠券 |
| | | payAmount = payAmount.subtract(couponDiscount).max(BigDecimal.ZERO); |
| | | serviceCharge_total = serviceCharge_total.subtract(couponDiscount).max(BigDecimal.ZERO); |
| | | chargingOrder.setCouponDiscountAmount(couponDiscount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingOrder.setWhitelistDiscountAmount(whitelistDiscount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingOrder.setWhitelistElectricRefundAmount(electricDiscount.setScale(2, RoundingMode.HALF_EVEN)); |
| | |
| | | if (2 == chargingOrder.getOrderSource() && 1 == site.getIsOpenSynchronization()) { |
| | | tcecClient.pushChargingGunStatus(chargingGun.getFullNumber(), 2); |
| | | } |
| | | wxSubscribeMessageService.sendChargeEnd(chargingOrder, site, chargingGun); |
| | | |
| | | //添加积分 |
| | | if (1 == chargingOrder.getOrderSource()) { |
| | | TAppUser appUser = appUserClient.getUserById(chargingOrder.getAppUserId()).getData(); |
| | | sendVipChargeFinishMessage(appUser, chargingOrder, site); |
| | | TIntegralRule integralRule = integralRuleClient.getSet().getData(); |
| | | if (null != integralRule) { |
| | | TAppUser appUser = appUserClient.getUserById(chargingOrder.getAppUserId()).getData(); |
| | | Integer num1 = JSON.parseObject(integralRule.getChargeCredit()).getInteger("num1"); |
| | | Integer integral = chargingOrder.getServiceCharge().intValue() * num1; |
| | | if (null != appUser.getVipId()) { |
| | | TVip vip = vipClient.getInfo1(appUser.getVipId()).getData(); |
| | | Integer doubleIntegration = vip.getDoubleIntegration(); |
| | | GetAppUserVipDetail getAppUserVipDetail = new GetAppUserVipDetail(); |
| | | getAppUserVipDetail.setAppUserId(appUser.getId()); |
| | | getAppUserVipDetail.setVipId(appUser.getVipId()); |
| | | TAppUserVipDetail vipDetail = appUserVipDetailClient.getAppUserVipDetail(getAppUserVipDetail).getData(); |
| | | TVip vip = getVipSnapshot(vipDetail, appUser.getVipId()); |
| | | Integer doubleIntegration = vip == null ? null : vip.getDoubleIntegration(); |
| | | //双倍积分 |
| | | if (1 == doubleIntegration) { |
| | | if (Integer.valueOf(1).equals(doubleIntegration)) { |
| | | integral *= 2; |
| | | } |
| | | } |
| | |
| | | this.updateById(chargingOrder); |
| | | triggerChargingOrderShare(chargingOrder); |
| | | } else if (isIcbcOrder(chargingOrder)) { |
| | | refundByIcbcAndFinish(chargingOrder, chargingOrderRefund, thirdRefundAmount, "充电完成退款", false, |
| | | refundAmount, balanceRefundAmount); |
| | | boolean refundSuccess = refundByIcbcAndFinish(chargingOrder, chargingOrderRefund, thirdRefundAmount, |
| | | "充电完成退款", false, refundAmount, balanceRefundAmount); |
| | | if (!refundSuccess) { |
| | | chargingOrderRefund.setRefundStatus(1); |
| | | chargingOrderRefundService.save(chargingOrderRefund); |
| | | chargingOrder.setRefundStatus(1); |
| | | this.updateById(chargingOrder); |
| | | throw new ServiceException("工行退款失败,结算未完成,请检查支付流水后重试退款"); |
| | | } |
| | | } else if (1 == rechargePaymentType) { |
| | | WxPaymentRefundModel model = new WxPaymentRefundModel(); |
| | | model.setOut_trade_no(chargingOrder.getCode()); |
| | |
| | | chargingMessageClient.pushOrderStatus(chargingOrderVo); |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | log.error("充电订单结算失败,订单号:{}", vo == null ? null : vo.getTransaction_serial_number(), e); |
| | | return R.fail("充电订单结算失败:" + firstNotBlank(e.getMessage(), "未知异常")); |
| | | } finally { |
| | | lock.unlock(); |
| | | if (locked && lock.isHeldByCurrentThread()) { |
| | | lock.unlock(); |
| | | } |
| | | } |
| | | return R.ok(); |
| | | } |
| | |
| | | |
| | | |
| | | @Override |
| | | public R<PayOrderInfoDto> getSpecialRefundOrder(PayOrderRefundDto payOrderQueryDto) { |
| | | TChargingOrder order = getSpecialRefundChargingOrder(payOrderQueryDto); |
| | | if (order == null) { |
| | | return R.fail("未查询到支付成功的充电订单"); |
| | | } |
| | | PayOrderInfoDto dto = new PayOrderInfoDto(); |
| | | dto.setOrderId(order.getId().toString()); |
| | | dto.setCode(order.getCode()); |
| | | dto.setTradeNo(order.getRechargeSerialNumber()); |
| | | dto.setPayType(order.getRechargePaymentType()); |
| | | dto.setPayAmount(money(order.getPaymentAmount())); |
| | | dto.setPayTime(order.getPayTime()); |
| | | dto.setRefundAmount(money(order.getRefundAmount())); |
| | | dto.setRechargeAmount(money(order.getRechargeAmount())); |
| | | dto.setFinalAmount(money(order.getPaymentAmount()).subtract(money(order.getRefundAmount())).setScale(2, RoundingMode.HALF_UP)); |
| | | dto.setPayMethod(order.getPayMethod()); |
| | | dto.setBalancePayAmount(order.getBalancePayAmount()); |
| | | dto.setThirdPayAmount(order.getThirdPayAmount()); |
| | | dto.setBalanceRefundAmount(order.getBalanceRefundAmount()); |
| | | dto.setThirdRefundAmount(order.getThirdRefundAmount()); |
| | | return R.ok(dto); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional |
| | | public R specialRefund(PayOrderRefundDto payOrderQueryDto) { |
| | | R passwordValid = validateOperationPassword(payOrderQueryDto); |
| | | if (passwordValid.getCode() != 200) { |
| | | return passwordValid; |
| | | } |
| | | if (payOrderQueryDto.getRefundAmount() == null || payOrderQueryDto.getRefundAmount().compareTo(BigDecimal.ZERO) <= 0) { |
| | | return R.fail("请输入正确的退款金额"); |
| | | } |
| | | TChargingOrder order = getSpecialRefundChargingOrder(payOrderQueryDto); |
| | | if (order == null) { |
| | | return R.fail("未查询到支付成功的充电订单"); |
| | | } |
| | | RLock lock = redissonClient.getLock("SPECIAL_REFUND_ORDER_LOCK:" + order.getId()); |
| | | if (!lock.tryLock()) { |
| | | return R.fail("该订单正在退款处理中,请勿重复提交"); |
| | | } |
| | | try { |
| | | order = this.getById(order.getId()); |
| | | if (order == null) { |
| | | return R.fail("充电订单不存在"); |
| | | } |
| | | BigDecimal refundAmount = money(payOrderQueryDto.getRefundAmount()); |
| | | BigDecimal refundableAmount = money(order.getPaymentAmount()).subtract(money(order.getRefundAmount())).setScale(2, RoundingMode.HALF_UP); |
| | | if (refundAmount.compareTo(refundableAmount) > 0) { |
| | | return R.fail("退款金额不能超过剩余可退金额"); |
| | | } |
| | | BigDecimal refundedThirdAmount = money(order.getThirdRefundAmount()); |
| | | BigDecimal refundableThirdAmount = money(order.getThirdPayAmount()).subtract(refundedThirdAmount); |
| | | if (refundableThirdAmount.compareTo(BigDecimal.ZERO) < 0) { |
| | | refundableThirdAmount = BigDecimal.ZERO; |
| | | } |
| | | BigDecimal thirdRefundAmount = isBalancePayMode(order.getPayMethod()) |
| | | ? refundAmount.min(refundableThirdAmount).setScale(2, RoundingMode.HALF_UP) |
| | | : refundAmount; |
| | | BigDecimal balanceRefundAmount = refundAmount.subtract(thirdRefundAmount).setScale(2, RoundingMode.HALF_UP); |
| | | if (balanceRefundAmount.compareTo(BigDecimal.ZERO) < 0) { |
| | | balanceRefundAmount = BigDecimal.ZERO; |
| | | } |
| | | TChargingOrderRefund refund = buildSpecialChargingRefund(order, payOrderQueryDto, refundAmount, balanceRefundAmount, thirdRefundAmount); |
| | | if (thirdRefundAmount.compareTo(BigDecimal.ZERO) == 0) { |
| | | refund.setRefundSerialNumber("BALANCE"); |
| | | finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount); |
| | | return R.ok(); |
| | | } |
| | | Integer payType = order.getRechargePaymentType(); |
| | | boolean success = false; |
| | | if (Integer.valueOf(1).equals(payType)) { |
| | | R shareRefund = refundIcbcShareBeforeMainRefund(order, refund, thirdRefundAmount); |
| | | if (shareRefund.getCode() != 200) { |
| | | return shareRefund; |
| | | } |
| | | success = refundByIcbcAndFinish(order, refund, thirdRefundAmount, SPECIAL_REFUND_TITLE, true, refundAmount, balanceRefundAmount); |
| | | } else if (Integer.valueOf(2).equals(payType)) { |
| | | RefundReq dto = new RefundReq(); |
| | | dto.setOutTradeNo(order.getCode()); |
| | | dto.setOutRequestNo(refund.getRefundCode()); |
| | | dto.setRefundAmount(thirdRefundAmount.toString()); |
| | | dto.setRefundReason(SPECIAL_REFUND_TITLE); |
| | | RefundResp resp = aliPaymentClient.refund(dto).getData(); |
| | | if (resp != null) { |
| | | refund.setRefundSerialNumber(resp.getTradeNo()); |
| | | refund.setRefundTime(LocalDateTime.now()); |
| | | finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount); |
| | | success = true; |
| | | } |
| | | } else if (Integer.valueOf(3).equals(payType)) { |
| | | RefundReq dto = new RefundReq(); |
| | | dto.setOutTradeNo(order.getRechargeSerialNumber()); |
| | | dto.setOutRequestNo(refund.getRefundCode()); |
| | | dto.setRefundAmount(thirdRefundAmount.toString()); |
| | | dto.setRefundReason(SPECIAL_REFUND_TITLE); |
| | | RefundResp resp = h5AliPaymentClient.refund(dto).getData(); |
| | | if (resp != null && "10000".equals(resp.getCode())) { |
| | | refund.setRefundSerialNumber(resp.getTradeNo()); |
| | | refund.setRefundTime(LocalDateTime.now()); |
| | | finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount); |
| | | success = true; |
| | | } |
| | | } |
| | | return success ? R.ok() : R.fail("退款失败,请检查支付渠道或稍后重试"); |
| | | } finally { |
| | | if (lock.isHeldByCurrentThread()) { |
| | | lock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private R refundIcbcShareBeforeMainRefund(TChargingOrder order, TChargingOrderRefund refund, BigDecimal thirdRefundAmount) { |
| | | if (order == null || refund == null || thirdRefundAmount == null || thirdRefundAmount.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return R.ok(); |
| | | } |
| | | if (!"ICBC".equals(order.getSourceName())) { |
| | | return R.ok(); |
| | | } |
| | | List<TChargingOrderShareRecord> shareRecords = chargingOrderShareRecordMapper.selectList( |
| | | new LambdaQueryWrapper<TChargingOrderShareRecord>() |
| | | .eq(TChargingOrderShareRecord::getChargingOrderId, order.getId()) |
| | | .eq(TChargingOrderShareRecord::getPayChannel, "ICBC") |
| | | .eq(TChargingOrderShareRecord::getNeedOnlineShare, true) |
| | | .eq(TChargingOrderShareRecord::getOnlineShareStatus, "SUCCESS") |
| | | .eq(TChargingOrderShareRecord::getDelFlag, false) |
| | | .orderByAsc(TChargingOrderShareRecord::getId)); |
| | | if (shareRecords == null || shareRecords.isEmpty()) { |
| | | return R.ok(); |
| | | } |
| | | BigDecimal thirdPayAmount = getRefundableThirdPayAmount(order); |
| | | if (thirdPayAmount.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return R.fail("订单已分账但第三方支付金额异常,禁止退款"); |
| | | } |
| | | BigDecimal cumulativeThirdRefund = money(order.getThirdRefundAmount()).add(thirdRefundAmount).min(thirdPayAmount).setScale(2, RoundingMode.HALF_UP); |
| | | for (TChargingOrderShareRecord shareRecord : shareRecords) { |
| | | BigDecimal shareAmount = money(shareRecord.getShareAmount()); |
| | | if (shareAmount.compareTo(BigDecimal.ZERO) <= 0) { |
| | | continue; |
| | | } |
| | | BigDecimal targetRefundAmount = shareAmount.multiply(cumulativeThirdRefund).divide(thirdPayAmount, 2, RoundingMode.HALF_UP).min(shareAmount).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal refundedShareAmount; |
| | | try { |
| | | refundedShareAmount = queryIcbcShareRefundAmount(order, shareRecord); |
| | | } catch (ServiceException e) { |
| | | return R.fail(e.getMessage()); |
| | | } |
| | | BigDecimal shareRefundAmount = targetRefundAmount.subtract(refundedShareAmount).setScale(2, RoundingMode.HALF_UP); |
| | | if (shareRefundAmount.compareTo(BigDecimal.ZERO) <= 0) { |
| | | continue; |
| | | } |
| | | R result = refundIcbcShareRecord(order, shareRecord, refund, shareRefundAmount); |
| | | if (result.getCode() != 200) { |
| | | return result; |
| | | } |
| | | } |
| | | return R.ok(); |
| | | } |
| | | |
| | | private BigDecimal queryIcbcShareRefundAmount(TChargingOrder order, TChargingOrderShareRecord shareRecord) { |
| | | try { |
| | | IcbcSubOrderQueryReq queryReq = IcbcAdminFundOperationSupport.buildSubOrderQueryRequest(order, shareRecord); |
| | | R<IcbcSubOrderQueryResp> queryResult = icbcPaymentClient.querySubOrder(queryReq); |
| | | IcbcSubOrderQueryResp queryResp = queryResult == null ? null : queryResult.getData(); |
| | | if (queryResult == null || queryResult.getCode() != 200 || queryResp == null || !"SUCCESS".equals(queryResp.getStatus())) { |
| | | throw new ServiceException(queryResult == null ? "工行子订单查询无响应" : firstNotBlank(queryResult.getMsg(), "工行子订单查询失败")); |
| | | } |
| | | return parseIcbcAmount(queryResp.getOrderRefundAmnt()); |
| | | } catch (IllegalArgumentException e) { |
| | | throw new ServiceException(e.getMessage()); |
| | | } catch (ServiceException e) { |
| | | throw e; |
| | | } catch (Exception e) { |
| | | throw new ServiceException("工行子订单查询异常:" + firstNotBlank(e.getMessage(), "未知异常")); |
| | | } |
| | | } |
| | | |
| | | private R refundIcbcShareRecord(TChargingOrder order, TChargingOrderShareRecord shareRecord, TChargingOrderRefund refund, BigDecimal shareRefundAmount) { |
| | | String suffix = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")); |
| | | String retSubOrderId = ("SPSUB" + refund.getRefundCode() + shareRecord.getId()).substring(0, Math.min(50, ("SPSUB" + refund.getRefundCode() + shareRecord.getId()).length())); |
| | | String outtrxSerialNo = ("SPSUBREF" + suffix + shareRecord.getId()).substring(0, Math.min(50, ("SPSUBREF" + suffix + shareRecord.getId()).length())); |
| | | try { |
| | | IcbcSubOrderRefundReq refundReq = IcbcAdminFundOperationSupport.buildSubOrderRefundRequest( |
| | | order, shareRecord, shareRefundAmount, retSubOrderId, outtrxSerialNo); |
| | | refundReq.setMerSpInfor("特殊退款分账退回:" + order.getCode()); |
| | | R<IcbcSubOrderRefundResp> refundResult = icbcPaymentClient.refundSubOrder(refundReq); |
| | | IcbcSubOrderRefundResp refundResp = refundResult == null ? null : refundResult.getData(); |
| | | if (refundResult == null || refundResult.getCode() != 200 || refundResp == null || !"SUCCESS".equals(refundResp.getStatus())) { |
| | | String message = refundResult == null ? "工行分账退款无响应" : firstNotBlank(refundResult.getMsg(), refundResp == null ? null : refundResp.getRawResponse(), "工行分账退款失败"); |
| | | log.error("特殊退款分账退回失败,订单号:{},退款单号:{},分账记录:{},金额:{},原因:{}", |
| | | order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount, message); |
| | | return R.fail("分账退回失败:" + message); |
| | | } |
| | | log.info("特殊退款分账退回成功,订单号:{},退款单号:{},分账记录:{},金额:{}", |
| | | order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount); |
| | | return R.ok(); |
| | | } catch (IllegalArgumentException | ServiceException e) { |
| | | log.error("特殊退款分账退回失败,订单号:{},退款单号:{},分账记录:{},金额:{}", |
| | | order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount, e); |
| | | return R.fail("分账退回失败:" + e.getMessage()); |
| | | } catch (Exception e) { |
| | | log.error("特殊退款分账退回异常,订单号:{},退款单号:{},分账记录:{},金额:{}", |
| | | order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount, e); |
| | | return R.fail("分账退回异常:" + firstNotBlank(e.getMessage(), "未知异常")); |
| | | } |
| | | } |
| | | |
| | | private BigDecimal parseIcbcAmount(String amount) { |
| | | if (!StringUtils.hasLength(amount)) { |
| | | return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | String value = amount.trim(); |
| | | try { |
| | | if (value.contains(".")) { |
| | | return money(new BigDecimal(value)); |
| | | } |
| | | return new BigDecimal(value).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP); |
| | | } catch (NumberFormatException e) { |
| | | throw new ServiceException("工行金额格式异常:" + amount); |
| | | } |
| | | } |
| | | |
| | | private R validateOperationPassword(PayOrderRefundDto payOrderQueryDto) { |
| | | if (payOrderQueryDto == null || StringUtils.isEmpty(payOrderQueryDto.getOperationPassword())) { |
| | | return R.fail("请输入操作密码"); |
| | | } |
| | | SysUser sysUser = sysUserClient.getSysUser(tokenService.getLoginUser().getUserid()).getData(); |
| | | if (sysUser == null || StringUtils.isEmpty(sysUser.getOperationPassword())) { |
| | | return R.fail("当前账号未配置操作密码"); |
| | | } |
| | | if (!SecurityUtils.matchesPassword(payOrderQueryDto.getOperationPassword(), sysUser.getOperationPassword())) { |
| | | return R.fail("操作密码错误"); |
| | | } |
| | | return R.ok(); |
| | | } |
| | | |
| | | private TChargingOrder getSpecialRefundChargingOrder(PayOrderRefundDto payOrderQueryDto) { |
| | | if (payOrderQueryDto == null) { |
| | | return null; |
| | | } |
| | | LambdaQueryWrapper<TChargingOrder> wrapper = new LambdaQueryWrapper<TChargingOrder>() |
| | | .eq(TChargingOrder::getRechargePaymentStatus, 2) |
| | | .eq(TChargingOrder::getDelFlag, false); |
| | | if (payOrderQueryDto.getOrderId() != null) { |
| | | wrapper.eq(TChargingOrder::getId, payOrderQueryDto.getOrderId()); |
| | | } else if (StringUtils.hasLength(payOrderQueryDto.getCode())) { |
| | | wrapper.eq(TChargingOrder::getCode, payOrderQueryDto.getCode()); |
| | | } else if (StringUtils.hasLength(payOrderQueryDto.getSerialNumber())) { |
| | | wrapper.eq(TChargingOrder::getRechargeSerialNumber, payOrderQueryDto.getSerialNumber()); |
| | | } else { |
| | | return null; |
| | | } |
| | | return this.getOne(wrapper.last("limit 1")); |
| | | } |
| | | |
| | | private TChargingOrderRefund buildSpecialChargingRefund(TChargingOrder order, PayOrderRefundDto dto, BigDecimal refundAmount, |
| | | BigDecimal balanceRefundAmount, BigDecimal thirdRefundAmount) { |
| | | TChargingOrderRefund refund = new TChargingOrderRefund(); |
| | | refund.setChargingOrderId(order.getId()); |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); |
| | | refund.setRefundCode("TSF" + sdf.format(new Date()) + (Double.valueOf(Math.random() * 1000).intValue())); |
| | | refund.setRefundAmount(refundAmount); |
| | | refund.setBalanceRefundAmount(balanceRefundAmount); |
| | | refund.setThirdRefundAmount(thirdRefundAmount); |
| | | refund.setRefundStatus(1); |
| | | refund.setPayType(order.getRechargePaymentType()); |
| | | refund.setRefundTime(LocalDateTime.now()); |
| | | refund.setCode(order.getCode()); |
| | | refund.setRefundTitle(SPECIAL_REFUND_TITLE); |
| | | refund.setRefundContent(SPECIAL_REFUND_TITLE); |
| | | refund.setRefundReason(StringUtils.hasLength(dto.getReason()) ? dto.getReason() : SPECIAL_REFUND_TITLE); |
| | | refund.setRefundRemark(dto.getRemark()); |
| | | refund.setRefundTotalAmount(money(order.getRefundAmount()).add(refundAmount)); |
| | | refund.setPayAmount(money(order.getPaymentAmount())); |
| | | return refund; |
| | | } |
| | | |
| | | @Override |
| | | public R payRefund(PayOrderRefundDto payOrderQueryDto) { |
| | | R passwordValid = validateOperationPassword(payOrderQueryDto); |
| | | if (passwordValid.getCode() != 200) { |
| | | return passwordValid; |
| | | } |
| | | if (payOrderQueryDto.getType() == 1) { |
| | | TChargingOrder tChargingOrder = this.baseMapper.selectById(payOrderQueryDto.getOrderId()); |
| | | if (tChargingOrder.getPaymentAmount().compareTo(payOrderQueryDto.getRefundAmount()) == -1) { |
| | |
| | | preChargeCheck.setInsulationTesting(false); |
| | | preChargeCheck.setSecureConnectionDetection(false); |
| | | preChargeCheck.setStartupSuccess(1); |
| | | preChargeCheck.setStartCommandSent(false); |
| | | preChargeCheck.setAddTime(chargingOrder.getReservationTime() != null ? chargingOrder.getReservationTime() : LocalDateTime.now()); |
| | | String key = "AQJC_" + chargingOrder.getChargingGunId(); |
| | | redisService.setCacheObject(key, preChargeCheck, 24L, TimeUnit.HOURS); |
| | | |
| | |
| | | } |
| | | |
| | | //计算优惠券 |
| | | BigDecimal couponDiscount = BigDecimal.ZERO; |
| | | boolean couponAppliedUpfront = money(chargingOrder.getCouponDiscountAmount()).compareTo(BigDecimal.ZERO) > 0; |
| | | BigDecimal couponDiscount = couponAppliedUpfront |
| | | ? money(chargingOrder.getCouponDiscountAmount()) : BigDecimal.ZERO; |
| | | if (null != chargingOrder.getAppCouponId()) { |
| | | //判断实际充电金额是否满足优惠券使用条件,如果不满足则不适用优惠券。 |
| | | TAppCoupon appCoupon = appCouponClient.getAppCouponById(chargingOrder.getAppCouponId()).getData(); |
| | | String couponJson = appCoupon.getCouponJson(); |
| | | TCoupon tCoupon = JSON.parseObject(couponJson, TCoupon.class); |
| | | Integer preferentialMode = tCoupon.getPreferentialMode(); |
| | | if (1 == preferentialMode) { |
| | | if (couponAppliedUpfront) { |
| | | appCoupon.setStatus(2); |
| | | appCouponClient.updateAppCoupon(appCoupon); |
| | | } else if (1 == preferentialMode) { |
| | | //满减 |
| | | if (payAmount.compareTo(tCoupon.getMeetTheConditions()) >= 0) { |
| | | BigDecimal couponDiscountAmount = tCoupon.getDiscountAmount(); |
| | |
| | | chargingOrder.setCouponDiscountAmount(BigDecimal.ZERO); |
| | | } |
| | | } |
| | | if (2 == preferentialMode) { |
| | | if (!couponAppliedUpfront && 2 == preferentialMode) { |
| | | //抵扣 |
| | | if (payAmount.compareTo(tCoupon.getMeetTheConditions()) >= 0) { |
| | | //折扣金额 |
| | |
| | | } |
| | | //优惠券优惠金额 |
| | | couponDiscount = couponDiscount.setScale(4, RoundingMode.HALF_EVEN); |
| | | //退款金额+优惠券 |
| | | refundAmount = refundAmount.add(couponDiscount); |
| | | //实际支付金额-优惠券 |
| | | payAmount = payAmount.subtract(couponDiscount); |
| | | if (serviceCharge_total.compareTo(BigDecimal.ZERO) > 0) { |
| | | serviceCharge_total = serviceCharge_total.subtract(couponDiscount); |
| | | if (!couponAppliedUpfront) { |
| | | refundAmount = refundAmount.add(couponDiscount); |
| | | } |
| | | //实际支付金额-优惠券 |
| | | payAmount = payAmount.subtract(couponDiscount).max(BigDecimal.ZERO); |
| | | serviceCharge_total = serviceCharge_total.subtract(couponDiscount).max(BigDecimal.ZERO); |
| | | chargingOrder.setCouponDiscountAmount(couponDiscount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingOrder.setServiceCharge(serviceCharge_total.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingOrder.setPaymentAmount(payAmount.setScale(2, RoundingMode.HALF_EVEN)); |
| | |
| | | if (resp == null || !StringUtils.hasLength(resp.getStatus())) { |
| | | return false; |
| | | } |
| | | return "SUCCESS".equals(resp.getStatus()); |
| | | if (!"SUCCESS".equals(resp.getStatus()) || !StringUtils.hasLength(resp.getPayData())) { |
| | | return false; |
| | | } |
| | | try { |
| | | JSONObject payData = JSON.parseObject(resp.getPayData()); |
| | | return StringUtils.hasLength(payData.getString("wxDataPackage")) || StringUtils.hasLength(payData.getString("package")); |
| | | } catch (Exception e) { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | private String buildIcbcPaymentFailReason(IcbcPaymentResp resp) { |
| | | if (resp == null) { |
| | | return "工行支付服务未返回支付数据"; |
| | | } |
| | | String reason = firstNotBlank(resp.getRawResponse(), resp.getStatus()); |
| | | if (!StringUtils.hasLength(reason)) { |
| | | return "工行支付服务返回数据不完整"; |
| | | } |
| | | if (reason.length() > 500) { |
| | | return reason.substring(0, 500); |
| | | } |
| | | return reason; |
| | | } |
| | | |
| | | private void triggerChargingOrderShare(TChargingOrder chargingOrder, Site site) { |
| | | if (!isNeedChargingOrderShare(chargingOrder)) { |
| | | return; |
| | | } |
| | | if (TransactionSynchronizationManager.isActualTransactionActive() |
| | | && TransactionSynchronizationManager.isSynchronizationActive()) { |
| | | Long chargingOrderId = chargingOrder.getId(); |
| | | Integer siteId = chargingOrder.getSiteId(); |
| | | String resourceKey = SHARE_AFTER_COMMIT_RESOURCE_PREFIX + chargingOrderId; |
| | | if (TransactionSynchronizationManager.hasResource(resourceKey)) { |
| | | return; |
| | | } |
| | | TransactionSynchronizationManager.bindResource(resourceKey, Boolean.TRUE); |
| | | try { |
| | | TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { |
| | | @Override |
| | | public void afterCommit() { |
| | | executeChargingOrderShareAfterCommit(chargingOrderId, siteId); |
| | | } |
| | | |
| | | @Override |
| | | public void afterCompletion(int status) { |
| | | if (TransactionSynchronizationManager.hasResource(resourceKey)) { |
| | | TransactionSynchronizationManager.unbindResource(resourceKey); |
| | | } |
| | | } |
| | | }); |
| | | } catch (RuntimeException e) { |
| | | TransactionSynchronizationManager.unbindResourceIfPossible(resourceKey); |
| | | throw e; |
| | | } |
| | | log.info("充电订单分账已登记为事务提交后执行,订单号:{}", chargingOrder.getCode()); |
| | | return; |
| | | } |
| | | executeChargingOrderShare(chargingOrder, site); |
| | | } |
| | | |
| | | private void executeChargingOrderShareAfterCommit(Long chargingOrderId, Integer siteId) { |
| | | try { |
| | | TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); |
| | | transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); |
| | | transactionTemplate.execute(status -> { |
| | | TChargingOrder latestOrder = chargingOrderId == null ? null : this.getById(chargingOrderId); |
| | | Site latestSite = loadChargingOrderSite(siteId); |
| | | executeChargingOrderShare(latestOrder, latestSite); |
| | | return null; |
| | | }); |
| | | } catch (Exception e) { |
| | | log.error("事务提交后执行充电订单分账失败,订单ID:{}", chargingOrderId, e); |
| | | } |
| | | } |
| | | |
| | | private void executeChargingOrderShare(TChargingOrder chargingOrder, Site site) { |
| | | try { |
| | | if (!isNeedChargingOrderShare(chargingOrder)) { |
| | | return; |
| | |
| | | if (chargingOrder == null || chargingOrder.getSiteId() == null) { |
| | | return; |
| | | } |
| | | R<List<Site>> siteResult = siteClient.getSiteByIds(Arrays.asList(chargingOrder.getSiteId())); |
| | | List<Site> sites = siteResult == null ? null : siteResult.getData(); |
| | | Site site = sites == null || sites.isEmpty() ? null : sites.get(0); |
| | | if (TransactionSynchronizationManager.isActualTransactionActive() |
| | | && TransactionSynchronizationManager.isSynchronizationActive()) { |
| | | triggerChargingOrderShare(chargingOrder, null); |
| | | return; |
| | | } |
| | | Site site = loadChargingOrderSite(chargingOrder.getSiteId()); |
| | | triggerChargingOrderShare(chargingOrder, site); |
| | | } catch (Exception e) { |
| | | log.error("充电订单分账站点加载失败,订单号:{}", chargingOrder == null ? null : chargingOrder.getCode(), e); |
| | | } |
| | | } |
| | | |
| | | private Site loadChargingOrderSite(Integer siteId) { |
| | | if (siteId == null) { |
| | | return null; |
| | | } |
| | | R<List<Site>> siteResult = siteClient.getSiteByIds(Collections.singletonList(siteId)); |
| | | List<Site> sites = siteResult == null ? null : siteResult.getData(); |
| | | return sites == null || sites.isEmpty() ? null : sites.get(0); |
| | | } |
| | | |
| | | private boolean isNeedChargingOrderShare(TChargingOrder chargingOrder) { |
| | |
| | | record.setElectricityShareAmount(electricityShareAmount); |
| | | record.setServiceShareAmount(serviceShareAmount); |
| | | record.setShareAmount(electricityShareAmount.add(serviceShareAmount).setScale(2, RoundingMode.HALF_UP)); |
| | | record.setNeedOnlineShare("ICBC".equals(payChannel) && record.getShareAmount().compareTo(BigDecimal.ZERO) > 0); |
| | | record.setOnlineShareStatus(Boolean.TRUE.equals(record.getNeedOnlineShare()) ? "INIT" : "RECORDED"); |
| | | if ("ICBC".equals(payChannel)) { |
| | | record.setSeqNo(chargingOrder.getCode()); |
| | | record.setBusiType("1"); |
| | | record.setOriTrxDate(chargingOrder.getPayTime() == null ? LocalDate.now() : chargingOrder.getPayTime().toLocalDate()); |
| | | record.setSubOrderId("ICBCS" + chargingOrder.getCode()); |
| | | record.setSubOrderId(chargingOrder.getCode()); |
| | | } |
| | | if ("ICBC".equals(payChannel) && partner != null) { |
| | | record.setSubMerId(partner.getIcbcSubMerId()); |
| | | record.setSubMerPrtclNo(partner.getIcbcSubMerPrtclNo()); |
| | | record.setMerAcct(partner.getIcbcMerAcct()); |
| | | } |
| | | boolean needOnlineShare = shouldProcessIcbcOnlineShare(payChannel, record.getShareAmount(), partner); |
| | | record.setNeedOnlineShare(needOnlineShare); |
| | | record.setOnlineShareStatus(needOnlineShare ? "INIT" : "RECORDED"); |
| | | if ("ICBC".equals(payChannel) && record.getShareAmount().compareTo(BigDecimal.ZERO) > 0 && !needOnlineShare) { |
| | | record.setFailReason("未配置完整工行二级商户参数,仅记录本地分账"); |
| | | } |
| | | record.setRetryCount(0); |
| | | record.setDelFlag(false); |
| | |
| | | Map<String, Object> row = rows.get(0); |
| | | BigDecimal principalAmount = getSummaryDecimal(row, "balanceIncome").max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal giftAmount = getSummaryDecimal(row, "balanceGiftIncome").max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP); |
| | | return new BalanceShareAmount(principalAmount, giftAmount); |
| | | BigDecimal[] normalized = normalizeBalanceShareAmounts(fallbackBalancePay, principalAmount, giftAmount); |
| | | return new BalanceShareAmount(normalized[0], normalized[1]); |
| | | } |
| | | |
| | | /** |
| | | * 余额退款流水可能在跨服务事务提交前暂时不可见,按订单净余额约束本金和赠送金额总和。 |
| | | */ |
| | | static BigDecimal[] normalizeBalanceShareAmounts(BigDecimal netBalanceAmount, |
| | | BigDecimal principalAmount, |
| | | BigDecimal giftAmount) { |
| | | BigDecimal netAmount = netBalanceAmount == null ? BigDecimal.ZERO : netBalanceAmount.max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal principal = principalAmount == null ? BigDecimal.ZERO : principalAmount.max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal gift = giftAmount == null ? BigDecimal.ZERO : giftAmount.max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal recordedTotal = principal.add(gift).setScale(2, RoundingMode.HALF_UP); |
| | | if (netAmount.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return new BigDecimal[]{BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP), BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)}; |
| | | } |
| | | if (recordedTotal.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return new BigDecimal[]{netAmount, BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)}; |
| | | } |
| | | BigDecimal normalizedPrincipal = netAmount.multiply(principal) |
| | | .divide(recordedTotal, 2, RoundingMode.HALF_UP); |
| | | BigDecimal normalizedGift = netAmount.subtract(normalizedPrincipal).setScale(2, RoundingMode.HALF_UP); |
| | | return new BigDecimal[]{normalizedPrincipal, normalizedGift}; |
| | | } |
| | | |
| | | private BigDecimal getChargingOrderShareElectricityAmount(TChargingOrder chargingOrder) { |
| | |
| | | return amount.multiply(ratio).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | static boolean shouldProcessIcbcOnlineShare(String payChannel, BigDecimal shareAmount, Partner partner) { |
| | | return "ICBC".equals(payChannel) |
| | | && shareAmount != null |
| | | && shareAmount.compareTo(BigDecimal.ZERO) > 0 |
| | | && partner != null |
| | | && StringUtils.hasLength(partner.getIcbcSubMerId()) |
| | | && StringUtils.hasLength(partner.getIcbcSubMerPrtclNo()); |
| | | } |
| | | |
| | | private IcbcSubOrderSplitReq buildIcbcSubOrderSplitReq(TChargingOrderShareRecord record) { |
| | | IcbcSubOrderSplitReq req = new IcbcSubOrderSplitReq(); |
| | | req.setChargingOrderCode(record.getChargingOrderCode()); |
| | |
| | | req.setSubMerId(record.getSubMerId()); |
| | | req.setSubMerPrtclNo(record.getSubMerPrtclNo()); |
| | | req.setClassifyAmt(record.getShareAmount()); |
| | | req.setMerAcct(record.getMerAcct()); |
| | | req.setMerSpInfor("充电订单分账:" + record.getChargingOrderCode()); |
| | | return req; |
| | | } |
| | |
| | | data.put("icbcPartnerId", partner.getIcbcPartnerId()); |
| | | data.put("icbcSubMerId", partner.getIcbcSubMerId()); |
| | | data.put("icbcSubMerPrtclNo", partner.getIcbcSubMerPrtclNo()); |
| | | data.put("icbcMerAcct", partner.getIcbcMerAcct()); |
| | | if (partner.getElectricityShareRatio() != null) { |
| | | data.put("electricityShareRatio", partner.getElectricityShareRatio()); |
| | | } |