| | |
| | | package com.ruoyi.order.util.task; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson2.util.UUIDUtils; |
| | | import com.alibaba.nacos.common.utils.UuidUtils; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; |
| | | import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper; |
| | | import com.ruoyi.account.api.feignClient.AppCouponClient; |
| | | import com.ruoyi.account.api.feignClient.AppUserClient; |
| | | import com.ruoyi.account.api.feignClient.AppUserIntegralChangeClient; |
| | | import com.ruoyi.account.api.model.TAppCoupon; |
| | | import com.ruoyi.account.api.model.TAppUser; |
| | | import com.ruoyi.account.api.model.TAppUserIntegralChange; |
| | | import com.ruoyi.chargingPile.api.dto.TBillingTemplateDTO; |
| | | import com.ruoyi.chargingPile.api.feignClient.ChargingGunClient; |
| | | import com.ruoyi.chargingPile.api.feignClient.SiteClient; |
| | | import com.ruoyi.chargingPile.api.model.Partner; |
| | | import com.ruoyi.chargingPile.api.model.Site; |
| | | import com.ruoyi.chargingPile.api.model.TBillingTemplateDetail; |
| | | import com.ruoyi.chargingPile.api.model.TChargingGun; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.common.core.utils.StringUtils; |
| | | import com.ruoyi.integration.api.feignClient.ChargingMessageClient; |
| | | import com.ruoyi.integration.api.feignClient.UploadRealTimeMonitoringDataClient; |
| | | import com.ruoyi.integration.api.feignClient.ChengDuJianGuanClient; |
| | | import com.ruoyi.integration.api.feignClient.TCECClient; |
| | | import com.ruoyi.integration.api.model.TransactionRecord; |
| | | import com.ruoyi.integration.api.model.UploadRealTimeMonitoringData; |
| | | import com.ruoyi.integration.api.vo.ChargingOrderVo; |
| | | import com.ruoyi.order.api.model.TChargingBill; |
| | | import com.ruoyi.order.api.model.TChargingOrder; |
| | | import com.ruoyi.order.api.model.TChargingOrderRefund; |
| | | import com.ruoyi.integration.api.vo.NotificationConnectorChargeStatusVo; |
| | | import com.ruoyi.order.api.model.*; |
| | | import com.ruoyi.order.api.vo.TransactionRecordMessageVO; |
| | | import com.ruoyi.order.service.TChargingBillService; |
| | | import com.ruoyi.order.service.TChargingOrderRefundService; |
| | | import com.ruoyi.order.service.TChargingOrderService; |
| | | import com.ruoyi.order.service.*; |
| | | import com.ruoyi.order.service.impl.ChargingCouponSupport; |
| | | import com.ruoyi.order.support.ChargeAccountSupport; |
| | | import com.ruoyi.order.util.mongodb.service.TransactionRecordService; |
| | | import com.ruoyi.order.util.mongodb.service.UploadRealTimeMonitoringDataService; |
| | | import com.ruoyi.other.api.domain.TCoupon; |
| | | import com.ruoyi.other.api.domain.TIntegralRule; |
| | | import com.ruoyi.other.api.domain.TVip; |
| | | import com.ruoyi.other.api.dto.CompanyPreferentialDTO; |
| | | import com.ruoyi.other.api.dto.ExchangeCodeConsumeDTO; |
| | | import com.ruoyi.other.api.feignClient.IntegralRuleClient; |
| | | import com.ruoyi.other.api.feignClient.OtherClient; |
| | | import com.ruoyi.other.api.feignClient.VipClient; |
| | | 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.RefundReq; |
| | | import com.ruoyi.payment.api.model.RefundResp; |
| | | import com.ruoyi.payment.api.model.WxPaymentRefundModel; |
| | | import com.ruoyi.payment.api.vo.IcbcRefundReq; |
| | | import com.ruoyi.payment.api.vo.IcbcRefundResp; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.logging.log4j.core.util.UuidUtil; |
| | | import org.apache.poi.util.StringUtil; |
| | | import org.redisson.api.RLock; |
| | | import org.redisson.api.RedissonClient; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.boot.web.context.WebServerInitializedEvent; |
| | | import org.springframework.context.ApplicationListener; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.math.BigDecimal; |
| | | import java.math.MathContext; |
| | | import java.math.RoundingMode; |
| | | import java.text.SimpleDateFormat; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.ZoneId; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.ArrayList; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import java.util.Random; |
| | | import java.time.temporal.ChronoUnit; |
| | | import java.time.temporal.TemporalAdjusters; |
| | | import java.util.*; |
| | | import java.util.concurrent.LinkedBlockingDeque; |
| | | import java.util.concurrent.ThreadPoolExecutor; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | |
| | | @Slf4j |
| | | @Component |
| | | public class TaskUtil implements ApplicationListener<WebServerInitializedEvent> { |
| | | private static final int WHITELIST_BENEFIT_ALL_FREE = 1; |
| | | |
| | | @Resource |
| | | private TChargingBillService chargingBillService; |
| | | |
| | | @Resource |
| | | private ITChargingSettlementBillService chargingSettlementBillService; |
| | | |
| | | @Resource |
| | | private ITAccountSettlementStatementService accountSettlementStatementService; |
| | | @Resource |
| | | private SiteClient siteClient; |
| | | @Resource |
| | | private TChargingOrderService chargingOrderService; |
| | | |
| | | @Resource |
| | | private TCardChargingOrderService cardChargingOrderService; |
| | | |
| | | @Resource |
| | | private TVinChargingOrderService vinChargingOrderService; |
| | | |
| | | @Resource |
| | | private TChargingOrderRefundService chargingOrderRefundService; |
| | |
| | | private ChargingMessageClient chargingMessageClient; |
| | | |
| | | private Integer port = null; |
| | | @Resource |
| | | private AccountingStrategyDetailOrderService accountingStrategyDetailOrderService; |
| | | |
| | | @Resource |
| | | private TChargingOrderAccountingStrategyService chargingOrderAccountingStrategyService; |
| | | |
| | | @Resource |
| | | private AppCouponClient appCouponClient; |
| | | |
| | | @Resource |
| | | private TCECClient tcecClient; |
| | | |
| | | @Resource |
| | | private VipClient vipClient; |
| | | |
| | | @Resource |
| | | private ChargingGunClient chargingGunClient; |
| | | |
| | | @Resource |
| | | private IntegralRuleClient integralRuleClient; |
| | | @Resource |
| | | private AppUserClient appUserClient; |
| | | |
| | | @Resource |
| | | private AppUserIntegralChangeClient appUserIntegralChangeClient; |
| | | |
| | | @Resource |
| | | private OtherClient otherClient; |
| | | |
| | | @Resource |
| | | private WxPaymentClient wxPaymentClient; |
| | | |
| | | @Resource |
| | | private IcbcPaymentClient icbcPaymentClient; |
| | | |
| | | @Resource |
| | | private AliPaymentClient aliPaymentClient; |
| | | @Resource |
| | | private H5AliPaymentClient h5AliPaymentClient; |
| | | |
| | | @Resource |
| | | private ITChargingOrderSummaryDataService chargingOrderSummaryDataService; |
| | | |
| | | @Resource |
| | | private ChengDuJianGuanClient chengDuJianGuanClient; |
| | | |
| | | @Resource |
| | | private RedissonClient redissonClient; |
| | | |
| | | @Resource |
| | | private ChargeAccountSupport chargeAccountSupport; |
| | | |
| | | |
| | | |
| | | /** |
| | | * 判断订单是否使用余额或余额组合支付 |
| | | * |
| | | * @param payMethod 支付方式 |
| | | * @return true=余额相关支付方式 |
| | | */ |
| | | private boolean isBalancePayMode(Integer payMethod) { |
| | | return payMethod != null && (payMethod == 1 || payMethod == 2); |
| | | } |
| | | |
| | | /** |
| | | * 将金额空值统一转换为两位小数,避免补偿结算金额计算出现空指针 |
| | | * |
| | | * @param amount 原始金额 |
| | | * @return 非空两位小数金额 |
| | | */ |
| | | private BigDecimal money(BigDecimal amount) { |
| | | return amount == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : amount.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | /** |
| | | * 获取第三方实付金额,兼容早期未写入third_pay_amount的纯第三方支付订单。 |
| | | * |
| | | * @param order 充电订单 |
| | | * @return 第三方可退款本金 |
| | | */ |
| | | private BigDecimal getRefundableThirdPayAmount(TChargingOrder order) { |
| | | 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 ("ICBC".equalsIgnoreCase(order.getSourceName())) { |
| | | return money(order.getRechargeAmount()) |
| | | .subtract(money(order.getBalancePayAmount())) |
| | | .max(BigDecimal.ZERO) |
| | | .setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | if (Integer.valueOf(3).equals(order.getPayMethod())) { |
| | | return money(order.getRechargeAmount()); |
| | | } |
| | | return thirdPayAmount; |
| | | } |
| | | |
| | | /** |
| | | * 判断订单是否为白名单订单 |
| | | * |
| | | * @param order 充电订单 |
| | | * @return true=白名单订单 |
| | | */ |
| | | private boolean isWhitelistOrder(TChargingOrder order) { |
| | | return order != null && Integer.valueOf(1).equals(order.getIsWhitelist()); |
| | | } |
| | | |
| | | /** |
| | | * 判断订单是否为白名单全部免费权益 |
| | | * |
| | | * @param order 充电订单 |
| | | * @return true=全部免费白名单订单 |
| | | */ |
| | | private boolean isWhitelistAllFreeOrder(TChargingOrder order) { |
| | | return isWhitelistOrder(order) |
| | | && Integer.valueOf(WHITELIST_BENEFIT_ALL_FREE).equals(order.getWhitelistBenefitType()); |
| | | } |
| | | |
| | | /** |
| | | * 按白名单权益拆分补偿结算的电费和服务费减免金额,确保任务补偿与正常结算口径一致 |
| | | * |
| | | * @param order 充电订单 |
| | | * @param electrovalence 原始电费 |
| | | * @param serviceCharge 原始服务费 |
| | | * @return 数组下标0=电费减免,下标1=服务费减免 |
| | | */ |
| | | private BigDecimal[] calculateWhitelistRefundSplit(TChargingOrder order, BigDecimal electrovalence, BigDecimal serviceCharge) { |
| | | return ChargingCouponSupport.calculateWhitelistRefundSplitByBenefitType( |
| | | isWhitelistOrder(order), |
| | | order == null ? null : order.getWhitelistBenefitType(), |
| | | electrovalence, |
| | | serviceCharge); |
| | | } |
| | | |
| | | /** |
| | | * 记录兑换码余额退款流水 |
| | | * |
| | | * @param order 充电订单 |
| | | * @param refundAmount 退款金额 |
| | | * @param refundNo 退款单号 |
| | | */ |
| | | private void recordBalanceRefund(TChargingOrder order, BigDecimal refundAmount, String refundNo) { |
| | | if (order == null || order.getAppUserId() == null || order.getId() == null |
| | | || refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | | ExchangeCodeConsumeDTO refundDTO = new ExchangeCodeConsumeDTO(); |
| | | refundDTO.setUserId(order.getAppUserId()); |
| | | refundDTO.setOrderId(order.getId()); |
| | | refundDTO.setAmount(refundAmount); |
| | | refundDTO.setRefundNo(refundNo); |
| | | R<ExchangeCodeConsumeResultVO> result = otherClient.refundExchangeCodeBalance(refundDTO); |
| | | if (result == null || result.getCode() != 200) { |
| | | throw new RuntimeException("退还兑换码余额失败"); |
| | | } |
| | | } |
| | | |
| | | @Scheduled(fixedRate = 1000 * 60) |
| | | public void taskOneMinutes() { |
| | | if (isScheduledTaskPort(port)) { |
| | | List<TChargingOrder> upStatusChargingOrder = chargingOrderService.findUpStatusChargingOrder(); |
| | | log.info("定时任务执行,查询需要手动修改状态的订单数量:{}", upStatusChargingOrder.size()); |
| | | for (TChargingOrder order : upStatusChargingOrder) { |
| | | if(2 == order.getStatus()){ |
| | | order.setStatus(-1); |
| | | }else{ |
| | | order.setStatus(5); |
| | | } |
| | | chargingOrderService.updateById(order); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** 实时监控静默阈值:超过该时间仍无交易记录时按异常断电补偿 */ |
| | | private static final long MONITORING_SILENT_MILLIS = TimeUnit.MINUTES.toMillis(10); |
| | | /** 无任何设备数据时的最长保护时间,避免 Mongo 短暂延迟造成误关单 */ |
| | | private static final long NO_DEVICE_DATA_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(24); |
| | | |
| | | /** |
| | | * 刷卡/VIN 订单补偿(10分钟一轮):真实交易记录优先统一结算;启动中有实时数据则恢复充电中; |
| | | * 仅在设备数据缺失或静默达到保护阈值后执行关单/累计电量兜底结算。 |
| | | */ |
| | | @Scheduled(fixedRate = 1000 * 60 * 10, initialDelay = 1000 * 60) |
| | | public void cardVinOrderCompensationTask() { |
| | | if (isScheduledTaskPort(port)) { |
| | | compensateCardOrders(); |
| | | compensateVinOrders(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * VIN 结算失败重试(1分钟一轮,结算失败主触发):处理账户结算失败回退的状态4订单, |
| | | * 与10分钟补偿任务(DB兜底扫描)互补;重试幂等由账户侧消费流水保证,不会重复扣款。 |
| | | */ |
| | | @Scheduled(fixedRate = 1000 * 60, initialDelay = 1000 * 30) |
| | | public void vinSettleRetryTask() { |
| | | if (isScheduledTaskPort(port)) { |
| | | try { |
| | | vinChargingOrderService.retryPendingVinSettle(); |
| | | } catch (Exception e) { |
| | | log.error("[VD结算重试] 定时任务异常", e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void compensateCardOrders() { |
| | | try { |
| | | int recovered = cardChargingOrderService.recoverFinishedOrdersWithoutDeduct(); |
| | | if (recovered > 0) { |
| | | log.warn("[SD扣款补偿] 本轮回退未扣款已结束订单数量:{}", recovered); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("[SD扣款补偿] 已结束未扣款扫描异常", e); |
| | | } |
| | | try { |
| | | for (TCardChargingOrder order : cardChargingOrderService.listTimeoutStartingOrders()) { |
| | | compensateCardStartingOrder(order); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("[SD补偿] 启动超时扫描异常", e); |
| | | } |
| | | try { |
| | | for (TCardChargingOrder order : cardChargingOrderService.listTimeoutChargingOrders()) { |
| | | compensateCardActiveOrder(order); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("[SD补偿] 活动订单扫描异常", e); |
| | | } |
| | | } |
| | | |
| | | private void compensateCardStartingOrder(TCardChargingOrder order) { |
| | | try { |
| | | TransactionRecord record = findCompleteTransactionRecord(order.getCode()); |
| | | if (record != null) { |
| | | cardChargingOrderService.cardEndChargeBillingCharge(toTransactionRecordVO(record)); |
| | | return; |
| | | } |
| | | UploadRealTimeMonitoringData latest = uploadRealTimeMonitoringDataService.getLastDataById(order.getCode()); |
| | | if (latest != null && (latest.getCreate_time() == null |
| | | || System.currentTimeMillis() - latest.getCreate_time().getTime() <= MONITORING_SILENT_MILLIS)) { |
| | | cardChargingOrderService.handleHandshakeVin(order.getCode(), null); |
| | | log.info("[SD补偿] 启动应答缺失但存在活跃实时数据,恢复充电中 code={}", order.getCode()); |
| | | return; |
| | | } |
| | | cardChargingOrderService.closeTimeoutStartingOrder(order); |
| | | } catch (Exception ex) { |
| | | log.error("[SD补偿] 启动订单处理异常,保留原状态待下轮重试 code={}", order.getCode(), ex); |
| | | } |
| | | } |
| | | |
| | | private void compensateCardActiveOrder(TCardChargingOrder order) { |
| | | try { |
| | | TransactionRecord record = findCompleteTransactionRecord(order.getCode()); |
| | | if (record != null) { |
| | | cardChargingOrderService.cardEndChargeBillingCharge(toTransactionRecordVO(record)); |
| | | return; |
| | | } |
| | | UploadRealTimeMonitoringData latest = uploadRealTimeMonitoringDataService.getLastDataById(order.getCode()); |
| | | if (shouldFallbackSettle(latest, order.getCreateTime(), false)) { |
| | | cardChargingOrderService.compensationSettle(order); |
| | | } |
| | | } catch (Exception ex) { |
| | | log.error("[SD补偿] 活动订单处理异常,保留原状态待下轮重试 code={}", order.getCode(), ex); |
| | | } |
| | | } |
| | | |
| | | private void compensateVinOrders() { |
| | | try { |
| | | for (TVinChargingOrder order : vinChargingOrderService.listTimeoutStartingOrders()) { |
| | | compensateVinStartingOrder(order); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("[VD补偿] 启动超时扫描异常", e); |
| | | } |
| | | try { |
| | | for (TVinChargingOrder order : vinChargingOrderService.listTimeoutChargingOrders()) { |
| | | compensateVinActiveOrder(order); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("[VD补偿] 活动订单扫描异常", e); |
| | | } |
| | | } |
| | | |
| | | private void compensateVinStartingOrder(TVinChargingOrder order) { |
| | | try { |
| | | TransactionRecord record = findCompleteTransactionRecord(order.getCode()); |
| | | if (record != null) { |
| | | vinChargingOrderService.vinEndChargeBillingCharge(toTransactionRecordVO(record)); |
| | | return; |
| | | } |
| | | UploadRealTimeMonitoringData latest = uploadRealTimeMonitoringDataService.getLastDataById(order.getCode()); |
| | | if (latest != null && (latest.getCreate_time() == null |
| | | || System.currentTimeMillis() - latest.getCreate_time().getTime() <= MONITORING_SILENT_MILLIS)) { |
| | | vinChargingOrderService.handleHandshakeVin(order.getCode(), null); |
| | | log.info("[VD补偿] 启动应答缺失但存在活跃实时数据,恢复充电中 code={}", order.getCode()); |
| | | return; |
| | | } |
| | | vinChargingOrderService.closeTimeoutStartingOrder(order); |
| | | } catch (Exception ex) { |
| | | log.error("[VD补偿] 启动订单处理异常,保留原状态待下轮重试 code={}", order.getCode(), ex); |
| | | } |
| | | } |
| | | |
| | | private void compensateVinActiveOrder(TVinChargingOrder order) { |
| | | try { |
| | | TransactionRecord record = findCompleteTransactionRecord(order.getCode()); |
| | | if (record != null) { |
| | | vinChargingOrderService.vinEndChargeBillingCharge(toTransactionRecordVO(record)); |
| | | return; |
| | | } |
| | | UploadRealTimeMonitoringData latest = uploadRealTimeMonitoringDataService.getLastDataById(order.getCode()); |
| | | boolean stopping = Integer.valueOf(4).equals(order.getStatus()); |
| | | if (shouldFallbackSettle(latest, order.getCreateTime(), stopping)) { |
| | | vinChargingOrderService.compensationSettle(order); |
| | | } |
| | | } catch (Exception ex) { |
| | | log.error("[VD补偿] 活动订单处理异常,保留原状态待下轮重试 code={}", order.getCode(), ex); |
| | | } |
| | | } |
| | | |
| | | private TransactionRecord findCompleteTransactionRecord(String code) { |
| | | TransactionRecord record = transactionRecordService.findOne(code); |
| | | return record != null && StringUtils.isNotEmpty(record.getResult()) ? record : null; |
| | | } |
| | | |
| | | private TransactionRecordMessageVO toTransactionRecordVO(TransactionRecord record) { |
| | | TransactionRecordMessageVO vo = new TransactionRecordMessageVO(); |
| | | BeanUtils.copyProperties(record, vo); |
| | | return vo; |
| | | } |
| | | |
| | | static boolean shouldFallbackSettle(UploadRealTimeMonitoringData latest, LocalDateTime createTime, boolean stopping) { |
| | | if (latest != null && latest.getCreate_time() != null) { |
| | | return stopping || System.currentTimeMillis() - latest.getCreate_time().getTime() > MONITORING_SILENT_MILLIS; |
| | | } |
| | | if (stopping) { |
| | | return true; |
| | | } |
| | | return createTime != null |
| | | && ChronoUnit.MILLIS.between(createTime, LocalDateTime.now()) > NO_DEVICE_DATA_TIMEOUT_MILLIS; |
| | | } |
| | | |
| | | |
| | | //十分钟循环执行的定时任务 |
| | | @Scheduled(fixedRate = 1000 * 60 * 10) |
| | | public void taskTenMinutes() { |
| | | if(null != port && port == 5400){ |
| | | if (isScheduledTaskPort(port)) { |
| | | List<TChargingOrder> failedStartupOrder = chargingOrderService.findFailedStartupOrder(); |
| | | log.info("定时任务执行,查询到启动失败的订单数量:{}", failedStartupOrder.size()); |
| | | for (TChargingOrder order : failedStartupOrder) { |
| | | log.info("定时任务执行,查询到启动失败的订单:{}", order.getCode()); |
| | | //查询是否有充电信息 |
| | | List<UploadRealTimeMonitoringData> dataByOrderCode = uploadRealTimeMonitoringDataService.getDataByOrderCode(order.getCode()); |
| | | log.info("充电实时数据:{}", JSON.toJSONString(dataByOrderCode)); |
| | | log.info("充电实时数据:{}", dataByOrderCode.size()); |
| | | //没有充电数据,则执行退款 |
| | | if(null == dataByOrderCode || dataByOrderCode.isEmpty()){ |
| | | log.info("定时任务执行,查询到启动失败的订单,执行退款:{}", order.getCode()); |
| | | chargingOrderService.refund(order.getCode()); |
| | | int num = 0; |
| | | while (true){ |
| | | TChargingOrderRefund one = chargingOrderRefundService.getOne(new LambdaQueryWrapper<TChargingOrderRefund>().eq(TChargingOrderRefund::getChargingOrderId, order.getId())); |
| | | if(null != one && 2 == one.getRefundStatus()){ |
| | | order.setStatus(-1); |
| | | chargingOrderService.updateById(order); |
| | | break; |
| | | if(1 == order.getOrderSource()){ |
| | | chargingOrderService.refund(order.getCode()); |
| | | int num = 0; |
| | | while (true){ |
| | | TChargingOrderRefund one = chargingOrderRefundService.getOne(new LambdaQueryWrapper<TChargingOrderRefund>().eq(TChargingOrderRefund::getChargingOrderId, order.getId())); |
| | | if(null != one && 2 == one.getRefundStatus()){ |
| | | order.setStatus(-1); |
| | | chargingOrderService.updateById(order); |
| | | break; |
| | | } |
| | | try { |
| | | Thread.sleep(1000); |
| | | } catch (InterruptedException e) { |
| | | throw new RuntimeException(e); |
| | | } |
| | | num++; |
| | | if(num > 30){ |
| | | break; |
| | | } |
| | | } |
| | | try { |
| | | Thread.sleep(5000); |
| | | } catch (InterruptedException e) { |
| | | throw new RuntimeException(e); |
| | | } |
| | | num++; |
| | | if(num > 10){ |
| | | break; |
| | | } |
| | | }else{ |
| | | order.setStatus(-1); |
| | | chargingOrderService.updateById(order); |
| | | } |
| | | } |
| | | } |
| | | |
| | | //处理退款中的数据 |
| | | List<TChargingOrder> stoppedOrder = chargingOrderService.findStoppedOrder(); |
| | | log.info("定时任务执行,查询到停止中的订单数量:{}", stoppedOrder.size()); |
| | | for (TChargingOrder order : stoppedOrder) { |
| | | log.info("定时任务执行,查询到停止中的订单:{}", order.getCode()); |
| | | TransactionRecord one = transactionRecordService.findOne(order.getCode()); |
| | | if(null != one){ |
| | | log.info("定时任务执行,查询到停止中的订单账单数据:{}", JSON.toJSONString(one)); |
| | | if(null == order.getStartTime()){ |
| | | List<UploadRealTimeMonitoringData> dataList = uploadRealTimeMonitoringDataService.getDataByOrderCode(order.getCode()); |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | if(dataList.isEmpty()){ |
| | | order.setStartTime(LocalDateTime.parse(one.getStart_time(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SS").withZone(ZoneId.systemDefault()))); |
| | | }else{ |
| | | order.setStartTime(LocalDateTime.parse(sdf.format(dataList.get(dataList.size() - 1).getCreate_time()), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault()))); |
| | | } |
| | | } |
| | | if(null == order.getEndTime()){ |
| | | order.setEndTime(LocalDateTime.parse(one.getEnd_time(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SS").withZone(ZoneId.systemDefault()))); |
| | | } |
| | | chargingOrderService.updateById(order); |
| | | TransactionRecordMessageVO vo = new TransactionRecordMessageVO(); |
| | | BeanUtils.copyProperties(one, vo); |
| | | R r = chargingOrderService.endChargeBillingCharge(vo); |
| | | log.info("定时任务执行,停止中的订单处理结果:{}", JSON.toJSONString(r)); |
| | | }else{ |
| | | endOrder(order); |
| | | } |
| | | } |
| | | |
| | | |
| | | //处理状态为充电中,但硬件已完成的订单 |
| | | List<TChargingOrder> chargingOrder = chargingOrderService.findChargingOrder(); |
| | | log.info("定时任务执行,查询到充电中的订单数量:{}", chargingOrder.size()); |
| | | for (TChargingOrder order : chargingOrder) { |
| | | log.info("定时任务执行,查询到充电中的订单:{}", order.getCode()); |
| | | TransactionRecord one = transactionRecordService.findOne(order.getCode()); |
| | | if(null != one && StringUtils.isNotEmpty(one.getResult())){ |
| | | log.info("定时任务执行,查询到充电中的订单账单数据:{}", JSON.toJSONString(one)); |
| | | if(null == order.getStartTime()){ |
| | | List<UploadRealTimeMonitoringData> dataList = uploadRealTimeMonitoringDataService.getDataByOrderCode(order.getCode()); |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | if(dataList.isEmpty()){ |
| | | order.setStartTime(LocalDateTime.parse(one.getStart_time(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SS").withZone(ZoneId.systemDefault()))); |
| | | }else{ |
| | | order.setStartTime(LocalDateTime.parse(sdf.format(dataList.get(dataList.size() - 1).getCreate_time()), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault()))); |
| | | } |
| | | } |
| | | if(null == order.getEndTime()){ |
| | | order.setEndTime(LocalDateTime.parse(one.getEnd_time(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SS").withZone(ZoneId.systemDefault()))); |
| | | } |
| | | chargingOrderService.updateById(order); |
| | | TransactionRecordMessageVO vo = new TransactionRecordMessageVO(); |
| | | BeanUtils.copyProperties(one, vo); |
| | | R r = chargingOrderService.endChargeBillingCharge(vo); |
| | | log.info("定时任务执行,充电中的订单处理结果:{}", JSON.toJSONString(r)); |
| | | }else{ |
| | | //判断充电流水超过10分钟没有更新,则判定为充电结束 |
| | | List<UploadRealTimeMonitoringData> dataList = uploadRealTimeMonitoringDataService.getDataByOrderCode(order.getCode()); |
| | | if(!dataList.isEmpty()){ |
| | | UploadRealTimeMonitoringData uploadRealTimeMonitoringData = dataList.get(0); |
| | | Date createTime = uploadRealTimeMonitoringData.getCreate_time(); |
| | | if(System.currentTimeMillis() - createTime.getTime() - (8 * 60 * 60 * 1000) > 1000 * 60 * 10){ |
| | | endOrder(order); |
| | | } |
| | | } |
| | | } |
| | | |
| | | //推送监管平台订单状态和订单详情 |
| | | ChargingOrderVo chargingOrderVo = new ChargingOrderVo(); |
| | | BeanUtils.copyProperties(order, chargingOrderVo); |
| | | chargingMessageClient.pushOrderInfo(chargingOrderVo); |
| | | chargingMessageClient.pushOrderStatus(chargingOrderVo); |
| | | } |
| | | |
| | | //处理还未退款,但是订单状态已结束的数据 |
| | | List<TChargingOrder> endChargingOrder = chargingOrderService.findEndChargingOrder(); |
| | | log.info("定时任务执行,查询到已结束未退款的订单数量:{}", endChargingOrder.size()); |
| | | for (TChargingOrder order : endChargingOrder) { |
| | | if(-1 == order.getStatus()){ |
| | | order.setStatus(2); |
| | | } |
| | | if(5 == order.getStatus()){ |
| | | order.setStatus(4); |
| | | } |
| | | chargingOrderService.updateById(order); |
| | | } |
| | | |
| | | } |
| | | } |
| | | |
| | | |
| | | @Scheduled(cron = "0 0 0 2 * ?") |
| | | |
| | | //定时任务添加结算账单汇总数据 |
| | | @Scheduled(cron = "0 30 0 * * ?") |
| | | public void taskMonth() { |
| | | if(null != port && port == 5400){ |
| | | try { |
| | | // 获取上个月的开始和结束日期 |
| | | LocalDate firstDayOfLastMonth = LocalDate.now().minusMonths(1). |
| | | withDayOfMonth(2); |
| | | String string = firstDayOfLastMonth.toString(); |
| | | // 将-替换为空字符串 |
| | | string = string.replace("-", ""); |
| | | // 生成一次全站订单 |
| | | TChargingBill tChargingBill = new TChargingBill(); |
| | | // 订单生成规则JSD+20231201(账单所属月份)+1131304205(随机10位数)+001(当月账单序号,每月重置) |
| | | Random random = new Random(); |
| | | String randomDigits = random.ints(10, 0, 10) // 生成10个随机数字,范围在0-9 |
| | | .mapToObj(String::valueOf) |
| | | .collect(Collectors.joining()); // 将其连接成一个字符串 |
| | | tChargingBill.setCode("JSD"+string+randomDigits ); |
| | | tChargingBill.setType(1); |
| | | tChargingBill.setSiteId(0); |
| | | tChargingBill.setBillTime(LocalDateTime.now()); |
| | | tChargingBill.setStatus(1); |
| | | tChargingBill.setOrderState(2); |
| | | tChargingBill.setBillType(1); |
| | | System.err.println("定时任务生成充电算帐单:全站"); |
| | | chargingBillService.save(tChargingBill); |
| | | // 生成一次全站订单 |
| | | TChargingBill tChargingBill1 = new TChargingBill(); |
| | | // 订单生成规则JSD+20231201(账单所属月份)+1131304205(随机10位数)+001(当月账单序号,每月重置) |
| | | Random random1 = new Random(); |
| | | String randomDigits1 = random1.ints(10, 0, 10) // 生成10个随机数字,范围在0-9 |
| | | .mapToObj(String::valueOf) |
| | | .collect(Collectors.joining()); // 将其连接成一个字符串 |
| | | tChargingBill1.setCode("JSD"+string+randomDigits1 ); |
| | | tChargingBill1.setType(1); |
| | | tChargingBill1.setSiteId(0); |
| | | tChargingBill1.setBillTime(LocalDateTime.now()); |
| | | tChargingBill1.setStatus(1); |
| | | tChargingBill1.setOrderState(2); |
| | | tChargingBill1.setBillType(2); |
| | | System.err.println("定时任务生成账户结算帐单:全站"); |
| | | chargingBillService.save(tChargingBill1); |
| | | System.err.println("定时任务生成各个站点结算帐单"); |
| | | List<Integer> collect = siteClient.getSiteAll().getData().stream().map(Site::getId).collect(Collectors.toList()); |
| | | // 充电算帐单 |
| | | List<TChargingBill> tChargingBills = new ArrayList<>(); |
| | | // 账户结算账单 |
| | | List<TChargingBill> tChargingBills1 = new ArrayList<>(); |
| | | for (int i = 0; i < collect.size(); i++) { |
| | | TChargingBill tChargingBill2 = new TChargingBill(); |
| | | Random random2 = new Random(); |
| | | String randomDigits2 = random2.ints(10, 0, 10) // 生成10个随机数字,范围在0-9 |
| | | .mapToObj(String::valueOf) |
| | | .collect(Collectors.joining()); // 将其连接成一个字符串 |
| | | tChargingBill2.setCode("JSD"+string+randomDigits2+(i+1) ); |
| | | tChargingBill2.setType(2); |
| | | List<TChargingOrder> list = chargingOrderService.lambdaQuery() |
| | | .eq(TChargingOrder::getSiteId, collect.get(i)).list(); |
| | | if (list.isEmpty()){ |
| | | continue; |
| | | } |
| | | tChargingBill2.setSiteId(collect.get(i)); |
| | | tChargingBill2.setBillTime(LocalDateTime.now()); |
| | | tChargingBill2.setBillType(1); |
| | | tChargingBill2.setStatus(1); |
| | | tChargingBill2.setOrderState(2); |
| | | tChargingBills.add(tChargingBill2); |
| | | TChargingBill tChargingBill3 = new TChargingBill(); |
| | | Random random3 = new Random(); |
| | | String randomDigits3 = random3.ints(10, 0, 10) // 生成10个随机数字,范围在0-9 |
| | | .mapToObj(String::valueOf) |
| | | .collect(Collectors.joining()); // 将其连接成一个字符串 |
| | | tChargingBill3.setCode("JSD"+string+randomDigits3+(i+1) ); |
| | | tChargingBill3.setType(2); |
| | | tChargingBill3.setSiteId(collect.get(i)); |
| | | tChargingBill3.setBillTime(LocalDateTime.now()); |
| | | tChargingBill3.setBillType(2); |
| | | tChargingBill3.setStatus(1); |
| | | tChargingBill3.setOrderState(2); |
| | | tChargingBills1.add(tChargingBill3); |
| | | } |
| | | System.err.println("列表"+tChargingBills); |
| | | if (!tChargingBills.isEmpty())chargingBillService.saveBatch(tChargingBills); |
| | | if (!tChargingBills1.isEmpty())chargingBillService.saveBatch(tChargingBills1); |
| | | |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | if (isScheduledTaskPort(port)) { |
| | | //推送三方引流平台数据 |
| | | pushOrderDrainagePlatform(); |
| | | //添加充电算账单汇总数据 |
| | | addChargingSettlementBill(); |
| | | //添加账户结算账单数据 |
| | | addAccountSettlementStatement(); |
| | | } |
| | | } |
| | | |
| | | // 每个月最后一天23点执行的定时任务 |
| | | @Scheduled(cron = "0 0 23 * * ?") |
| | | public void taskLastDay() { |
| | | if(null != port && port == 5400){ |
| | | if (isScheduledTaskPort(port)) { |
| | | try { |
| | | // 获取上个月的开始和结束日期 |
| | | LocalDate firstDayOfLastMonth = LocalDate.now().minusMonths(1). |
| | | withDayOfMonth(1); |
| | | LocalDate lastDayOfLastMonth = LocalDate.now().minusMonths(1). |
| | | withDayOfMonth(firstDayOfLastMonth.lengthOfMonth()); |
| | | // 将 LocalDate 转换为 LocalDateTime,并设定时间为一天的开始 |
| | | LocalDateTime startDateTime = firstDayOfLastMonth.atStartOfDay(); |
| | | LocalDateTime endDateTime = lastDayOfLastMonth.atTime(23, 59, 59); // 设定到最后一秒 |
| | | // 构建查询条件 |
| | | LambdaQueryWrapper<TChargingBill> queryWrapper = new LambdaQueryWrapper<>(); |
| | | queryWrapper.ge(TChargingBill::getBillTime, |
| | | Date.from(startDateTime.atZone(ZoneId.systemDefault()).toInstant())) |
| | | .le(TChargingBill::getBillTime, |
| | | Date.from(endDateTime.atZone(ZoneId.systemDefault()).toInstant())); // 使用 le 包括最后一天的记录 |
| | | List<TChargingBill> list = chargingBillService.list(queryWrapper); |
| | | for (TChargingBill tChargingBill : list) { |
| | | tChargingBill.setStatus(2); |
| | | int dayOfMonth = LocalDate.now().getDayOfMonth(); |
| | | int lastDayOfMonth = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth(); |
| | | //当月最后一天 |
| | | if(dayOfMonth == lastDayOfMonth){ |
| | | //修改充电结算账单为已出账 |
| | | chargingSettlementBillService.update(new UpdateWrapper<TChargingSettlementBill>().set("status", 2).set("create_time", LocalDateTime.now()).eq("status", 1)); |
| | | //修改账户结算账单为已出账 |
| | | accountSettlementStatementService.update(new UpdateWrapper<TAccountSettlementStatement>().set("status", 2).set("create_time", LocalDateTime.now()).eq("status", 1)); |
| | | } |
| | | chargingBillService.updateBatchById(list); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | @Override |
| | | public void onApplicationEvent(WebServerInitializedEvent event) { |
| | | port = event.getWebServer().getPort(); |
| | | System.out.println("端口号:" + port); |
| | | } |
| | | |
| | | static boolean isScheduledTaskPort(Integer serverPort) { |
| | | return Integer.valueOf(5400).equals(serverPort) || Integer.valueOf(9400).equals(serverPort); |
| | | } |
| | | |
| | | |
| | | |
| | | @Transactional |
| | | public void endOrder(TChargingOrder order){ |
| | | RLock lock = redissonClient.getLock("endOrder:" + order.getCode()); |
| | | try { |
| | | boolean tryLock = lock.tryLock(60, 60, TimeUnit.SECONDS); |
| | | if(tryLock){ |
| | | List<UploadRealTimeMonitoringData> dataList = uploadRealTimeMonitoringDataService.getDataByOrderCode(order.getCode()); |
| | | if(!dataList.isEmpty()){ |
| | | //获取当前订单的计费规则,然后分段计算总的度数和充电金额 |
| | | List<AccountingStrategyDetailOrder> detailOrders = accountingStrategyDetailOrderService.list(new LambdaQueryWrapper<AccountingStrategyDetailOrder>() |
| | | .eq(AccountingStrategyDetailOrder::getChargingOrderId, order.getId()).last(" order by start_time")); |
| | | detailOrders.get(detailOrders.size() - 1).setEndTime("23:59"); |
| | | SimpleDateFormat sdf = new SimpleDateFormat("HH:mm"); |
| | | //开始解析每个时段的充电度数 |
| | | BigDecimal decimal = BigDecimal.ZERO; |
| | | //如果使用优惠券需要判断优惠券是否满足使用条件 |
| | | //退款金额=优惠券金额+剩余充电金额 |
| | | BigDecimal periodElectricPrice_total = BigDecimal.ZERO; |
| | | BigDecimal periodServicePrice_total = BigDecimal.ZERO; |
| | | BigDecimal vipDiscountAmount_total = BigDecimal.ZERO; |
| | | BigDecimal serviceCharge_total = BigDecimal.ZERO; |
| | | BigDecimal total = BigDecimal.ZERO; |
| | | //判断实时数据是否跨天 |
| | | SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd"); |
| | | Date end_time = dataList.get(0).getCreate_time(); |
| | | String end = sdf2.format(end_time); |
| | | Date start_time = dataList.get(dataList.size() - 1).getCreate_time(); |
| | | String start = sdf2.format(start_time); |
| | | if(!end.equals(start)){ |
| | | List<AccountingStrategyDetailOrder> list = detailOrders.stream().map(s->{ |
| | | AccountingStrategyDetailOrder accountingStrategyDetailOrder = new AccountingStrategyDetailOrder(); |
| | | BeanUtils.copyProperties(s, accountingStrategyDetailOrder); |
| | | return accountingStrategyDetailOrder; |
| | | }).collect(Collectors.toList()); |
| | | for (AccountingStrategyDetailOrder detailOrder : detailOrders) { |
| | | detailOrder.setStartTime(start + " " + detailOrder.getStartTime()); |
| | | detailOrder.setEndTime(start + " " + detailOrder.getEndTime()); |
| | | } |
| | | for (AccountingStrategyDetailOrder detailOrder : list) { |
| | | detailOrder.setStartTime(end + " " + detailOrder.getStartTime()); |
| | | detailOrder.setEndTime(end + " " + detailOrder.getEndTime()); |
| | | } |
| | | detailOrders.addAll(list); |
| | | }else{ |
| | | for (AccountingStrategyDetailOrder detailOrder : detailOrders) { |
| | | detailOrder.setStartTime(start + " " + detailOrder.getStartTime()); |
| | | detailOrder.setEndTime(start + " " + detailOrder.getEndTime()); |
| | | } |
| | | } |
| | | |
| | | //企业账户流程:白名单企业账户订单按企业优惠结算(方式2=服务费比例优惠,方式3=按计费模板同阶段单价) |
| | | boolean enterpriseSettlement = chargingOrderService.isWhitelistEnterpriseOrder(order); |
| | | CompanyPreferentialDTO enterprisePreferential = enterpriseSettlement |
| | | ? chargeAccountSupport.loadCompanyPreferential(order.getCompanyId()) : null; |
| | | Integer enterprisePreferentialType = null == enterprisePreferential ? null : enterprisePreferential.getPreferentialType(); |
| | | TBillingTemplateDTO enterpriseTemplate = enterpriseSettlement |
| | | ? chargeAccountSupport.loadBillingTemplate(enterprisePreferential) : null; |
| | | BigDecimal enterpriseElectricDiscount = BigDecimal.ZERO; |
| | | BigDecimal enterpriseServiceDiscount = BigDecimal.ZERO; |
| | | |
| | | SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMMddHHmm"); |
| | | List<TChargingOrderAccountingStrategy> list = new ArrayList<>(); |
| | | for (AccountingStrategyDetailOrder detailOrder : detailOrders) { |
| | | long startTime = Long.parseLong(detailOrder.getStartTime().replaceAll("-", "").replaceAll(" ", "").replaceAll(":", "")); |
| | | long endTime = Long.parseLong(detailOrder.getEndTime().replaceAll("-", "").replaceAll(" ", "").replaceAll(":", "")); |
| | | List<UploadRealTimeMonitoringData> collect = dataList.stream().filter(s -> Long.parseLong(sdf1.format(s.getCreate_time())) > startTime && Long.parseLong(sdf1.format(s.getCreate_time())) <= endTime).collect(Collectors.toList()); |
| | | |
| | | if(!collect.isEmpty()){ |
| | | //升序排序 |
| | | collect.sort(new Comparator<UploadRealTimeMonitoringData>(){ |
| | | @Override |
| | | public int compare(UploadRealTimeMonitoringData o1, UploadRealTimeMonitoringData o2) { |
| | | return o1.getCharging_degree().compareTo(o2.getCharging_degree()); |
| | | } |
| | | }); |
| | | UploadRealTimeMonitoringData uploadRealTimeMonitoringData = collect.get(collect.size() - 1); |
| | | BigDecimal chargingDegree = uploadRealTimeMonitoringData.getCharging_degree(); |
| | | BigDecimal subtract = chargingDegree.subtract(decimal); |
| | | decimal = chargingDegree; |
| | | if(subtract.compareTo(BigDecimal.ZERO) > 0){ |
| | | //组装充电明细数据 |
| | | TChargingOrderAccountingStrategy chargingOrderAccountingStrategy = new TChargingOrderAccountingStrategy(); |
| | | chargingOrderAccountingStrategy.setChargingOrderId(order.getId()); |
| | | chargingOrderAccountingStrategy.setAccountingStrategyDetailId(detailOrder.getId()); |
| | | chargingOrderAccountingStrategy.setType(detailOrder.getType()); |
| | | chargingOrderAccountingStrategy.setElectrovalence(detailOrder.getElectrovalence()); |
| | | chargingOrderAccountingStrategy.setServiceCharge(detailOrder.getServiceCharge()); |
| | | chargingOrderAccountingStrategy.setCostServiceCharge(detailOrder.getCostServiceCharge()); |
| | | chargingOrderAccountingStrategy.setChargingCapacity(subtract); |
| | | chargingOrderAccountingStrategy.setCreateTime(LocalDateTime.now()); |
| | | |
| | | if (list.size() == 0) { |
| | | String time = sdf.format(collect.get(0).getCreate_time()); |
| | | chargingOrderAccountingStrategy.setStartTime(time); |
| | | } else { |
| | | chargingOrderAccountingStrategy.setStartTime(detailOrder.getStartTime().split(" ")[1]); |
| | | } |
| | | chargingOrderAccountingStrategy.setEndTime(detailOrder.getEndTime().split(" ")[1]); |
| | | |
| | | //已充电总度数 |
| | | BigDecimal finalElectricPrice = detailOrder.getElectrovalence(); |
| | | BigDecimal finalServicePrice = detailOrder.getServiceCharge(); |
| | | //企业账户流程:按企业优惠计算最终用电单价 |
| | | if (enterpriseSettlement) { |
| | | if (Integer.valueOf(ChargeAccountSupport.PREFERENTIAL_TYPE_RATE).equals(enterprisePreferentialType)) { |
| | | //方式2:服务费按比例优惠 |
| | | finalServicePrice = ChargeAccountSupport.applyServiceDiscountRate(finalServicePrice, enterprisePreferential.getServiceDiscountRate()); |
| | | } else if (Integer.valueOf(ChargeAccountSupport.PREFERENTIAL_TYPE_TEMPLATE).equals(enterprisePreferentialType)) { |
| | | //方式3:按计费模板同电价阶段单价 |
| | | TBillingTemplateDetail templateDetail = ChargeAccountSupport.findTemplateDetail(enterpriseTemplate, detailOrder.getType()); |
| | | if (null == templateDetail) { |
| | | log.error("[CD补偿结算] 计费模板缺少对应电价阶段,该阶段按原价结算 code={} type={}", order.getCode(), detailOrder.getType()); |
| | | } else { |
| | | if (null != templateDetail.getElectrovalence()) { |
| | | finalElectricPrice = templateDetail.getElectrovalence(); |
| | | } |
| | | if (null != templateDetail.getServiceCharge()) { |
| | | finalServicePrice = templateDetail.getServiceCharge(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | BigDecimal electrovalenc = finalElectricPrice.multiply(subtract); |
| | | BigDecimal originalServicePrice = detailOrder.getServiceCharge().multiply(subtract); |
| | | BigDecimal serviceCharge = finalServicePrice.multiply(subtract); |
| | | BigDecimal vipDiscountAmount = BigDecimal.ZERO; |
| | | //计算优惠金额;企业账户流程按企业优惠结算,其余订单中余额支付(含组合支付)不参与会员折扣 |
| | | if (enterpriseSettlement) { |
| | | //企业优惠金额(原价-优惠价),用于订单优惠统计与 whitelist_* 快照 |
| | | enterpriseElectricDiscount = enterpriseElectricDiscount.add(detailOrder.getElectrovalence().multiply(subtract).subtract(electrovalenc)); |
| | | enterpriseServiceDiscount = enterpriseServiceDiscount.add(originalServicePrice.subtract(serviceCharge)); |
| | | } else if (!isWhitelistOrder(order) && !isBalancePayMode(order.getPayMethod()) && null != order.getVipDiscount()) { |
| | | //0.58折 |
| | | vipDiscountAmount = serviceCharge.multiply(new BigDecimal(1).subtract(order.getVipDiscount())); |
| | | serviceCharge = serviceCharge.multiply(order.getVipDiscount()); |
| | | } |
| | | //企业账户流程:明细单价记录优惠后单价,金额与单价口径一致 |
| | | if (enterpriseSettlement) { |
| | | chargingOrderAccountingStrategy.setElectrovalence(finalElectricPrice); |
| | | chargingOrderAccountingStrategy.setServiceCharge(finalServicePrice); |
| | | } |
| | | chargingOrderAccountingStrategy.setPeriodElectricPrice(electrovalenc.setScale(4, RoundingMode.HALF_EVEN)); |
| | | chargingOrderAccountingStrategy.setPeriodServicePrice(serviceCharge.setScale(4, RoundingMode.HALF_EVEN)); |
| | | chargingOrderAccountingStrategy.setPeriodOriginalServicePrice(originalServicePrice.setScale(4, RoundingMode.HALF_EVEN)); |
| | | chargingOrderAccountingStrategy.setVipDiscountAmount(vipDiscountAmount.setScale(4, RoundingMode.HALF_EVEN)); |
| | | chargingOrderAccountingStrategy.setCreateTime(LocalDateTime.now()); |
| | | list.add(chargingOrderAccountingStrategy); |
| | | //电费 |
| | | periodElectricPrice_total = periodElectricPrice_total.add(electrovalenc); |
| | | //服务费(含折扣) |
| | | serviceCharge_total = serviceCharge_total.add(serviceCharge); |
| | | //服务费(不含折扣) |
| | | periodServicePrice_total = periodServicePrice_total.add(originalServicePrice); |
| | | //会员折扣优惠金额 |
| | | vipDiscountAmount_total = vipDiscountAmount_total.add(vipDiscountAmount); |
| | | //原始总金额(不含折扣) |
| | | total = total.add(electrovalenc.add(originalServicePrice)); |
| | | } |
| | | } |
| | | |
| | | } |
| | | list.get(list.size() - 1).setEndTime(sdf.format(dataList.get(0).getCreate_time())); |
| | | chargingOrderAccountingStrategyService.saveBatch(list); |
| | | |
| | | BigDecimal orderAmount = BigDecimal.valueOf(total.doubleValue()); |
| | | //原金额 |
| | | BigDecimal rechargeAmount = order.getRechargeAmount(); |
| | | //支付金额 |
| | | BigDecimal payAmount = periodElectricPrice_total.add(serviceCharge_total); |
| | | //退款金额=充值金额-实际支付金额 |
| | | BigDecimal refundAmount = rechargeAmount.subtract(payAmount); |
| | | |
| | | UploadRealTimeMonitoringData uploadRealTimeMonitoringData = uploadRealTimeMonitoringDataService.getLastDataById(order.getCode()); |
| | | if (null != uploadRealTimeMonitoringData && null == order.getEndMode()) { |
| | | Integer soc = uploadRealTimeMonitoringData.getSoc(); |
| | | if (soc >= 98) { |
| | | order.setEndMode(2); |
| | | } else if (null != order.getResidualAmount() && order.getResidualAmount().compareTo(new BigDecimal(1)) <= 0) { |
| | | order.setEndMode(3); |
| | | } else { |
| | | order.setEndMode(1); |
| | | } |
| | | } |
| | | |
| | | order.setResidualAmount(rechargeAmount.subtract(total).setScale(2, RoundingMode.HALF_EVEN)); |
| | | order.setStartTime(list.get(0).getCreateTime()); |
| | | order.setEndTime(list.get(list.size() - 1).getCreateTime()); |
| | | order.setStatus(5); |
| | | order.setOrderAmount(orderAmount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | order.setElectrovalence(periodElectricPrice_total.setScale(2, RoundingMode.HALF_EVEN)); |
| | | order.setChargingCapacity(list.get(list.size() - 1).getChargingCapacity()); |
| | | order.setElectricity(list.get(list.size() - 1).getChargingCapacity()); |
| | | order.setVipDiscountAmount(vipDiscountAmount_total); |
| | | if (vipDiscountAmount_total.compareTo(BigDecimal.ZERO) == 0) { |
| | | order.setVipDiscount(BigDecimal.valueOf(1)); |
| | | } |
| | | |
| | | //计算优惠券 |
| | | BigDecimal couponDiscount = BigDecimal.ZERO; |
| | | if (!isWhitelistOrder(order) && null != order.getAppCouponId()) { |
| | | //判断实际充电金额是否满足优惠券使用条件,如果不满足则不适用优惠券。 |
| | | TAppCoupon appCoupon = appCouponClient.getAppCouponById(order.getAppCouponId()).getData(); |
| | | String couponJson = appCoupon.getCouponJson(); |
| | | TCoupon tCoupon = com.alibaba.fastjson2.JSON.parseObject(couponJson, TCoupon.class); |
| | | Integer preferentialMode = tCoupon.getPreferentialMode(); |
| | | if (1 == preferentialMode) { |
| | | //满减 |
| | | if (payAmount.compareTo(tCoupon.getMeetTheConditions()) >= 0) { |
| | | BigDecimal couponDiscountAmount = tCoupon.getDiscountAmount(); |
| | | //如果优惠金额大于服务费金额,以服务费作为最大限制 |
| | | if (serviceCharge_total.compareTo(couponDiscountAmount) < 0) { |
| | | couponDiscount = serviceCharge_total; |
| | | serviceCharge_total = BigDecimal.ZERO; |
| | | } else { |
| | | couponDiscount = couponDiscountAmount; |
| | | } |
| | | appCoupon.setStatus(2); |
| | | appCouponClient.updateAppCoupon(appCoupon); |
| | | } else { |
| | | appCouponClient.refund(order.getAppCouponId().toString()); |
| | | order.setAppCouponId(null); |
| | | order.setCouponDiscountAmount(BigDecimal.ZERO); |
| | | } |
| | | } |
| | | if (2 == preferentialMode) { |
| | | //抵扣 |
| | | if (payAmount.compareTo(tCoupon.getMeetTheConditions()) >= 0) { |
| | | //折扣金额 |
| | | BigDecimal divide = payAmount.multiply(new BigDecimal(10).subtract(tCoupon.getDiscount())).divide(new BigDecimal(10)); |
| | | divide = divide.compareTo(tCoupon.getMaximumDiscountAmount()) > 0 ? tCoupon.getMaximumDiscountAmount() : divide; |
| | | //如果优惠金额大于服务费金额,以服务费作为最大限制 |
| | | if (serviceCharge_total.compareTo(divide) < 0) { |
| | | couponDiscount = serviceCharge_total; |
| | | serviceCharge_total = BigDecimal.ZERO; |
| | | } else { |
| | | couponDiscount = divide; |
| | | } |
| | | |
| | | appCoupon.setStatus(2); |
| | | appCouponClient.updateAppCoupon(appCoupon); |
| | | } else { |
| | | order.setAppCouponId(null); |
| | | order.setCouponDiscountAmount(BigDecimal.ZERO); |
| | | appCouponClient.refund(order.getAppCouponId().toString()); |
| | | } |
| | | } |
| | | } |
| | | BigDecimal[] whitelistRefundSplit = calculateWhitelistRefundSplit(order, periodElectricPrice_total, serviceCharge_total); |
| | | BigDecimal electricDiscount = whitelistRefundSplit[0]; |
| | | BigDecimal serviceDiscount = whitelistRefundSplit[1]; |
| | | BigDecimal whitelistDiscount = electricDiscount.add(serviceDiscount).setScale(4, RoundingMode.HALF_EVEN); |
| | | if (enterpriseSettlement) { |
| | | // 企业账户流程:企业优惠已在明细阶段计入金额,这里只做统计快照(金额口径与实时结算一致) |
| | | electricDiscount = enterpriseElectricDiscount.max(BigDecimal.ZERO).setScale(4, RoundingMode.HALF_EVEN); |
| | | serviceDiscount = enterpriseServiceDiscount.max(BigDecimal.ZERO).setScale(4, RoundingMode.HALF_EVEN); |
| | | whitelistDiscount = electricDiscount.add(serviceDiscount); |
| | | } else if (whitelistDiscount.compareTo(BigDecimal.ZERO) > 0) { |
| | | // 补偿任务需与实时结算使用同一拆分口径,避免财务导出金额不一致。 |
| | | periodElectricPrice_total = periodElectricPrice_total.subtract(electricDiscount).max(BigDecimal.ZERO); |
| | | serviceCharge_total = serviceCharge_total.subtract(serviceDiscount).max(BigDecimal.ZERO); |
| | | refundAmount = refundAmount.add(whitelistDiscount); |
| | | payAmount = payAmount.subtract(whitelistDiscount).max(BigDecimal.ZERO); |
| | | } |
| | | //优惠券优惠金额 |
| | | 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); |
| | | } |
| | | order.setCouponDiscountAmount(couponDiscount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | //企业账户流程:企业优惠金额与 whitelist_* 三列快照双写(订单优惠统计沿用 whitelist_* 口径) |
| | | if (enterpriseSettlement) { |
| | | order.setDiscountElectrovalence(electricDiscount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | order.setDiscountServiceCharge(serviceDiscount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | order.setWhitelistElectricRefundAmount(electricDiscount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | order.setWhitelistServiceRefundAmount(serviceDiscount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | order.setWhitelistDiscountAmount(whitelistDiscount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | } else { |
| | | order.setWhitelistDiscountAmount(whitelistDiscount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | order.setWhitelistElectricRefundAmount(electricDiscount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | order.setWhitelistServiceRefundAmount(serviceDiscount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | } |
| | | order.setElectrovalence(periodElectricPrice_total.setScale(2, RoundingMode.HALF_EVEN)); |
| | | order.setServiceCharge(serviceCharge_total.setScale(2, RoundingMode.HALF_EVEN)); |
| | | if (enterpriseSettlement) { |
| | | // 企业账户流程:资金来自企业账户额度(差额结算时退回企业账户),订单无用户侧退款,实付金额=实际消费金额 |
| | | order.setBalancePayAmount(payAmount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | order.setPaymentAmount(payAmount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | order.setRefundAmount(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_EVEN)); |
| | | order.setRefundStatus(2); |
| | | refundAmount = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_EVEN); |
| | | } else { |
| | | order.setPaymentAmount(payAmount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | order.setRefundAmount(refundAmount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | order.setRefundStatus(1); |
| | | } |
| | | UploadRealTimeMonitoringData asc = uploadRealTimeMonitoringDataService.getDataByOrderCode(order.getCode(), "asc"); |
| | | UploadRealTimeMonitoringData desc = uploadRealTimeMonitoringDataService.getDataByOrderCode(order.getCode(), "desc"); |
| | | if(null != asc){ |
| | | order.setStartSoc(new BigDecimal(asc.getSoc())); |
| | | } |
| | | if(null != desc){ |
| | | order.setEndSoc(new BigDecimal(desc.getSoc())); |
| | | } |
| | | chargingOrderService.updateById(order); |
| | | |
| | | //企业账户流程:结算企业额度(按实际消费扣减锁定额度、差额退回企业账户)并写消费流水 |
| | | if (enterpriseSettlement && !chargingOrderService.settleWhitelistEnterpriseAccount(order, payAmount)) { |
| | | // 结算失败回退为停止中,交由补偿任务下一轮重新结算,避免企业额度长期冻结 |
| | | order.setStatus(4); |
| | | chargingOrderService.updateById(order); |
| | | log.error("[CD补偿结算] 企业账户额度结算失败,订单回退停止中等待重试 code={}", order.getCode()); |
| | | return; |
| | | } |
| | | |
| | | order = chargingOrderService.getById(order.getId()); |
| | | Site site = siteClient.getSiteByIds(Arrays.asList(order.getSiteId())).getData().get(0); |
| | | //推送三方平台 |
| | | if (2 == order.getOrderSource() && 1 == site.getIsOpenSynchronization()) { |
| | | TChargingOrder finalChargingOrder = order; |
| | | new Thread(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | tcecClient.notificationEquipChargeStatus(finalChargingOrder.getStartChargeSeq(), finalChargingOrder.getOperatorId()); |
| | | tcecClient.notificationStopChargeResult(finalChargingOrder.getStartChargeSeq(), finalChargingOrder.getChargingGunId().toString(), |
| | | finalChargingOrder.getOperatorId()); |
| | | tcecClient.notificationChargeOrderInfo(finalChargingOrder.getStartChargeSeq(), finalChargingOrder.getOperatorId()); |
| | | } |
| | | }).start(); |
| | | } |
| | | |
| | | //开始将优惠券优惠的金额添加到明细中 |
| | | BigDecimal couponDiscountAmount = order.getCouponDiscountAmount(); |
| | | if (null != couponDiscountAmount && couponDiscountAmount.compareTo(BigDecimal.ZERO) > 0) { |
| | | List<TChargingOrderAccountingStrategy> list1 = chargingOrderAccountingStrategyService.list(new LambdaQueryWrapper<TChargingOrderAccountingStrategy>().eq(TChargingOrderAccountingStrategy::getChargingOrderId, order.getId())); |
| | | BigDecimal reduce = list1.stream().map(TChargingOrderAccountingStrategy::getPeriodServicePrice).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | for (TChargingOrderAccountingStrategy chargingOrderAccountingStrategy : list1) { |
| | | BigDecimal periodServicePrice = chargingOrderAccountingStrategy.getPeriodServicePrice(); |
| | | BigDecimal multiply = couponDiscountAmount.multiply(periodServicePrice.divide(reduce, new MathContext(4, RoundingMode.HALF_EVEN))); |
| | | periodServicePrice = periodServicePrice.subtract(multiply); |
| | | chargingOrderAccountingStrategy.setPeriodServicePrice(periodServicePrice.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingOrderAccountingStrategy.setCouponDiscountAmount(multiply.setScale(2, RoundingMode.HALF_EVEN)); |
| | | } |
| | | chargingOrderAccountingStrategyService.updateBatchById(list1); |
| | | } |
| | | |
| | | // 将枪状态重置为空闲 |
| | | TChargingGun chargingGun = new TChargingGun(); |
| | | chargingGun.setId(order.getChargingGunId()); |
| | | chargingGun.setStatus(2); |
| | | chargingGun.setChargingPower(BigDecimal.ZERO); |
| | | chargingGunClient.updateChargingGunById(chargingGun); |
| | | chargingGun = chargingGunClient.getChargingGunById(order.getChargingGunId()).getData(); |
| | | //推送状态给三方平台 |
| | | if (2 == order.getOrderSource() && 1 == site.getIsOpenSynchronization()) { |
| | | tcecClient.pushChargingGunStatus(chargingGun.getFullNumber(), 2); |
| | | } |
| | | ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); |
| | | TChargingOrder finalOrder = order; |
| | | threadPoolExecutor.execute(()->{ |
| | | //推送成都监管 |
| | | NotificationConnectorChargeStatusVo statusVo = new NotificationConnectorChargeStatusVo(); |
| | | statusVo.setOrderCode(finalOrder.getCode()); |
| | | statusVo.setChargingGunId(finalOrder.getChargingGunId()); |
| | | chengDuJianGuanClient.notificationConnectorChargeStatus(statusVo); |
| | | chengDuJianGuanClient.notificationOrderInfo(statusVo); |
| | | }); |
| | | |
| | | |
| | | //添加积分 |
| | | if (1 == order.getOrderSource()) { |
| | | TIntegralRule integralRule = integralRuleClient.getSet().getData(); |
| | | if (null != integralRule) { |
| | | TAppUser appUser = appUserClient.getUserById(order.getAppUserId()).getData(); |
| | | Integer num1 = com.alibaba.fastjson2.JSON.parseObject(integralRule.getChargeCredit()).getInteger("num1"); |
| | | Integer integral = order.getServiceCharge().intValue() * num1; |
| | | if (null != appUser.getVipId()) { |
| | | TVip vip = vipClient.getInfo1(appUser.getVipId()).getData(); |
| | | Integer doubleIntegration = vip.getDoubleIntegration(); |
| | | //双倍积分 |
| | | if (1 == doubleIntegration) { |
| | | integral *= 2; |
| | | } |
| | | } |
| | | |
| | | if (integral > 0) { |
| | | TAppUserIntegralChange appUserIntegralChange = new TAppUserIntegralChange(); |
| | | appUserIntegralChange.setAppUserId(appUser.getId()); |
| | | appUserIntegralChange.setChangeType(2); |
| | | appUserIntegralChange.setHistoricalIntegral(appUser.getPoints()); |
| | | appUser.setPoints(appUser.getPoints() + integral); |
| | | appUserIntegralChange.setCurrentIntegral(appUser.getPoints()); |
| | | appUserIntegralChange.setCreateTime(LocalDateTime.now()); |
| | | appUserIntegralChange.setOrderCode(order.getCode()); |
| | | appUserIntegralChange.setExtension(order.getId().toString()); |
| | | appUserClient.updateAppUser(appUser); |
| | | appUserIntegralChangeClient.addAppUserIntegralChange(appUserIntegralChange); |
| | | } |
| | | } |
| | | |
| | | //计算用户标签 |
| | | chargingOrderService.editUserTag(order); |
| | | //用户推荐奖励 |
| | | chargingOrderService.referralReward(order); |
| | | |
| | | //开始构建退款费用 |
| | | refundAmount = refundAmount.setScale(2, RoundingMode.HALF_EVEN); |
| | | BigDecimal refundableThirdPayAmount = getRefundableThirdPayAmount(order); |
| | | BigDecimal remainingThirdAmount = refundableThirdPayAmount |
| | | .subtract(money(order.getThirdRefundAmount())).max(BigDecimal.ZERO); |
| | | BigDecimal remainingBalanceAmount = money(order.getBalancePayAmount()) |
| | | .subtract(money(order.getBalanceRefundAmount())).max(BigDecimal.ZERO); |
| | | BigDecimal refundableAmount = remainingThirdAmount.add(remainingBalanceAmount).setScale(2, RoundingMode.HALF_UP); |
| | | // 白名单全部免费只记录预估预充金额,实际渠道退款金额保持0,防止补偿任务重复发起退款。 |
| | | BigDecimal recordedRefundAmount = isWhitelistAllFreeOrder(order) ? money(order.getRechargeAmount()) : refundAmount; |
| | | BigDecimal channelRefundAmount = isWhitelistAllFreeOrder(order) |
| | | ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) |
| | | : refundAmount.min(refundableAmount).setScale(2, RoundingMode.HALF_UP); |
| | | Integer rechargePaymentType = order.getRechargePaymentType(); |
| | | BigDecimal thirdRefundAmount = channelRefundAmount.min(remainingThirdAmount).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal balanceRefundAmount = channelRefundAmount.subtract(thirdRefundAmount) |
| | | .min(remainingBalanceAmount).setScale(2, RoundingMode.HALF_UP); |
| | | if (channelRefundAmount.compareTo(BigDecimal.ZERO) > 0) { |
| | | refundAmount = thirdRefundAmount.add(balanceRefundAmount).setScale(2, RoundingMode.HALF_UP); |
| | | order.setThirdRefundAmount(money(order.getThirdRefundAmount()).add(thirdRefundAmount)); |
| | | order.setBalanceRefundAmount(money(order.getBalanceRefundAmount()).add(balanceRefundAmount)); |
| | | order.setRefundAmount(recordedRefundAmount); |
| | | order.setRefundStatus(refundAmount.compareTo(BigDecimal.ZERO) > 0 ? 1 : 2); |
| | | chargingOrderService.updateById(order); |
| | | //构建退款明细 |
| | | TChargingOrderRefund chargingOrderRefund = new TChargingOrderRefund(); |
| | | chargingOrderRefund.setChargingOrderId(order.getId()); |
| | | SimpleDateFormat sdfaa = new SimpleDateFormat("yyyyMMddHHmmssSSS"); |
| | | chargingOrderRefund.setRefundCode("CDF" + sdfaa.format(new Date()) + (Double.valueOf(Math.random() * 1000).intValue())); |
| | | chargingOrderRefund.setRefundAmount(refundAmount); |
| | | chargingOrderRefund.setRefundStatus(1); |
| | | chargingOrderRefund.setPayType(rechargePaymentType); |
| | | chargingOrderRefund.setCode(order.getCode()); |
| | | chargingOrderRefund.setRefundTitle("充电完成退款"); |
| | | chargingOrderRefund.setRefundContent("充电完成退款"); |
| | | chargingOrderRefund.setRefundReason("充电完成退款"); |
| | | chargingOrderRefund.setRefundRemark("实际充电消费金额:" + order.getPaymentAmount()); |
| | | chargingOrderRefund.setRefundTotalAmount(refundAmount); |
| | | chargingOrderRefund.setPayAmount(rechargeAmount); |
| | | if (thirdRefundAmount.compareTo(BigDecimal.ZERO) == 0) { |
| | | chargingOrderRefund.setRefundStatus(2); |
| | | chargingOrderRefund.setRefundSerialNumber("BALANCE"); |
| | | chargingOrderRefund.setRefundTime(LocalDateTime.now()); |
| | | chargingOrderRefundService.save(chargingOrderRefund); |
| | | if (balanceRefundAmount.compareTo(BigDecimal.ZERO) > 0) { |
| | | recordBalanceRefund(order, balanceRefundAmount, chargingOrderRefund.getRefundCode()); |
| | | } |
| | | order.setRefundStatus(2); |
| | | order.setRefundTime(LocalDateTime.now()); |
| | | chargingOrderService.updateById(order); |
| | | } else if ("ICBC".equalsIgnoreCase(order.getSourceName())) { |
| | | IcbcRefundReq req = new IcbcRefundReq(); |
| | | req.setOutTradeNo(order.getCode()); |
| | | req.setOrderId(order.getRechargeSerialNumber()); |
| | | req.setOutRefundNo(chargingOrderRefund.getRefundCode()); |
| | | req.setRefundAmount(thirdRefundAmount); |
| | | req.setTotalAmount(getRefundableThirdPayAmount(order)); |
| | | req.setReason("充电完成退款"); |
| | | 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())) { |
| | | chargingOrderRefundService.save(chargingOrderRefund); |
| | | String refundId = StringUtils.isNotEmpty(resp.getRefundId()) ? resp.getRefundId() : resp.getOutRefundNo(); |
| | | chargingOrderService.chargingOrderStartupFailureWxRefund( |
| | | chargingOrderRefund.getRefundCode(), refundId, "SUCCESS", resp.getRefundTime()); |
| | | } else { |
| | | log.error("工行充电完成退款失败,订单号:{},退款单号:{},返回:{}", |
| | | order.getCode(), chargingOrderRefund.getRefundCode(), result == null ? null : result.getMsg()); |
| | | } |
| | | } else if (1 == rechargePaymentType) { |
| | | WxPaymentRefundModel model = new WxPaymentRefundModel(); |
| | | model.setOut_trade_no(order.getCode()); |
| | | model.setOut_refund_no(chargingOrderRefund.getRefundCode()); |
| | | model.setReason("充电完成退款"); |
| | | model.setNotify_url("/payment/wx/refund/notify"); |
| | | WxPaymentRefundModel.RefundAmount amount = new WxPaymentRefundModel.RefundAmount(); |
| | | amount.setRefund(thirdRefundAmount.multiply(new BigDecimal(100)).intValue()); |
| | | amount.setTotal(getRefundableThirdPayAmount(order).multiply(new BigDecimal(100)).intValue()); |
| | | amount.setCurrency("CNY"); |
| | | model.setAmount(amount); |
| | | R<Map<String, Object>> result = wxPaymentClient.refundOrderR(model); |
| | | if (200 == result.getCode()) { |
| | | chargingOrderRefundService.save(chargingOrderRefund); |
| | | //??? |
| | | Map<String, Object> amount1 = (Map<String, Object>) result.getData().get("amount"); |
| | | Object refund_fee1 = amount1.get("refund_fee"); |
| | | BigDecimal refund_fee = new BigDecimal(null == refund_fee1 ? "0" : refund_fee1.toString()).divide(new BigDecimal(100)); |
| | | chargingOrderRefund.setRefundFee(refund_fee); |
| | | chargingOrderRefundService.updateById(chargingOrderRefund); |
| | | } |
| | | } |
| | | else if (2 == rechargePaymentType) { |
| | | RefundReq dto = new RefundReq(); |
| | | dto.setOutTradeNo(order.getCode()); |
| | | dto.setOutRequestNo(chargingOrderRefund.getRefundCode()); |
| | | dto.setRefundAmount(thirdRefundAmount.toString()); |
| | | dto.setRefundReason("充电完成退款"); |
| | | RefundResp resp = aliPaymentClient.refund(dto).getData(); |
| | | if (null != resp) { |
| | | chargingOrderRefundService.save(chargingOrderRefund); |
| | | chargingOrderService.chargingOrderStartupFailureWxRefund(chargingOrderRefund.getRefundCode(), resp.getTradeNo(), "SUCCESS", null); |
| | | } |
| | | } |
| | | else if (3 == rechargePaymentType) { |
| | | RefundReq dto = new RefundReq(); |
| | | dto.setOutTradeNo(order.getRechargeSerialNumber()); |
| | | dto.setOutRequestNo(chargingOrderRefund.getRefundCode()); |
| | | dto.setRefundAmount(thirdRefundAmount.toString()); |
| | | dto.setRefundReason("充电完成退款"); |
| | | RefundResp resp = h5AliPaymentClient.refund(dto).getData(); |
| | | if (null != resp && "10000".equals(resp.getCode())) { |
| | | chargingOrderRefundService.save(chargingOrderRefund); |
| | | chargingOrderService.chargingOrderStartupFailureWxRefund(chargingOrderRefund.getRefundCode(), resp.getTradeNo(), "SUCCESS", null); |
| | | } |
| | | } |
| | | } else if (recordedRefundAmount.compareTo(BigDecimal.ZERO) > 0) { |
| | | order.setThirdRefundAmount(thirdRefundAmount); |
| | | order.setBalanceRefundAmount(balanceRefundAmount); |
| | | order.setRefundAmount(recordedRefundAmount); |
| | | order.setRefundStatus(2); |
| | | chargingOrderService.updateById(order); |
| | | |
| | | TChargingOrderRefund chargingOrderRefund = new TChargingOrderRefund(); |
| | | chargingOrderRefund.setChargingOrderId(order.getId()); |
| | | SimpleDateFormat sdfaa = new SimpleDateFormat("yyyyMMddHHmmssSSS"); |
| | | chargingOrderRefund.setRefundCode("CDF" + sdfaa.format(new Date()) + (Double.valueOf(Math.random() * 1000).intValue())); |
| | | chargingOrderRefund.setRefundAmount(recordedRefundAmount); |
| | | chargingOrderRefund.setBalanceRefundAmount(balanceRefundAmount); |
| | | chargingOrderRefund.setThirdRefundAmount(thirdRefundAmount); |
| | | chargingOrderRefund.setRefundStatus(2); |
| | | chargingOrderRefund.setRefundSerialNumber("WHITELIST"); |
| | | chargingOrderRefund.setRefundTime(LocalDateTime.now()); |
| | | chargingOrderRefund.setPayType(rechargePaymentType); |
| | | chargingOrderRefund.setCode(order.getCode()); |
| | | chargingOrderRefund.setRefundTitle("充电完成退款"); |
| | | chargingOrderRefund.setRefundContent("充电完成退款"); |
| | | chargingOrderRefund.setRefundReason("充电完成退款"); |
| | | chargingOrderRefund.setRefundRemark("白名单全部免费,记录预估退款金额"); |
| | | chargingOrderRefund.setRefundTotalAmount(recordedRefundAmount); |
| | | chargingOrderRefund.setPayAmount(rechargeAmount); |
| | | chargingOrderRefundService.save(chargingOrderRefund); |
| | | } |
| | | } |
| | | |
| | | //添加汇总统计数据 |
| | | List<TChargingOrderAccountingStrategy> list4 = chargingOrderAccountingStrategyService.list(new QueryWrapper<TChargingOrderAccountingStrategy>().eq("charging_order_id", order.getId())); |
| | | BigDecimal electronic_reduce = list4.stream().map(TChargingOrderAccountingStrategy::getPeriodElectricPrice).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | BigDecimal service_reduce = list4.stream().map(TChargingOrderAccountingStrategy::getPeriodServicePrice).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | BigDecimal chargingCapacity = list4.stream().map(TChargingOrderAccountingStrategy::getChargingCapacity).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | BigDecimal serviceMoney = BigDecimal.ZERO; |
| | | BigDecimal paymentMoney = BigDecimal.ZERO; |
| | | long time = 0; |
| | | if (!order.getOrderSource().equals(2)) { |
| | | serviceMoney = serviceMoney.add(service_reduce); |
| | | paymentMoney = paymentMoney.add(electronic_reduce).add(service_reduce); |
| | | } else { |
| | | serviceMoney = serviceMoney.add(service_reduce.multiply(new BigDecimal("0.8"))); |
| | | paymentMoney = paymentMoney.add(electronic_reduce).add(service_reduce.multiply(new BigDecimal("0.8"))); |
| | | } |
| | | if (order.getStartTime() != null && order.getEndTime() != null) { |
| | | time = ChronoUnit.SECONDS.between(order.getStartTime(), order.getEndTime()); |
| | | } |
| | | TChargingOrderSummaryData summaryData = new TChargingOrderSummaryData(); |
| | | summaryData.setChargingOrderId(order.getId()); |
| | | summaryData.setChargingCapacity(chargingCapacity.setScale(6, RoundingMode.HALF_EVEN)); |
| | | summaryData.setChargingDuration(time); |
| | | summaryData.setIncome(paymentMoney.setScale(2, BigDecimal.ROUND_HALF_EVEN)); |
| | | summaryData.setElectricPrice(electronic_reduce.setScale(2, BigDecimal.ROUND_HALF_EVEN)); |
| | | summaryData.setServicePrice(serviceMoney.setScale(2, BigDecimal.ROUND_HALF_EVEN)); |
| | | chargingOrderSummaryDataService.save(summaryData); |
| | | |
| | | //推送监管平台订单状态和订单详情 |
| | | ChargingOrderVo chargingOrderVo = new ChargingOrderVo(); |
| | | BeanUtils.copyProperties(order, chargingOrderVo); |
| | | chargingMessageClient.pushOrderInfo(chargingOrderVo); |
| | | chargingMessageClient.pushOrderStatus(chargingOrderVo); |
| | | } |
| | | } |
| | | }catch (Exception e){ |
| | | e.printStackTrace(); |
| | | }finally { |
| | | lock.unlock(); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 添加充电算账单汇总数据 |
| | | * 生成上个月账单,避免月末最后一天 00:30 之后结算的订单漏统计(与账户结算账单 addAccountSettlementStatement 保持一致)。 |
| | | */ |
| | | public void addChargingSettlementBill(){ |
| | | int day = LocalDate.now().getDayOfMonth(); |
| | | int lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth(); |
| | | if (2 <= day && day <= lastDay) { |
| | | refreshChargingSettlementBill(LocalDate.now().minusMonths(1)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 主动刷新指定月份的充电结算账单。重建该月全部数据(含已出账),并保持已出账月份状态不回退。 |
| | | */ |
| | | public synchronized void refreshChargingSettlementBill(LocalDate billingMonth){ |
| | | try { |
| | | if (billingMonth == null) { |
| | | throw new IllegalArgumentException("账单月份不能为空"); |
| | | } |
| | | LocalDate firstDayOfBillingMonth = billingMonth.withDayOfMonth(2); |
| | | String string = firstDayOfBillingMonth.toString(); |
| | | // 将-替换为空字符串 |
| | | string = string.replace("-", ""); |
| | | |
| | | String targetMonth = billingMonth.format(DateTimeFormatter.ofPattern("yyyy-MM")); |
| | | //统计数据的开始时间 |
| | | String startTime = targetMonth + "-01 00:00:00"; |
| | | //统计数据的结束时间 |
| | | int lastDay = billingMonth.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth(); |
| | | String endTime = targetMonth + "-" + String.format("%02d", lastDay) + " 23:59:59"; |
| | | LocalDateTime createTime = LocalDateTime.now(); |
| | | |
| | | //删除之前生成的汇总数据,在添加最新的汇总数据 |
| | | //重建时删除该月全部记录(含已出账),避免重建已冻结月份时旧数据残留造成重复;并保留已出账状态 |
| | | String format = firstDayOfBillingMonth.format(DateTimeFormatter.ofPattern("yyyy-MM")); |
| | | boolean targetFrozen = chargingSettlementBillService.count(new QueryWrapper<TChargingSettlementBill>() |
| | | .eq("status", 2) |
| | | .last(" and DATE_FORMAT(billing_cycle, '%Y-%m') = '" + format + "'")) > 0; |
| | | chargingSettlementBillService.remove(new QueryWrapper<TChargingSettlementBill>().last(" and DATE_FORMAT(billing_cycle, '%Y-%m') = '" + format + "'")); |
| | | int targetStatus = targetFrozen ? 2 : 1; |
| | | |
| | | //循环生成按照开始时间和结束时间分别统计的汇总数据 |
| | | for (int i = 1; i < 3; i++) { |
| | | TChargingSettlementBill chargingSettlementBill = new TChargingSettlementBill(); |
| | | // 订单生成规则JSD+20231201(账单所属月份)+1131304205(随机10位数)+001(当月账单序号,每月重置) |
| | | Random random = new Random(); |
| | | String randomDigits = random.ints(10, 0, 10) // 生成10个随机数字,范围在0-9 |
| | | .mapToObj(String::valueOf) |
| | | .collect(Collectors.joining()); // 将其连接成一个字符串 |
| | | chargingSettlementBill.setSiteId(0); |
| | | chargingSettlementBill.setStatisticalDateType(i); |
| | | chargingSettlementBill.setBillingCycle(firstDayOfBillingMonth); |
| | | chargingSettlementBill.setStatus(targetStatus); |
| | | chargingSettlementBill.setCode("JSD"+string+randomDigits); |
| | | |
| | | //根据业务订单查询汇总数据 |
| | | List<Map<String, Object>> mapList = chargingOrderService.getChargingSettlementBill(startTime, endTime, i); |
| | | Long chargingDuration = 0L; |
| | | Integer orderNumber = 0; |
| | | BigDecimal totalAmount = BigDecimal.ZERO; |
| | | BigDecimal platformFee = BigDecimal.ZERO; |
| | | BigDecimal tripartiteCommission = BigDecimal.ZERO; |
| | | BigDecimal fundsReceived = BigDecimal.ZERO; |
| | | BigDecimal totalElectricity = BigDecimal.ZERO; |
| | | BigDecimal totalElectricityCharge = BigDecimal.ZERO; |
| | | BigDecimal totalServiceFee = BigDecimal.ZERO; |
| | | BigDecimal couponDeduction = BigDecimal.ZERO; |
| | | List<TChargingSettlementBill> list = new ArrayList<>(); |
| | | for (Map<String, Object> map : mapList) { |
| | | Long charging_duration = null == map.get("chargingDuration") ? 0L : Long.valueOf(map.get("chargingDuration").toString()); |
| | | Integer order_number = null == map.get("orderNum") ? 0 : Integer.valueOf(map.get("orderNum").toString()); |
| | | BigDecimal total_amount = null == map.get("totalAmount") ? BigDecimal.ZERO : new BigDecimal(map.get("totalAmount").toString()); |
| | | BigDecimal platform_fee = null == map.get("platformFee") ? BigDecimal.ZERO : new BigDecimal(map.get("platformFee").toString()); |
| | | BigDecimal tripartite_commission = null == map.get("tripartiteCommission") ? BigDecimal.ZERO : new BigDecimal(map.get("tripartiteCommission").toString()); |
| | | BigDecimal funds_received = total_amount.subtract(platform_fee).subtract(tripartite_commission); |
| | | BigDecimal total_electricity = null == map.get("totalElectricity") ? BigDecimal.ZERO : new BigDecimal(map.get("totalElectricity").toString()); |
| | | BigDecimal total_electricity_charge = null == map.get("totalElectricityCharge") ? BigDecimal.ZERO : new BigDecimal(map.get("totalElectricityCharge").toString()); |
| | | BigDecimal total_service_fee = null == map.get("totalServiceFee") ? BigDecimal.ZERO : new BigDecimal(map.get("totalServiceFee").toString()); |
| | | BigDecimal coupon_deduction = null == map.get("couponDeduction") ? BigDecimal.ZERO : new BigDecimal(map.get("couponDeduction").toString()); |
| | | //构建每个站点的汇总数据 |
| | | TChargingSettlementBill chargingSettlementBill1 = new TChargingSettlementBill(); |
| | | String randomDigits1 = random.ints(10, 0, 10) // 生成10个随机数字,范围在0-9 |
| | | .mapToObj(String::valueOf) |
| | | .collect(Collectors.joining()); // 将其连接成一个字符串 |
| | | chargingSettlementBill1.setSiteId(Integer.valueOf(map.get("siteId").toString())); |
| | | chargingSettlementBill1.setStatisticalDateType(i); |
| | | chargingSettlementBill1.setBillingCycle(firstDayOfBillingMonth); |
| | | chargingSettlementBill1.setStatus(targetStatus); |
| | | chargingSettlementBill1.setCode("JSD"+string+randomDigits1); |
| | | chargingSettlementBill1.setChargingDuration(charging_duration); |
| | | chargingSettlementBill1.setOrderNumber(order_number); |
| | | chargingSettlementBill1.setTotalAmount(total_amount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingSettlementBill1.setCouponDeduction(coupon_deduction.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingSettlementBill1.setPlatformFee(platform_fee.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingSettlementBill1.setTripartiteCommission(tripartite_commission.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingSettlementBill1.setFundsReceived(funds_received.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingSettlementBill1.setTotalElectricity(total_electricity.setScale(4, RoundingMode.HALF_EVEN)); |
| | | chargingSettlementBill1.setTotalElectricityCharge(total_electricity_charge.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingSettlementBill1.setTotalServiceFee(total_service_fee.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingSettlementBill1.setCreateTime(createTime); |
| | | list.add(chargingSettlementBill1); |
| | | //计算全站汇总数据 |
| | | chargingDuration = chargingDuration + charging_duration; |
| | | orderNumber = orderNumber + order_number; |
| | | totalAmount = totalAmount.add(total_amount); |
| | | platformFee = platformFee.add(platform_fee); |
| | | tripartiteCommission = tripartiteCommission.add(tripartite_commission); |
| | | fundsReceived = fundsReceived.add(funds_received); |
| | | totalElectricity = totalElectricity.add(total_electricity); |
| | | totalElectricityCharge = totalElectricityCharge.add(total_electricity_charge); |
| | | totalServiceFee = totalServiceFee.add(total_service_fee); |
| | | couponDeduction = couponDeduction.add(coupon_deduction); |
| | | } |
| | | |
| | | chargingSettlementBill.setChargingDuration(chargingDuration); |
| | | chargingSettlementBill.setOrderNumber(orderNumber); |
| | | chargingSettlementBill.setTotalAmount(totalAmount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingSettlementBill.setCouponDeduction(couponDeduction.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingSettlementBill.setPlatformFee(platformFee.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingSettlementBill.setTripartiteCommission(tripartiteCommission.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingSettlementBill.setFundsReceived(fundsReceived.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingSettlementBill.setTotalElectricity(totalElectricity.setScale(4, RoundingMode.HALF_EVEN)); |
| | | chargingSettlementBill.setTotalElectricityCharge(totalElectricityCharge.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingSettlementBill.setTotalServiceFee(totalServiceFee.setScale(2, RoundingMode.HALF_EVEN)); |
| | | chargingSettlementBill.setCreateTime(createTime); |
| | | chargingSettlementBillService.save(chargingSettlementBill); |
| | | chargingSettlementBillService.saveBatch(list); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("生成{}充电结算账单失败", billingMonth, e); |
| | | throw new RuntimeException("生成充电结算账单失败", e); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 添加账户结算账单数据 |
| | | */ |
| | | public void addAccountSettlementStatement(){ |
| | | int day = LocalDate.now().getDayOfMonth(); |
| | | int lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth(); |
| | | //时间在当月的2号到当月的最后一天才执行汇总数据和生成新的汇总数据(中间有修改数据的情况) |
| | | if (2 <= day && day <= lastDay) { |
| | | refreshAccountSettlementStatement(LocalDate.now().minusMonths(1)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 主动刷新指定月份的账户结算账单。只重建该月未出账数据,已出账数据不受影响。 |
| | | */ |
| | | public synchronized void refreshAccountSettlementStatement(LocalDate billingMonth) { |
| | | try { |
| | | if (billingMonth == null) { |
| | | throw new IllegalArgumentException("账单月份不能为空"); |
| | | } |
| | | LocalDate firstDayOfBillingMonth = billingMonth.withDayOfMonth(2); |
| | | String string = firstDayOfBillingMonth.toString(); |
| | | // 将-替换为空字符串 |
| | | string = string.replace("-", ""); |
| | | |
| | | String targetMonth = billingMonth.format(DateTimeFormatter.ofPattern("yyyy-MM")); |
| | | //统计数据的开始时间 |
| | | String startTime = targetMonth + "-01 00:00:00"; |
| | | //统计数据的结束时间 |
| | | int targetLastDay = billingMonth.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth(); |
| | | String endTime = targetMonth + "-" + String.format("%02d", targetLastDay) + " 23:59:59"; |
| | | LocalDateTime createTime = LocalDateTime.now(); |
| | | |
| | | //删除之前生成的汇总数据,在添加最新的汇总数据 |
| | | String format = firstDayOfBillingMonth.format(DateTimeFormatter.ofPattern("yyyy-MM")); |
| | | accountSettlementStatementService.remove(new QueryWrapper<TAccountSettlementStatement>().eq("status", 1).last(" and DATE_FORMAT(billing_cycle, '%Y-%m') = '" + format + "'")); |
| | | |
| | | Map<Integer, BigDecimal> siteServiceShareRatioMap = loadSiteServiceShareRatioMap(); |
| | | // V2.0 账户类型统一为工商银行商户,只生成明星充电小程序订单账单。 |
| | | for (int i = 3; i < 4; i++) { |
| | | TAccountSettlementStatement accountSettlementStatement = new TAccountSettlementStatement(); |
| | | // 订单生成规则JSD+20231201(账单所属月份)+1131304205(随机10位数)+001(当月账单序号,每月重置) |
| | | Random random = new Random(); |
| | | String randomDigits = random.ints(10, 0, 10) // 生成10个随机数字,范围在0-9 |
| | | .mapToObj(String::valueOf) |
| | | .collect(Collectors.joining()); // 将其连接成一个字符串 |
| | | accountSettlementStatement.setSiteId(0); |
| | | accountSettlementStatement.setAccountType(i); |
| | | accountSettlementStatement.setBillingCycle(firstDayOfBillingMonth); |
| | | accountSettlementStatement.setStatus(1); |
| | | accountSettlementStatement.setCode("JSD"+string+randomDigits); |
| | | |
| | | //根据业务订单查询汇总数据 |
| | | List<Map<String, Object>> mapList = chargingOrderService.getAccountSettlementStatement(startTime, endTime); |
| | | BigDecimal rechargeAmount = BigDecimal.ZERO; |
| | | BigDecimal refundAmount = BigDecimal.ZERO; |
| | | BigDecimal platformFee = BigDecimal.ZERO; |
| | | BigDecimal tripartiteCommission = BigDecimal.ZERO; |
| | | BigDecimal recordedAmount = BigDecimal.ZERO; |
| | | BigDecimal platformIncome = BigDecimal.ZERO; |
| | | BigDecimal partnerIncome = BigDecimal.ZERO; |
| | | List<TAccountSettlementStatement> list = new ArrayList<>(); |
| | | for (Map<String, Object> map : mapList) { |
| | | Integer siteId = Integer.valueOf(map.get("siteId").toString()); |
| | | BigDecimal recharge_amount = null == map.get("rechargeAmount") ? BigDecimal.ZERO : new BigDecimal(map.get("rechargeAmount").toString()); |
| | | BigDecimal refund_amount = null == map.get("refundAmount") ? BigDecimal.ZERO : new BigDecimal(map.get("refundAmount").toString()); |
| | | BigDecimal service_amount = null == map.get("serviceAmount") ? BigDecimal.ZERO : new BigDecimal(map.get("serviceAmount").toString()); |
| | | BigDecimal partner_income = calculateAccountPartnerIncome( |
| | | service_amount, siteServiceShareRatioMap.get(siteId), false); |
| | | BigDecimal platform_income = calculateAccountPlatformIncome(recharge_amount, partner_income); |
| | | BigDecimal recorded_amount = recharge_amount.subtract(refund_amount); |
| | | //构建每个站点的汇总数据 |
| | | TAccountSettlementStatement accountSettlementStatement1 = new TAccountSettlementStatement(); |
| | | String randomDigits1 = random.ints(10, 0, 10) // 生成10个随机数字,范围在0-9 |
| | | .mapToObj(String::valueOf) |
| | | .collect(Collectors.joining()); // 将其连接成一个字符串 |
| | | accountSettlementStatement1.setSiteId(siteId); |
| | | accountSettlementStatement1.setAccountType(i); |
| | | accountSettlementStatement1.setBillingCycle(firstDayOfBillingMonth); |
| | | accountSettlementStatement1.setStatus(1); |
| | | accountSettlementStatement1.setCode("JSD"+string+randomDigits1); |
| | | accountSettlementStatement1.setRechargeAmount(recharge_amount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | accountSettlementStatement1.setRefundAmount(refund_amount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | accountSettlementStatement1.setPlatformFee(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_EVEN)); |
| | | accountSettlementStatement1.setTripartiteCommission(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_EVEN)); |
| | | accountSettlementStatement1.setRecordedAmount(recorded_amount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | accountSettlementStatement1.setPlatformIncome(platform_income.setScale(2, RoundingMode.HALF_UP)); |
| | | accountSettlementStatement1.setPartnerIncome(partner_income.setScale(2, RoundingMode.HALF_UP)); |
| | | accountSettlementStatement1.setCreateTime(createTime); |
| | | list.add(accountSettlementStatement1); |
| | | //计算全站汇总数据 |
| | | rechargeAmount = rechargeAmount.add(recharge_amount); |
| | | refundAmount = refundAmount.add(refund_amount); |
| | | recordedAmount = recordedAmount.add(recorded_amount); |
| | | platformIncome = platformIncome.add(platform_income); |
| | | partnerIncome = partnerIncome.add(partner_income); |
| | | } |
| | | |
| | | accountSettlementStatement.setRechargeAmount(rechargeAmount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | accountSettlementStatement.setRefundAmount(refundAmount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | accountSettlementStatement.setPlatformFee(platformFee.setScale(2, RoundingMode.HALF_EVEN)); |
| | | accountSettlementStatement.setTripartiteCommission(tripartiteCommission.setScale(2, RoundingMode.HALF_EVEN)); |
| | | accountSettlementStatement.setRecordedAmount(recordedAmount.setScale(2, RoundingMode.HALF_EVEN)); |
| | | accountSettlementStatement.setPlatformIncome(platformIncome.setScale(2, RoundingMode.HALF_UP)); |
| | | accountSettlementStatement.setPartnerIncome(partnerIncome.setScale(2, RoundingMode.HALF_UP)); |
| | | accountSettlementStatement.setCreateTime(createTime); |
| | | accountSettlementStatementService.save(accountSettlementStatement); |
| | | accountSettlementStatementService.saveBatch(list); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("生成{}账户结算账单失败", billingMonth, e); |
| | | throw new RuntimeException("生成账户结算账单失败", e); |
| | | } |
| | | } |
| | | |
| | | static BigDecimal calculateAccountPartnerIncome(BigDecimal paidServiceAmount, BigDecimal serviceShareRatio, |
| | | boolean thirdPartyOrder) { |
| | | if (thirdPartyOrder || paidServiceAmount == null || serviceShareRatio == null |
| | | || serviceShareRatio.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | return paidServiceAmount.multiply(serviceShareRatio) |
| | | .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | static BigDecimal calculateAccountPlatformIncome(BigDecimal paymentAmount, BigDecimal partnerIncome) { |
| | | BigDecimal paid = paymentAmount == null ? BigDecimal.ZERO : paymentAmount; |
| | | BigDecimal partner = partnerIncome == null ? BigDecimal.ZERO : partnerIncome; |
| | | return paid.subtract(partner).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private Map<Integer, BigDecimal> loadSiteServiceShareRatioMap() { |
| | | List<Site> sites = Optional.ofNullable(siteClient.getSiteAll()) |
| | | .map(R::getData).orElse(Collections.emptyList()); |
| | | List<Partner> partners = Optional.ofNullable(siteClient.getAllPartner()) |
| | | .map(R::getData).orElse(Collections.emptyList()); |
| | | Map<Integer, Partner> partnerMap = partners.stream() |
| | | .filter(partner -> partner != null && partner.getId() != null) |
| | | .collect(Collectors.toMap(Partner::getId, partner -> partner, (left, right) -> left)); |
| | | Map<Integer, BigDecimal> ratioMap = new HashMap<>(); |
| | | 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; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 推送三方引流平台充电数据 |
| | | */ |
| | | public void pushOrderDrainagePlatform(){ |
| | | String format = LocalDateTime.now().minusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); |
| | | List<TChargingOrder> list = chargingOrderService.list(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getStatus, 5) |
| | | .eq(TChargingOrder::getOrderSource, 2).eq(TChargingOrder::getDelFlag, 0) |
| | | .isNotNull(TChargingOrder::getStartChargeSeq).isNotNull(TChargingOrder::getOperatorId) |
| | | .last(" and DATE_FORMAT(end_time, '%Y-%m-%d') = '" + format + "'")); |
| | | for (TChargingOrder tChargingOrder : list) { |
| | | String code = tChargingOrder.getCode(); |
| | | chargingOrderService.pushOrderInfo(code); |
| | | } |
| | | |
| | | } |
| | | } |