| | |
| | | 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.order.api.vo.PlatformStopChargingReplyVO; |
| | | import com.ruoyi.order.dto.*; |
| | | import com.ruoyi.order.mapper.TChargingOrderMapper; |
| | | import com.ruoyi.order.mapper.TChargingOrderShareRecordMapper; |
| | | import com.ruoyi.order.mapper.TSettlementConfirmMapper; |
| | | import com.ruoyi.order.service.TChargingOrderAccountingStrategyService; |
| | | import com.ruoyi.order.service.TChargingOrderRefundService; |
| | |
| | | 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.other.api.vo.ExchangeCodeConsumeResultVO; |
| | | import com.ruoyi.payment.api.feignClient.AliPaymentClient; |
| | | import com.ruoyi.payment.api.feignClient.H5AliPaymentClient; |
| | | import com.ruoyi.payment.api.feignClient.IcbcPaymentClient; |
| | | import com.ruoyi.payment.api.feignClient.WxPaymentClient; |
| | | import com.ruoyi.payment.api.model.*; |
| | | 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; |
| | |
| | | private AliPaymentClient aliPaymentClient; |
| | | @Resource |
| | | private H5AliPaymentClient h5AliPaymentClient; |
| | | @Resource |
| | | private IcbcPaymentClient icbcPaymentClient; |
| | | @Resource |
| | | private TChargingOrderShareRecordMapper chargingOrderShareRecordMapper; |
| | | @Resource |
| | | private ChargingOrderSettlementExecutor chargingOrderSettlementExecutor; |
| | | |
| | | @Resource |
| | | private AppCouponClient appCouponClient; |
| | |
| | | changeUserBalance(chargingOrder.getAppUserId(), balanceRefundAmount); |
| | | recordBalanceRefund(chargingOrder, balanceRefundAmount, refund.getRefundCode()); |
| | | } |
| | | triggerChargingOrderShare(chargingOrder); |
| | | } |
| | | |
| | | private boolean refundByIcbcAndFinish(TChargingOrder chargingOrder, TChargingOrderRefund refund, BigDecimal thirdRefundAmount, |
| | | String reason, boolean finishManual, BigDecimal refundAmount, BigDecimal balanceRefundAmount) { |
| | | IcbcRefundReq req = new IcbcRefundReq(); |
| | | req.setOutTradeNo(chargingOrder.getCode()); |
| | | req.setOrderId(chargingOrder.getRechargeSerialNumber()); |
| | | req.setOutRefundNo(refund.getRefundCode()); |
| | | req.setRefundAmount(thirdRefundAmount); |
| | | req.setTotalAmount(getRefundableThirdPayAmount(chargingOrder)); |
| | | req.setReason(reason); |
| | | R<IcbcRefundResp> result = icbcPaymentClient.refund(req); |
| | | IcbcRefundResp resp = result == null ? null : result.getData(); |
| | | if (result == null || result.getCode() != 200 || resp == null || !"SUCCESS".equals(resp.getStatus())) { |
| | | log.error("工行退款失败,订单号:{},退款单号:{},返回:{}", chargingOrder.getCode(), refund.getRefundCode(), result == null ? null : result.getMsg()); |
| | | return false; |
| | | } |
| | | if (finishManual) { |
| | | refund.setRefundSerialNumber(firstNotBlank(resp.getRefundId(), resp.getOutRefundNo())); |
| | | refund.setRefundTime(LocalDateTime.now()); |
| | | finishManualChargingRefund(chargingOrder, refund, refundAmount, balanceRefundAmount, thirdRefundAmount); |
| | | } else { |
| | | chargingOrderRefundService.save(refund); |
| | | chargingOrderStartupFailureWxRefund(refund.getRefundCode(), firstNotBlank(resp.getRefundId(), resp.getOutRefundNo()), "SUCCESS", resp.getRefundTime()); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | private boolean isIcbcOrder(TChargingOrder chargingOrder) { |
| | | return chargingOrder != null && "ICBC".equals(chargingOrder.getSourceName()); |
| | | } |
| | | |
| | | /** |
| | | * 判断订单是否为白名单订单 |
| | |
| | | .eq(TChargingOrder::getRechargePaymentStatus, 1).eq(TChargingOrder::getDelFlag, 0)); |
| | | for (TChargingOrder tChargingOrder : list) { |
| | | Integer rechargePaymentType = tChargingOrder.getRechargePaymentType(); |
| | | if (1 == rechargePaymentType) { |
| | | 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 (2 == rechargePaymentType) { |
| | | if (!"ICBC".equals(tChargingOrder.getSourceName()) && 2 == rechargePaymentType) { |
| | | AliQueryOrder data = aliPaymentClient.query(tChargingOrder.getCode()).getData(); |
| | | if (null != data) { |
| | | //支付失败,删除无效的订单 |
| | |
| | | } |
| | | } |
| | | } |
| | | if (3 == rechargePaymentType) { |
| | | if (!"ICBC".equals(tChargingOrder.getSourceName()) && 3 == rechargePaymentType) { |
| | | AliQueryOrder data = h5AliPaymentClient.query(tChargingOrder.getCode()).getData(); |
| | | if (null != data) { |
| | | //支付失败,删除无效的订单 |
| | |
| | | .eq(TChargingOrder::getAppUserCarId, userId).eq(TChargingOrder::getRechargePaymentStatus, 1).eq(TChargingOrder::getDelFlag, 0)); |
| | | if (null != one) { |
| | | //查询三方支付数据,支付中直接结束三方订单 |
| | | if (1 == one.getRechargePaymentType()) { |
| | | if ("ICBC".equals(one.getSourceName())) { |
| | | IcbcPaymentResp data = icbcPaymentClient.query(one.getCode()).getData(); |
| | | if (null != data && isIcbcRemovableStatus(data.getStatus(), one)) { |
| | | this.removeById(one.getId()); |
| | | } else { |
| | | return AjaxResult.error("存在支付中订单,请稍后再试"); |
| | | } |
| | | } |
| | | if (!"ICBC".equals(one.getSourceName()) && 1 == one.getRechargePaymentType()) { |
| | | NotifyV3PayDecodeRespBody data = wxPaymentClient.queryOrderInfo(one.getCode()).getData(); |
| | | if (null != data) { |
| | | String trade_state = data.getTrade_state(); |
| | |
| | | } |
| | | } |
| | | } |
| | | if (2 == one.getRechargePaymentType()) { |
| | | if (!"ICBC".equals(one.getSourceName()) && 2 == one.getRechargePaymentType()) { |
| | | AliQueryOrder data = aliPaymentClient.query(one.getCode()).getData(); |
| | | if (null != data) { |
| | | String trade_state = data.getTradeStatus(); |
| | |
| | | } |
| | | } |
| | | } |
| | | if (3 == one.getRechargePaymentType()) { |
| | | if (!"ICBC".equals(one.getSourceName()) && 3 == one.getRechargePaymentType()) { |
| | | AliQueryOrder data = h5AliPaymentClient.query(one.getCode()).getData(); |
| | | if (null != data) { |
| | | String trade_state = data.getTradeStatus(); |
| | |
| | | chargingOrder.setWhitelistServiceRefundAmount(BigDecimal.ZERO); |
| | | chargingOrder.setVipDiscountAmount(BigDecimal.ZERO); |
| | | chargingOrder.setOrderSource(1); |
| | | 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); |
| | |
| | | } |
| | | //会员优惠折扣将其计入增加充电时长(增加总充电金额) |
| | | //如果充电总金额未使用完,则需要退回费用=(原金额/总金额)*(总金额-实际充电金额) |
| | | if (1 == addChargingOrder.getPaymentType()) { |
| | | PaymentOrder paymentOrder = new PaymentOrder(); |
| | | paymentOrder.setCode(chargingOrder.getCode()); |
| | | paymentOrder.setAmount(thirdPayAmount); |
| | | paymentOrder.setOpenId(appUser.getWxOpenid()); |
| | | paymentOrder.setDescription("充电充值"); |
| | | paymentOrder.setNotifyUrl("/payment/wx/pay/notify"); |
| | | Map<String, Object> data = wxPaymentClient.orderPay(paymentOrder).getData(); |
| | | 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)); |
| | | IcbcPaymentResp data = icbcPaymentClient.payment(req).getData(); |
| | | if (isIcbcPaymentCreated(data)) { |
| | | if (StringUtils.hasLength(data.getTradeNo())) { |
| | | chargingOrder.setRechargeSerialNumber(data.getTradeNo()); |
| | | this.updateById(chargingOrder); |
| | | } |
| | | return AjaxResult.success(data); |
| | | } |
| | | throw new RuntimeException("调起工行支付失败"); |
| | | } |
| | | if (2 == addChargingOrder.getPaymentType()) { |
| | | if (Integer.valueOf(2).equals(addChargingOrder.getPaymentType())) { |
| | | AliPaymentReq req = new AliPaymentReq(); |
| | | req.setOutTradeNo(chargingOrder.getCode()); |
| | | req.setTotalAmount(thirdPayAmount.toString()); |
| | |
| | | req.setBody("充电充值"); |
| | | req.setNotifyUrl("/payment/ali/callBack"); |
| | | AliPaymentResp data = aliPaymentClient.payment(req).getData(); |
| | | if (null != data) { |
| | | if (data != null) { |
| | | return AjaxResult.success(data); |
| | | } |
| | | throw new RuntimeException("调起支付宝支付失败"); |
| | | } |
| | | if (3 == addChargingOrder.getPaymentType()) { |
| | | if (Integer.valueOf(3).equals(addChargingOrder.getPaymentType())) { |
| | | AliPaymentReq req = new AliPaymentReq(); |
| | | req.setOutTradeNo(chargingOrder.getCode()); |
| | | req.setTotalAmount(thirdPayAmount.toString()); |
| | |
| | | } |
| | | } |
| | | 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 = money(order.getChargeAmount()).compareTo(BigDecimal.ZERO) > 0 |
| | | ? money(order.getChargeAmount()) : money(order.getRechargeAmount()); |
| | | 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::getSourceName, "ICBC") |
| | | .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("当前枪没有属于你的可模拟ICBC已支付订单"); |
| | | } |
| | | return order; |
| | | } |
| | | |
| | | private ChargingSimulationVO buildSimulationVO(TChargingOrder order) { |
| | | ChargingSimulationVO result = new ChargingSimulationVO(); |
| | | result.setOrderId(order.getId()); |
| | | result.setOrderCode(order.getCode()); |
| | | result.setOrderStatus(order.getStatus()); |
| | | result.setRechargeAmount(order.getRechargeAmount()); |
| | | result.setChargeAmount(order.getChargeAmount()); |
| | | result.setElectricityFee(order.getElectrovalence()); |
| | | result.setServiceFee(order.getServiceCharge()); |
| | | result.setPaymentAmount(order.getPaymentAmount()); |
| | | result.setRefundAmount(order.getRefundAmount()); |
| | | 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) { |
| | | 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); |
| | | return requireFeignData(icbcPaymentClient.query(order.getCode()), "工行支付查询失败"); |
| | | } |
| | | |
| | | @Override |
| | | public IcbcRefundResp refundIcbcAdminPayment(IcbcAdminFundOperationReq request) { |
| | | if (request == null) { |
| | | throw new ServiceException("请求参数不能为空"); |
| | | } |
| | | TChargingOrder order = loadIcbcAdminOrder(request.getOrderCode()); |
| | | 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()), "工行支付查询失败"); |
| | | String orderId = extractIcbcOrderId(payment); |
| | | order.setRechargeSerialNumber(orderId); |
| | | 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); |
| | | 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); |
| | | 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()); |
| | | 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) { |
| | | 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 { |
| | | IcbcAdminFundOperationSupport.validateOrder(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(); |
| | |
| | | return; |
| | | } |
| | | this.updateById(chargingOrder); |
| | | if (isIcbcOrder(chargingOrder)) { |
| | | refundByIcbcAndFinish(chargingOrder, chargingOrderRefund, thirdRefundAmount, "充电失败,取消充电订单", false, |
| | | recordedRefundAmount, balanceRefundAmount); |
| | | return; |
| | | } |
| | | if (thirdRefundAmount.compareTo(BigDecimal.ZERO) > 0 && 1 == rechargePaymentType) { |
| | | WxPaymentRefundModel model = new WxPaymentRefundModel(); |
| | | model.setOut_trade_no(chargingOrder.getCode()); |
| | |
| | | finishManualChargingRefund(chargingOrder, chargingOrderRefund, money, balanceRefundAmount, thirdRefundAmount); |
| | | return; |
| | | } |
| | | if (isIcbcOrder(chargingOrder)) { |
| | | refundByIcbcAndFinish(chargingOrder, chargingOrderRefund, thirdRefundAmount, "充电完成退款", true, |
| | | money, balanceRefundAmount); |
| | | return; |
| | | } |
| | | if (1 == rechargePaymentType) { |
| | | WxPaymentRefundModel model = new WxPaymentRefundModel(); |
| | | model.setOut_trade_no(chargingOrder.getCode()); |
| | |
| | | chargingOrder.setRefundSerialNumber(refund_id); |
| | | chargingOrder.setRefundTime(LocalDateTime.now()); |
| | | this.updateById(chargingOrder); |
| | | triggerChargingOrderShare(this.getById(order.getId())); |
| | | } |
| | | } catch (InterruptedException e) { |
| | | throw new RuntimeException(e); |
| | |
| | | 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(); |
| | | for (ChargingOrderVO chargingOrderVO : list) { |
| | | if (roleType == 2) { |
| | |
| | | } |
| | | Map<String, Object> fundMap = this.baseMapper.chargingOrderFundSummary(dto, startTime1, startTime2, endTime1, endTime2); |
| | | if (null != fundMap) { |
| | | tCharingOrderVO.setPlatformIncomeTotal(getSummaryDecimal(fundMap, "platformIncomeTotal")); |
| | | tCharingOrderVO.setPartnerIncomeTotal(getSummaryDecimal(fundMap, "partnerIncomeTotal")); |
| | | 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")); |
| | |
| | | return totalMap; |
| | | } |
| | | |
| | | private Map<String, Object> getChargingOrderShareIncomeSummary(ChargingOrderQuery dto, String startTime1, String startTime2, String endTime1, String endTime2) { |
| | | 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; |
| | | } |
| | | BigDecimal platformIncomeTotal = BigDecimal.ZERO; |
| | | BigDecimal partnerIncomeTotal = BigDecimal.ZERO; |
| | | int batchSize = 500; |
| | | for (int i = 0; i < orderIds.size(); i += batchSize) { |
| | | List<Long> batchIds = orderIds.subList(i, Math.min(i + batchSize, orderIds.size())); |
| | | Map<String, Object> batchMap = this.baseMapper.chargingOrderShareIncomeSummaryByOrderIds(batchIds); |
| | | if (batchMap != null) { |
| | | platformIncomeTotal = platformIncomeTotal.add(getSummaryDecimal(batchMap, "platformIncomeTotal")); |
| | | partnerIncomeTotal = partnerIncomeTotal.add(getSummaryDecimal(batchMap, "partnerIncomeTotal")); |
| | | } |
| | | } |
| | | totalMap.put("platformIncomeTotal", platformIncomeTotal); |
| | | totalMap.put("partnerIncomeTotal", partnerIncomeTotal); |
| | | return totalMap; |
| | | } |
| | | |
| | | private BigDecimal getSummaryDecimal(Map<String, Object> map, String key) { |
| | | if (map == null) { |
| | | return BigDecimal.ZERO; |
| | |
| | | } |
| | | |
| | | @Override |
| | | public List<SixChargingDto> charge(LocalDate sixBefore, List<Integer> siteIds) { |
| | | return this.baseMapper.charge(sixBefore, siteIds); |
| | | public List<SixChargingDto> charge(LocalDate sixBefore, List<Integer> siteIds, Boolean partnerView) { |
| | | return this.baseMapper.charge(sixBefore, siteIds, partnerView); |
| | | } |
| | | |
| | | @Override |
| | | public List<SixCircleDto> circle(List<Integer> siteIds, LocalDate sixBefore) { |
| | | return this.baseMapper.circle(siteIds, sixBefore); |
| | | public List<SixCircleDto> circle(List<Integer> siteIds, LocalDate sixBefore, Boolean partnerView) { |
| | | return this.baseMapper.circle(siteIds, sixBefore, partnerView); |
| | | } |
| | | |
| | | @Override |
| | | public Map<String, Object> countAll(List<Integer> siteIds) { |
| | | return this.baseMapper.countAll(siteIds); |
| | | public Map<String, Object> countAll(List<Integer> siteIds, Boolean partnerView) { |
| | | return this.baseMapper.countAll(siteIds, partnerView); |
| | | } |
| | | |
| | | /** |
| | | * 统计近六月订单收入支付方式占比;服务层聚合避免复杂CASE SQL在当前数据库驱动中转换异常。 |
| | | */ |
| | | @Override |
| | | public List<SixIncomeCircleDto> payTypeCircle(LocalDate sixBefore, List<Integer> siteIds) { |
| | | public List<SixIncomeCircleDto> payTypeCircle(LocalDate sixBefore, List<Integer> siteIds, Boolean partnerView) { |
| | | if (siteIds == null || siteIds.isEmpty()) { |
| | | return buildPayTypeCircleData(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO); |
| | | } |
| | | if (Boolean.TRUE.equals(partnerView)) { |
| | | return buildPartnerShareCircleData(this.baseMapper.chargingOrderShareIncomeByPayChannel(sixBefore, siteIds)); |
| | | } |
| | | List<TChargingOrder> orders = this.lambdaQuery() |
| | | .select(TChargingOrder::getRechargePaymentType, TChargingOrder::getPayMethod, |
| | |
| | | Integer payMethod = order.getPayMethod(); |
| | | if (Integer.valueOf(1).equals(payMethod) || Integer.valueOf(2).equals(payMethod)) { |
| | | balanceAmount = balanceAmount.add(balancePayAmount); |
| | | } |
| | | } |
| | | return buildPayTypeCircleData(wechatAmount, alipayAmount, balanceAmount); |
| | | } |
| | | |
| | | private List<SixIncomeCircleDto> buildPartnerShareCircleData(List<Map<String, Object>> rows) { |
| | | BigDecimal wechatAmount = BigDecimal.ZERO; |
| | | BigDecimal alipayAmount = BigDecimal.ZERO; |
| | | BigDecimal balanceAmount = BigDecimal.ZERO; |
| | | if (rows != null) { |
| | | for (Map<String, Object> row : rows) { |
| | | String channel = row.get("payChannel") == null ? "" : row.get("payChannel").toString(); |
| | | BigDecimal shareAmount = getSummaryDecimal(row, "shareAmount"); |
| | | if ("WECHAT".equalsIgnoreCase(channel) || "WX".equalsIgnoreCase(channel)) { |
| | | wechatAmount = wechatAmount.add(shareAmount); |
| | | } else if ("ALIPAY".equalsIgnoreCase(channel) || "ALI".equalsIgnoreCase(channel)) { |
| | | alipayAmount = alipayAmount.add(shareAmount); |
| | | } else if ("BALANCE".equalsIgnoreCase(channel) || "WHITELIST".equalsIgnoreCase(channel)) { |
| | | balanceAmount = balanceAmount.add(shareAmount); |
| | | } else { |
| | | wechatAmount = wechatAmount.add(shareAmount); |
| | | } |
| | | } |
| | | } |
| | | return buildPayTypeCircleData(wechatAmount, alipayAmount, balanceAmount); |
| | |
| | | PageInfo<ChargingOrderListVO> pageInfo = new PageInfo<>(dto.getPageCurr(), dto.getPageSize()); |
| | | |
| | | List<ChargingOrderListVO> list = new ArrayList<>(); |
| | | Map<Integer, BigDecimal> siteServiceShareRatioMap = loadSiteServiceShareRatioMap(); |
| | | List<Map<String, Object>> maps = this.baseMapper.chargingList1(pageInfo, dto, startTime1, startTime2, endTime1, endTime2); |
| | | for (Map<String, Object> map : maps) { |
| | | if(null == map){ |
| | |
| | | } |
| | | chargingOrderListVO.setUid(map.get("id").toString()); |
| | | chargingOrderListVO.setCode(map.get("code").toString()); |
| | | chargingOrderListVO.setId(Long.valueOf(map.get("id").toString())); |
| | | chargingOrderListVO.setSiteId(Integer.valueOf(map.get("siteId").toString())); |
| | | chargingOrderListVO.setOrderSource(Integer.valueOf(map.get("orderSource").toString())); |
| | | chargingOrderListVO.setSiteName(map.get("siteName").toString()); |
| | | chargingOrderListVO.setOrderType(map.get("orderType").toString()); |
| | | chargingOrderListVO.setTerminalName(map.get("terminalName").toString()); |
| | | chargingOrderListVO.setElectricity(null == map.get("electricity") ? BigDecimal.ZERO : new BigDecimal(map.get("electricity").toString())); |
| | | chargingOrderListVO.setChargingCapacity(chargingOrderListVO.getElectricity()); |
| | | Object periodElectricPrice = map.get("periodElectricPrice"); |
| | | Object periodServicePrice = map.get("periodServicePrice"); |
| | | if(null != periodServicePrice){ |
| | | Integer orderSource = Integer.valueOf(map.get("orderSource").toString()); |
| | | BigDecimal periodElectricPrice = getSummaryDecimal(map, "periodElectricPrice"); |
| | | BigDecimal periodServicePrice = getSummaryDecimal(map, "periodServicePrice"); |
| | | if(periodServicePrice.compareTo(BigDecimal.ZERO) != 0){ |
| | | Integer orderSource = chargingOrderListVO.getOrderSource(); |
| | | if(2 == orderSource){ |
| | | BigDecimal bigDecimal = new BigDecimal(periodServicePrice.toString()).multiply(new BigDecimal(0.8)).setScale(2, RoundingMode.HALF_EVEN); |
| | | chargingOrderListVO.setPaymentAmount(new BigDecimal(periodElectricPrice.toString()).add(bigDecimal)); |
| | | BigDecimal sharingAmount = new BigDecimal(periodServicePrice.toString()).multiply(new BigDecimal(0.2)).setScale(2, RoundingMode.HALF_EVEN); |
| | | BigDecimal bigDecimal = periodServicePrice.multiply(new BigDecimal("0.8")).setScale(2, RoundingMode.HALF_EVEN); |
| | | chargingOrderListVO.setServiceCharge(bigDecimal); |
| | | chargingOrderListVO.setPaymentAmount(periodElectricPrice.add(bigDecimal).setScale(2, RoundingMode.HALF_EVEN)); |
| | | BigDecimal sharingAmount = periodServicePrice.multiply(new BigDecimal("0.2")).setScale(2, RoundingMode.HALF_EVEN); |
| | | chargingOrderListVO.setSharingAmount(sharingAmount); |
| | | }else{ |
| | | chargingOrderListVO.setPaymentAmount(new BigDecimal(periodElectricPrice.toString()).add(new BigDecimal(periodServicePrice.toString())).setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingOrderListVO.setServiceCharge(periodServicePrice); |
| | | chargingOrderListVO.setPaymentAmount(periodElectricPrice.add(periodServicePrice).setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingOrderListVO.setSharingAmount(BigDecimal.ZERO); |
| | | } |
| | | }else{ |
| | | chargingOrderListVO.setServiceCharge(BigDecimal.ZERO); |
| | | chargingOrderListVO.setPaymentAmount(BigDecimal.ZERO); |
| | | chargingOrderListVO.setSharingAmount(BigDecimal.ZERO); |
| | | } |
| | | chargingOrderListVO.setElectrovalence(null == map.get("electrovalence") ? BigDecimal.ZERO : new BigDecimal(map.get("electrovalence").toString())); |
| | | chargingOrderListVO.setServiceCharge(null == map.get("serviceCharge") ? BigDecimal.ZERO : new BigDecimal(map.get("serviceCharge").toString())); |
| | | BigDecimal bigDecimal = new BigDecimal("0.006"); |
| | | if (!"0".equals(map.get("rechargePaymentType").toString())) { |
| | | chargingOrderListVO.setCommissionAmount(new BigDecimal(map.get("rechargeAmount").toString()).multiply(bigDecimal)); |
| | | } |
| | | fillChargingPeriodIncome(chargingOrderListVO, siteServiceShareRatioMap); |
| | | if(null != map.get("tripartitePlatformName")){ |
| | | chargingOrderListVO.setTripartitePlatformName(map.get("tripartitePlatformName").toString()); |
| | | } |
| | |
| | | Map<String, Object> map = this.baseMapper.chargingListSum1(dto, startTime1, startTime2, endTime1, endTime2); |
| | | System.err.println("充电时段统计-查询参数:" + JSON.toJSONString(dto)); |
| | | System.err.println("充电时段统计-汇总数据:" + JSON.toJSONString(map)); |
| | | if (map == null) { |
| | | map = new HashMap<>(); |
| | | } |
| | | //订单数量 |
| | | int orderCount = Integer.parseInt(map.get("orderCount").toString()); |
| | | int orderCount = Integer.parseInt(String.valueOf(map.getOrDefault("orderCount", "0"))); |
| | | // 计算充电总度数 |
| | | BigDecimal electronic = new BigDecimal(map.get("electronic").toString()); |
| | | BigDecimal electronic = new BigDecimal(String.valueOf(map.getOrDefault("electronic", "0"))); |
| | | // 支付金额 |
| | | BigDecimal paymentAmount = new BigDecimal(map.get("paymentAmount").toString()); |
| | | BigDecimal paymentAmount = new BigDecimal(String.valueOf(map.getOrDefault("paymentAmount", "0"))); |
| | | // 电费 |
| | | BigDecimal electrovalence = new BigDecimal(map.get("electrovalence").toString()); |
| | | BigDecimal electrovalence = new BigDecimal(String.valueOf(map.getOrDefault("electrovalence", "0"))); |
| | | // 服务费 |
| | | BigDecimal serviceCharge = new BigDecimal(map.get("serviceCharge").toString()); |
| | | chargingOrderTimeVO.setOrderCount(orderCount); |
| | | BigDecimal serviceCharge = new BigDecimal(String.valueOf(map.getOrDefault("serviceCharge", "0"))); |
| | | chargingOrderTimeVO.setOrderCount(orderCount); |
| | | chargingOrderTimeVO.setChargingCapacity(electronic.setScale(2, BigDecimal.ROUND_HALF_EVEN)); |
| | | chargingOrderTimeVO.setPaymentAmount(paymentAmount.setScale(2, BigDecimal.ROUND_HALF_EVEN)); |
| | | chargingOrderTimeVO.setElectrovalence(electrovalence.setScale(2, BigDecimal.ROUND_HALF_EVEN)); |
| | | chargingOrderTimeVO.setServiceCharge(serviceCharge.setScale(2, BigDecimal.ROUND_HALF_EVEN)); |
| | | BigDecimal platformIncome = BigDecimal.ZERO; |
| | | BigDecimal partnerIncome = BigDecimal.ZERO; |
| | | for (Map<String, Object> sumMap : list1) { |
| | | if (sumMap == null) { |
| | | continue; |
| | | } |
| | | ChargingOrderListVO item = new ChargingOrderListVO(); |
| | | item.setSiteId(getSummaryLong(sumMap, "siteId").intValue()); |
| | | item.setOrderSource(getSummaryLong(sumMap, "orderSource").intValue()); |
| | | item.setPaymentAmount(getSummaryDecimal(sumMap, "paymentAmount")); |
| | | item.setServiceCharge(getSummaryDecimal(sumMap, "serviceCharge")); |
| | | fillChargingPeriodIncome(item, siteServiceShareRatioMap); |
| | | platformIncome = platformIncome.add(nullToZero(item.getPlatformIncome())); |
| | | partnerIncome = partnerIncome.add(nullToZero(item.getPartnerIncome())); |
| | | } |
| | | chargingOrderTimeVO.setPlatformIncome(platformIncome.setScale(2, RoundingMode.HALF_UP)); |
| | | chargingOrderTimeVO.setPartnerIncome(partnerIncome.setScale(2, RoundingMode.HALF_UP)); |
| | | // 计算尖峰平谷充电到账占比 |
| | | BigDecimal add = jianElectronic.add(fengElectronic).add(pingElectronic).add(guElectronic); |
| | | // 计算尖峰平谷服务费占比 |
| | |
| | | dto.setSiteIds(siteIds); |
| | | List<ChargingOrderListVO> list1 = this.baseMapper.chargingList(dto, startTime1, startTime2, endTime1, endTime2); |
| | | fillChargingPeriodExportBalanceIncome(list1); |
| | | fillChargingPeriodIncome(list1); |
| | | return list1; |
| | | } |
| | | |
| | | private void fillChargingPeriodIncome(List<ChargingOrderListVO> list) { |
| | | if (list == null || list.isEmpty()) { |
| | | return; |
| | | } |
| | | Map<Integer, BigDecimal> siteServiceShareRatioMap = loadSiteServiceShareRatioMap(); |
| | | for (ChargingOrderListVO item : list) { |
| | | fillChargingPeriodIncome(item, siteServiceShareRatioMap); |
| | | } |
| | | } |
| | | |
| | | private void fillChargingPeriodIncome(ChargingOrderListVO item, Map<Integer, BigDecimal> siteServiceShareRatioMap) { |
| | | if (item == null) { |
| | | return; |
| | | } |
| | | BigDecimal paymentAmount = nullToZero(item.getPaymentAmount()); |
| | | BigDecimal partnerIncome = BigDecimal.ZERO; |
| | | if (!Integer.valueOf(2).equals(item.getOrderSource())) { |
| | | BigDecimal serviceShareRatio = siteServiceShareRatioMap.get(item.getSiteId()); |
| | | if (serviceShareRatio != null && serviceShareRatio.compareTo(BigDecimal.ZERO) > 0) { |
| | | partnerIncome = nullToZero(item.getServiceCharge()) |
| | | .multiply(serviceShareRatio) |
| | | .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP); |
| | | } |
| | | } |
| | | item.setPartnerIncome(partnerIncome); |
| | | item.setPlatformIncome(paymentAmount.subtract(partnerIncome).max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP)); |
| | | } |
| | | |
| | | private Map<Integer, BigDecimal> loadSiteServiceShareRatioMap() { |
| | | Map<Integer, BigDecimal> ratioMap = new HashMap<>(); |
| | | R<List<Site>> siteResult = siteClient.getSiteAll(); |
| | | R<List<Partner>> partnerResult = siteClient.getAllPartner(); |
| | | List<Site> sites = siteResult == null ? Collections.emptyList() : siteResult.getData(); |
| | | List<Partner> partners = partnerResult == null ? Collections.emptyList() : partnerResult.getData(); |
| | | if (sites == null || sites.isEmpty() || partners == null || partners.isEmpty()) { |
| | | return ratioMap; |
| | | } |
| | | Map<Integer, Partner> partnerMap = partners.stream() |
| | | .filter(partner -> partner != null && partner.getId() != null) |
| | | .collect(Collectors.toMap(Partner::getId, partner -> partner, (left, right) -> left)); |
| | | for (Site site : sites) { |
| | | if (site == null || site.getId() == null || site.getPartnerId() == null) { |
| | | continue; |
| | | } |
| | | Partner partner = partnerMap.get(site.getPartnerId()); |
| | | if (partner != null && partner.getServiceShareRatio() != null) { |
| | | ratioMap.put(site.getId(), partner.getServiceShareRatio()); |
| | | } |
| | | } |
| | | return ratioMap; |
| | | } |
| | | |
| | | /** |
| | |
| | | Map<String, Object> row = incomeMap.get(item.getId()); |
| | | item.setBalanceIncome(getSummaryDecimal(row, "balanceIncome")); |
| | | item.setBalanceGiftIncome(getSummaryDecimal(row, "balanceGiftIncome")); |
| | | } |
| | | } |
| | | |
| | | private void fillChargingOrderShareIncome(List<ChargingOrderVO> list) { |
| | | if (list == null || list.isEmpty()) { |
| | | return; |
| | | } |
| | | List<Long> orderIds = list.stream().map(ChargingOrderVO::getId).filter(Objects::nonNull).collect(Collectors.toList()); |
| | | if (orderIds.isEmpty()) { |
| | | return; |
| | | } |
| | | Map<Long, Map<String, Object>> incomeMap = new HashMap<>(); |
| | | List<Map<String, Object>> rows = this.baseMapper.chargingOrderShareIncomeByOrderIds(orderIds); |
| | | if (rows != null) { |
| | | for (Map<String, Object> row : rows) { |
| | | Long orderId = getSummaryLong(row, "orderId"); |
| | | if (orderId != null) { |
| | | incomeMap.put(orderId, row); |
| | | } |
| | | } |
| | | } |
| | | for (ChargingOrderVO item : list) { |
| | | Map<String, Object> row = incomeMap.get(item.getId()); |
| | | item.setPlatformIncome(getSummaryDecimal(row, "platformIncome")); |
| | | item.setPartnerIncome(getSummaryDecimal(row, "partnerIncome")); |
| | | } |
| | | } |
| | | |
| | |
| | | @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()); |
| | |
| | | chargingOrder.setRefundStatus(2); |
| | | chargingOrder.setRefundTime(LocalDateTime.now()); |
| | | this.updateById(chargingOrder); |
| | | triggerChargingOrderShare(chargingOrder); |
| | | } else if (isIcbcOrder(chargingOrder)) { |
| | | 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()); |
| | |
| | | chargingOrder.setRefundStatus(2); |
| | | chargingOrder.setRefundTime(LocalDateTime.now()); |
| | | this.updateById(chargingOrder); |
| | | triggerChargingOrderShare(chargingOrder); |
| | | } else { |
| | | //构建退款明细 |
| | | TChargingOrderRefund chargingOrderRefund = new TChargingOrderRefund(); |
| | |
| | | chargingOrder.setRefundStatus(2); |
| | | chargingOrder.setRefundTime(LocalDateTime.now()); |
| | | this.updateById(chargingOrder); |
| | | triggerChargingOrderShare(chargingOrder); |
| | | } |
| | | } |
| | | |
| | |
| | | summaryData.setElectricPrice(electronic_reduce.setScale(2, BigDecimal.ROUND_HALF_EVEN)); |
| | | summaryData.setServicePrice(serviceMoney.setScale(2, BigDecimal.ROUND_HALF_EVEN)); |
| | | chargingOrderSummaryDataService.save(summaryData); |
| | | if (channelRefundAmount.compareTo(BigDecimal.ZERO) == 0 && Integer.valueOf(2).equals(chargingOrder.getRefundStatus())) { |
| | | triggerChargingOrderShare(chargingOrder, site); |
| | | } |
| | | |
| | | //推送监管平台订单状态和订单详情 |
| | | ChargingOrderVo chargingOrderVo = new ChargingOrderVo(); |
| | |
| | | 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(); |
| | | } |
| | |
| | | if (thirdRefundAmount.compareTo(BigDecimal.ZERO) == 0) { |
| | | chargingOrderRefund.setRefundSerialNumber("BALANCE"); |
| | | finishManualChargingRefund(tChargingOrder, chargingOrderRefund, manualRefundAmount, balanceRefundAmount, thirdRefundAmount); |
| | | } |
| | | else if (isIcbcOrder(tChargingOrder)) { |
| | | refundByIcbcAndFinish(tChargingOrder, chargingOrderRefund, thirdRefundAmount, "取消订单", true, |
| | | manualRefundAmount, balanceRefundAmount); |
| | | } |
| | | else if (1 == tChargingOrder.getRechargePaymentType()) { |
| | | WxPaymentRefundModel model = new WxPaymentRefundModel(); |
| | |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | private boolean isIcbcRemovableStatus(String status, TChargingOrder order) { |
| | | if (!StringUtils.hasLength(status)) { |
| | | return false; |
| | | } |
| | | if (Arrays.asList("FAIL", "FAILED", "CLOSED", "TRADE_CLOSED", "REVOKED", "PAYERROR").contains(status)) { |
| | | return true; |
| | | } |
| | | // 工行B2C不支持主动关单;NOTPAY超时仅释放本地待支付订单,不代表远端订单已关闭。 |
| | | return "NOTPAY".equals(status) && isLocalPendingOrderExpired(order); |
| | | } |
| | | |
| | | private boolean isLocalPendingOrderExpired(TChargingOrder order) { |
| | | return order != null && order.getCreateTime() != null && order.getCreateTime().plusMinutes(30).isBefore(LocalDateTime.now()); |
| | | } |
| | | |
| | | private boolean isIcbcPaymentCreated(IcbcPaymentResp resp) { |
| | | if (resp == null || !StringUtils.hasLength(resp.getStatus())) { |
| | | return false; |
| | | } |
| | | 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 void triggerChargingOrderShare(TChargingOrder chargingOrder, Site site) { |
| | | try { |
| | | if (!isNeedChargingOrderShare(chargingOrder)) { |
| | | return; |
| | | } |
| | | Partner partner = loadPartner(site); |
| | | for (TChargingOrderShareRecord shareRecord : buildChargingOrderShareRecords(chargingOrder, site, partner)) { |
| | | TChargingOrderShareRecord exist = chargingOrderShareRecordMapper.selectOne(new LambdaQueryWrapper<TChargingOrderShareRecord>() |
| | | .eq(TChargingOrderShareRecord::getChargingOrderCode, shareRecord.getChargingOrderCode()) |
| | | .eq(TChargingOrderShareRecord::getPayChannel, shareRecord.getPayChannel()) |
| | | .eq(TChargingOrderShareRecord::getDelFlag, false) |
| | | .last("limit 1")); |
| | | if (exist != null && ("SUCCESS".equals(exist.getOnlineShareStatus()) || "PROCESSING".equals(exist.getOnlineShareStatus()))) { |
| | | continue; |
| | | } |
| | | shareRecord = mergeChargingOrderShareRecord(exist, shareRecord); |
| | | saveOrUpdateChargingOrderShareRecord(shareRecord); |
| | | if (Boolean.TRUE.equals(shareRecord.getNeedOnlineShare())) { |
| | | processIcbcOnlineShare(shareRecord, partner); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("充电订单分账记录触发失败,订单号:{}", chargingOrder == null ? null : chargingOrder.getCode(), e); |
| | | recordChargingOrderShareFailure(chargingOrder, site, e); |
| | | } |
| | | } |
| | | |
| | | private void triggerChargingOrderShare(TChargingOrder chargingOrder) { |
| | | try { |
| | | 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); |
| | | triggerChargingOrderShare(chargingOrder, site); |
| | | } catch (Exception e) { |
| | | log.error("充电订单分账站点加载失败,订单号:{}", chargingOrder == null ? null : chargingOrder.getCode(), e); |
| | | } |
| | | } |
| | | |
| | | private boolean isNeedChargingOrderShare(TChargingOrder chargingOrder) { |
| | | return chargingOrder != null |
| | | && Integer.valueOf(5).equals(chargingOrder.getStatus()) |
| | | && Integer.valueOf(2).equals(chargingOrder.getRechargePaymentStatus()) |
| | | && Integer.valueOf(2).equals(chargingOrder.getRefundStatus()) |
| | | && getChargingOrderShareBaseAmount(chargingOrder).compareTo(BigDecimal.ZERO) > 0; |
| | | } |
| | | |
| | | private Partner loadPartner(Site site) { |
| | | if (site == null || site.getPartnerId() == null) { |
| | | return null; |
| | | } |
| | | R<Partner> partnerResult = partnerClient.getPartnerById(site.getPartnerId()); |
| | | return partnerResult == null ? null : partnerResult.getData(); |
| | | } |
| | | |
| | | private List<TChargingOrderShareRecord> buildChargingOrderShareRecords(TChargingOrder chargingOrder, Site site, Partner partner) { |
| | | BigDecimal actualThirdPay = getActualThirdPayAmount(chargingOrder); |
| | | BalanceShareAmount balanceShareAmount = getBalanceShareAmount(chargingOrder); |
| | | BigDecimal actualBalancePay = balanceShareAmount.getPrincipalAmount(); |
| | | BigDecimal actualTotalPay = actualThirdPay.add(actualBalancePay).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal shareElectricityAmount = getChargingOrderShareElectricityAmount(chargingOrder); |
| | | BigDecimal shareServiceAmount = getChargingOrderShareServiceAmount(chargingOrder); |
| | | BigDecimal shareBaseAmount = shareElectricityAmount.add(shareServiceAmount).setScale(2, RoundingMode.HALF_UP); |
| | | if (actualTotalPay.compareTo(BigDecimal.ZERO) <= 0 && isWhitelistOrder(chargingOrder)) { |
| | | actualTotalPay = shareBaseAmount; |
| | | } |
| | | if (actualTotalPay.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return Collections.emptyList(); |
| | | } |
| | | if (!isWhitelistOrder(chargingOrder) && shareBaseAmount.compareTo(BigDecimal.ZERO) > 0 && actualTotalPay.compareTo(shareBaseAmount) < 0) { |
| | | BigDecimal effectiveRatio = actualTotalPay.divide(shareBaseAmount, 6, RoundingMode.HALF_UP); |
| | | shareElectricityAmount = shareElectricityAmount.multiply(effectiveRatio).setScale(2, RoundingMode.HALF_UP); |
| | | shareServiceAmount = shareServiceAmount.multiply(effectiveRatio).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | List<TChargingOrderShareRecord> records = new ArrayList<>(); |
| | | if (actualBalancePay.compareTo(BigDecimal.ZERO) > 0) { |
| | | records.add(buildChargingOrderShareRecord(chargingOrder, site, partner, "BALANCE", actualBalancePay, |
| | | balanceShareAmount.getGiftAmount(), actualTotalPay, shareElectricityAmount, shareServiceAmount)); |
| | | } |
| | | if (actualThirdPay.compareTo(BigDecimal.ZERO) > 0) { |
| | | records.add(buildChargingOrderShareRecord(chargingOrder, site, partner, resolveThirdPayChannel(chargingOrder), actualThirdPay, |
| | | BigDecimal.ZERO, actualTotalPay, shareElectricityAmount, shareServiceAmount)); |
| | | } |
| | | if (records.isEmpty() && isWhitelistOrder(chargingOrder)) { |
| | | records.add(buildChargingOrderShareRecord(chargingOrder, site, partner, "WHITELIST", actualTotalPay, |
| | | BigDecimal.ZERO, actualTotalPay, shareElectricityAmount, shareServiceAmount)); |
| | | } |
| | | return records; |
| | | } |
| | | |
| | | private TChargingOrderShareRecord buildChargingOrderShareRecord(TChargingOrder chargingOrder, Site site, Partner partner, |
| | | String payChannel, BigDecimal channelPayAmount, BigDecimal channelGiftAmount, |
| | | BigDecimal actualTotalPay, BigDecimal shareElectricityAmount, |
| | | BigDecimal shareServiceAmount) { |
| | | TChargingOrderShareRecord record = new TChargingOrderShareRecord(); |
| | | record.setChargingOrderId(chargingOrder.getId()); |
| | | record.setChargingOrderCode(chargingOrder.getCode()); |
| | | record.setSiteId(chargingOrder.getSiteId()); |
| | | record.setPartnerId(site == null ? null : site.getPartnerId()); |
| | | record.setPayChannel(payChannel); |
| | | record.setChannelPayAmount(money(channelPayAmount)); |
| | | record.setChannelGiftAmount(money(channelGiftAmount)); |
| | | BigDecimal channelRatio = channelPayAmount.divide(actualTotalPay, 6, RoundingMode.HALF_UP); |
| | | record.setChannelPayRatio(channelRatio); |
| | | BigDecimal channelElectricityAmount = shareElectricityAmount.multiply(channelRatio).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal channelServiceAmount = shareServiceAmount.multiply(channelRatio).setScale(2, RoundingMode.HALF_UP); |
| | | record.setChannelElectricityAmount(channelElectricityAmount); |
| | | record.setChannelServiceAmount(channelServiceAmount); |
| | | record.setElectricityShareRatio(partner == null ? null : partner.getElectricityShareRatio()); |
| | | record.setServiceShareRatio(partner == null ? null : partner.getServiceShareRatio()); |
| | | BigDecimal electricityShareAmount = calculateShareAmount(channelElectricityAmount, record.getElectricityShareRatio()); |
| | | BigDecimal serviceShareAmount = calculateShareAmount(channelServiceAmount, record.getServiceShareRatio()); |
| | | 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(chargingOrder.getCode()); |
| | | } |
| | | if ("ICBC".equals(payChannel) && partner != null) { |
| | | record.setSubMerId(partner.getIcbcSubMerId()); |
| | | record.setSubMerPrtclNo(partner.getIcbcSubMerPrtclNo()); |
| | | record.setMerAcct(partner.getIcbcMerAcct()); |
| | | } |
| | | record.setRetryCount(0); |
| | | record.setDelFlag(false); |
| | | return record; |
| | | } |
| | | |
| | | private TChargingOrderShareRecord mergeChargingOrderShareRecord(TChargingOrderShareRecord exist, TChargingOrderShareRecord fresh) { |
| | | if (exist == null) { |
| | | return fresh; |
| | | } |
| | | fresh.setId(exist.getId()); |
| | | fresh.setRetryCount(exist.getRetryCount() == null ? 0 : exist.getRetryCount()); |
| | | fresh.setRequestJson(exist.getRequestJson()); |
| | | fresh.setResponseJson(exist.getResponseJson()); |
| | | fresh.setIcbcOrderId(exist.getIcbcOrderId()); |
| | | fresh.setSubOrderSeq(exist.getSubOrderSeq()); |
| | | fresh.setSubOrderTrxid(exist.getSubOrderTrxid()); |
| | | fresh.setRecResult(exist.getRecResult()); |
| | | fresh.setLastRetryTime(exist.getLastRetryTime()); |
| | | return fresh; |
| | | } |
| | | |
| | | private BigDecimal getActualThirdPayAmount(TChargingOrder chargingOrder) { |
| | | return getRefundableThirdPayAmount(chargingOrder).subtract(money(chargingOrder.getThirdRefundAmount())).max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private BigDecimal getActualBalancePayAmount(TChargingOrder chargingOrder) { |
| | | return money(chargingOrder.getBalancePayAmount()).subtract(money(chargingOrder.getBalanceRefundAmount())).max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private BalanceShareAmount getBalanceShareAmount(TChargingOrder chargingOrder) { |
| | | if (chargingOrder == null || chargingOrder.getId() == null) { |
| | | return new BalanceShareAmount(BigDecimal.ZERO, BigDecimal.ZERO); |
| | | } |
| | | BigDecimal fallbackBalancePay = getActualBalancePayAmount(chargingOrder); |
| | | if (fallbackBalancePay.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return new BalanceShareAmount(BigDecimal.ZERO, BigDecimal.ZERO); |
| | | } |
| | | List<Map<String, Object>> rows = this.baseMapper.exchangeCodeBalanceIncomeByOrderIds(Collections.singletonList(chargingOrder.getId())); |
| | | if (rows == null || rows.isEmpty()) { |
| | | return new BalanceShareAmount(fallbackBalancePay, BigDecimal.ZERO); |
| | | } |
| | | 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); |
| | | } |
| | | |
| | | private BigDecimal getChargingOrderShareElectricityAmount(TChargingOrder chargingOrder) { |
| | | if (chargingOrder == null) { |
| | | return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | return money(chargingOrder.getElectrovalence()).add(money(chargingOrder.getWhitelistElectricRefundAmount())).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private BigDecimal getChargingOrderShareServiceAmount(TChargingOrder chargingOrder) { |
| | | if (chargingOrder == null) { |
| | | return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | return money(chargingOrder.getServiceCharge()).add(money(chargingOrder.getWhitelistServiceRefundAmount())).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private BigDecimal getChargingOrderShareBaseAmount(TChargingOrder chargingOrder) { |
| | | return getChargingOrderShareElectricityAmount(chargingOrder).add(getChargingOrderShareServiceAmount(chargingOrder)).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private String resolveThirdPayChannel(TChargingOrder chargingOrder) { |
| | | if ("ICBC".equals(chargingOrder.getSourceName())) { |
| | | return "ICBC"; |
| | | } |
| | | if (Integer.valueOf(1).equals(chargingOrder.getRechargePaymentType())) { |
| | | return "WECHAT"; |
| | | } |
| | | if (Integer.valueOf(2).equals(chargingOrder.getRechargePaymentType()) || Integer.valueOf(3).equals(chargingOrder.getRechargePaymentType())) { |
| | | return "ALIPAY"; |
| | | } |
| | | return "OTHER"; |
| | | } |
| | | |
| | | private void processIcbcOnlineShare(TChargingOrderShareRecord record, Partner partner) { |
| | | try { |
| | | String invalidReason = validateIcbcShareRecord(record, partner); |
| | | if (StringUtils.hasLength(invalidReason)) { |
| | | markChargingOrderShare(record, "FAIL", invalidReason); |
| | | return; |
| | | } |
| | | IcbcSubOrderSplitReq req = buildIcbcSubOrderSplitReq(record); |
| | | record.setRequestJson(JSON.toJSONString(req)); |
| | | record.setOnlineShareStatus("PROCESSING"); |
| | | record.setRetryCount(record.getRetryCount() == null ? 1 : record.getRetryCount() + 1); |
| | | record.setLastRetryTime(LocalDateTime.now()); |
| | | saveOrUpdateChargingOrderShareRecord(record); |
| | | |
| | | R<IcbcSubOrderSplitResp> result = icbcPaymentClient.splitSubOrder(req); |
| | | IcbcSubOrderSplitResp resp = result == null ? null : result.getData(); |
| | | record.setResponseJson(resp == null ? (result == null ? null : JSON.toJSONString(result)) : resp.getRawResponse()); |
| | | if (result == null || result.getCode() != 200 || resp == null) { |
| | | markChargingOrderShare(record, "FAIL", result == null ? "工行拆分接口无响应" : result.getMsg()); |
| | | return; |
| | | } |
| | | record.setIcbcOrderId(resp.getOrderId()); |
| | | record.setSubOrderSeq(resp.getSubOrderSeq()); |
| | | record.setSubOrderTrxid(resp.getSubOrderTrxid()); |
| | | record.setRecResult(resp.getRecResult()); |
| | | if ("SUCCESS".equals(resp.getStatus()) && ("0".equals(resp.getRecResult()) || !StringUtils.hasLength(resp.getRecResult()))) { |
| | | markChargingOrderShare(record, "SUCCESS", null); |
| | | } else { |
| | | markChargingOrderShare(record, "FAIL", "工行拆分未成功:" + firstNotBlank(resp.getRecResult(), resp.getStatus())); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("工行线上分账处理失败,订单号:{},渠道:{}", record == null ? null : record.getChargingOrderCode(), record == null ? null : record.getPayChannel(), e); |
| | | if (record != null) { |
| | | markChargingOrderShare(record, "FAIL", firstNotBlank(e.getMessage(), "工行线上分账处理异常")); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void recordChargingOrderShareFailure(TChargingOrder chargingOrder, Site site, Exception e) { |
| | | try { |
| | | if (chargingOrder == null || !"ICBC".equals(chargingOrder.getSourceName()) || getActualThirdPayAmount(chargingOrder).compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | | TChargingOrderShareRecord record = chargingOrderShareRecordMapper.selectOne(new LambdaQueryWrapper<TChargingOrderShareRecord>() |
| | | .eq(TChargingOrderShareRecord::getChargingOrderCode, chargingOrder.getCode()) |
| | | .eq(TChargingOrderShareRecord::getPayChannel, "ICBC") |
| | | .eq(TChargingOrderShareRecord::getDelFlag, false) |
| | | .last("limit 1")); |
| | | if (record == null) { |
| | | BigDecimal actualThirdPay = getActualThirdPayAmount(chargingOrder); |
| | | BigDecimal actualTotalPay = actualThirdPay.add(getBalanceShareAmount(chargingOrder).getPrincipalAmount()).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal shareElectricityAmount = getChargingOrderShareElectricityAmount(chargingOrder); |
| | | BigDecimal shareServiceAmount = getChargingOrderShareServiceAmount(chargingOrder); |
| | | record = buildChargingOrderShareRecord(chargingOrder, site, null, "ICBC", actualThirdPay, BigDecimal.ZERO, |
| | | actualTotalPay.compareTo(BigDecimal.ZERO) > 0 ? actualTotalPay : actualThirdPay, shareElectricityAmount, shareServiceAmount); |
| | | } |
| | | record.setNeedOnlineShare(true); |
| | | markChargingOrderShare(record, "FAIL", firstNotBlank(e.getMessage(), "充电订单分账记录触发异常")); |
| | | } catch (Exception ex) { |
| | | log.error("充电订单分账失败补偿记录落库失败,订单号:{}", chargingOrder == null ? null : chargingOrder.getCode(), ex); |
| | | } |
| | | } |
| | | |
| | | private String validateIcbcShareRecord(TChargingOrderShareRecord record, Partner partner) { |
| | | if (partner == null) { |
| | | return "站点未配置合作商,无法工行拆分"; |
| | | } |
| | | if (partner.getElectricityShareRatio() == null && partner.getServiceShareRatio() == null) { |
| | | return "合作商未配置分成比例,跳过工行拆分"; |
| | | } |
| | | if (!StringUtils.hasLength(partner.getIcbcSubMerId())) { |
| | | return "合作商未配置工行二级商户编号"; |
| | | } |
| | | if (!StringUtils.hasLength(partner.getIcbcSubMerPrtclNo())) { |
| | | return "合作商未配置工行二级商户协议编号"; |
| | | } |
| | | if (money(record.getShareAmount()).compareTo(BigDecimal.ZERO) <= 0) { |
| | | return "工行拆分金额为0"; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private BigDecimal calculateShareAmount(BigDecimal amount, BigDecimal ratio) { |
| | | if (amount == null || ratio == null) { |
| | | return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | return amount.multiply(ratio).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private IcbcSubOrderSplitReq buildIcbcSubOrderSplitReq(TChargingOrderShareRecord record) { |
| | | IcbcSubOrderSplitReq req = new IcbcSubOrderSplitReq(); |
| | | req.setChargingOrderCode(record.getChargingOrderCode()); |
| | | req.setSeqNo(record.getSeqNo()); |
| | | req.setBusiType(record.getBusiType()); |
| | | req.setOriMerId(record.getOriMerId()); |
| | | req.setOriTrxDate(record.getOriTrxDate() == null ? null : record.getOriTrxDate().toString()); |
| | | req.setSubOrderId(record.getSubOrderId()); |
| | | req.setSubMerId(record.getSubMerId()); |
| | | req.setSubMerPrtclNo(record.getSubMerPrtclNo()); |
| | | req.setClassifyAmt(record.getShareAmount()); |
| | | req.setMerAcct(record.getMerAcct()); |
| | | req.setMerSpInfor("充电订单分账:" + record.getChargingOrderCode()); |
| | | return req; |
| | | } |
| | | |
| | | private void markChargingOrderShare(TChargingOrderShareRecord record, String status, String reason) { |
| | | record.setOnlineShareStatus(status); |
| | | record.setFailReason(reason); |
| | | saveOrUpdateChargingOrderShareRecord(record); |
| | | } |
| | | |
| | | private void saveOrUpdateChargingOrderShareRecord(TChargingOrderShareRecord record) { |
| | | if (record.getId() == null) { |
| | | chargingOrderShareRecordMapper.insert(record); |
| | | } else { |
| | | chargingOrderShareRecordMapper.updateById(record); |
| | | } |
| | | } |
| | | |
| | | private String buildIcbcProfitSharing(Site site) { |
| | | if (site == null || site.getPartnerId() == null) { |
| | | return null; |
| | | } |
| | | R<Partner> partnerResult = partnerClient.getPartnerById(site.getPartnerId()); |
| | | Partner partner = partnerResult == null ? null : partnerResult.getData(); |
| | | if (partner == null || !StringUtils.hasLength(partner.getIcbcSubMerId()) || !StringUtils.hasLength(partner.getIcbcSubMerPrtclNo())) { |
| | | return null; |
| | | } |
| | | if (partner.getElectricityShareRatio() == null && partner.getServiceShareRatio() == null) { |
| | | return null; |
| | | } |
| | | JSONObject data = new JSONObject(); |
| | | 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()); |
| | | } |
| | | if (partner.getServiceShareRatio() != null) { |
| | | data.put("serviceShareRatio", partner.getServiceShareRatio()); |
| | | } |
| | | return data.toJSONString(); |
| | | } |
| | | |
| | | private static class BalanceShareAmount { |
| | | private final BigDecimal principalAmount; |
| | | private final BigDecimal giftAmount; |
| | | |
| | | private BalanceShareAmount(BigDecimal principalAmount, BigDecimal giftAmount) { |
| | | this.principalAmount = principalAmount == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : principalAmount.setScale(2, RoundingMode.HALF_UP); |
| | | this.giftAmount = giftAmount == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : giftAmount.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private BigDecimal getPrincipalAmount() { |
| | | return principalAmount; |
| | | } |
| | | |
| | | private BigDecimal getGiftAmount() { |
| | | return giftAmount; |
| | | } |
| | | } |
| | | |
| | | private String firstNotBlank(String... values) { |
| | | for (String value : values) { |
| | | if (StringUtils.hasLength(value)) { |
| | | return value; |
| | | } |
| | | } |
| | | return ""; |
| | | } |
| | | } |