package com.ruoyi.order.service.impl; import cn.hutool.core.util.NumberUtil; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.account.api.dto.SendMessageDTO; import com.ruoyi.account.api.feignClient.*; import com.ruoyi.account.api.model.*; import com.ruoyi.account.api.vo.AppUserWhitelistMatchVO; import com.ruoyi.account.api.vo.GetAppUserVipDetail; import com.ruoyi.account.api.vo.GetInviteUser; import com.ruoyi.chargingPile.api.dto.GetSiteListDTO; import com.ruoyi.chargingPile.api.feignClient.*; import com.ruoyi.chargingPile.api.model.*; import com.ruoyi.chargingPile.api.support.ReservationTimeRangeSupport; import com.ruoyi.chargingPile.api.vo.GetChargingGunByCode; import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.dto.ChargingOrderGroup; import com.ruoyi.common.core.dto.ChargingPercentProvinceDto; import com.ruoyi.common.core.exception.ServiceException; import com.ruoyi.common.core.web.domain.AjaxResult; import com.ruoyi.common.core.web.domain.BasePojo; import com.ruoyi.common.core.web.page.PageInfo; import com.ruoyi.common.redis.service.RedisService; import com.ruoyi.common.security.service.TokenService; import com.ruoyi.common.security.utils.SecurityUtils; import com.ruoyi.integration.api.feignClient.PlatformStartChargingReplyClient; import com.ruoyi.integration.api.feignClient.SendMessageClient; import com.ruoyi.integration.api.feignClient.UploadRealTimeMonitoringDataClient; import com.ruoyi.integration.api.model.ChargingHandshake; import com.ruoyi.integration.api.model.PlatformStartCharging; import com.ruoyi.integration.api.model.PlatformStartChargingReply; import com.ruoyi.integration.api.model.UploadRealTimeMonitoringData; import com.ruoyi.integration.api.feignClient.*; import com.ruoyi.integration.api.model.*; import com.ruoyi.integration.api.vo.ChargingOrderVo; import com.ruoyi.integration.api.vo.GetPlatformStopChargingReply; import com.ruoyi.integration.api.vo.NotificationConnectorChargeStatusVo; import com.ruoyi.integration.api.vo.StartChargeResult; import com.ruoyi.order.api.dto.SettlementConfirmAdd; import com.ruoyi.order.api.feignClient.AccountingStrategyDetailOrderClient; import com.ruoyi.order.api.feignClient.AccountingStrategyOrderClient; import com.ruoyi.order.api.model.*; import com.ruoyi.order.api.query.ChargingOrderQuery; import com.ruoyi.order.api.query.SettlementListQuery; import com.ruoyi.order.api.query.UploadRealTimeMonitoringDataQuery; import com.ruoyi.order.api.vo.*; 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.service.TChargingOrderService; import com.ruoyi.order.service.TOrderEvaluateService; import com.ruoyi.order.service.*; import com.ruoyi.order.support.ReservationOrderSupport; import com.ruoyi.order.support.ReservationRecommendationCalculator; import com.ruoyi.order.util.mongodb.service.PlatformStartChargingReplyService; 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.StatisticsOfBatteryVO; import com.ruoyi.order.vo.LargeChargingPowerVo; import com.ruoyi.order.vo.ReservationRecommendationVO; import com.ruoyi.other.api.domain.*; import com.ruoyi.order.vo.ChargingOrderListInfoVO; import com.ruoyi.other.api.domain.TVip; import com.ruoyi.other.api.dto.ExchangeCodeConsumeDTO; import com.ruoyi.other.api.feignClient.*; 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; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionTemplate; import org.springframework.util.DigestUtils; import org.springframework.util.StringUtils; import javax.annotation.Resource; import javax.crypto.MacSpi; import java.lang.reflect.InvocationTargetException; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import java.nio.charset.StandardCharsets; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.*; import java.time.format.DateTimeFormatter; import java.time.format.TextStyle; import java.time.temporal.ChronoUnit; import java.time.temporal.WeekFields; import java.util.*; import java.util.concurrent.*; import java.util.stream.Collectors; /** *

* 服务实现类 *

* * @author xiaochen * @since 2024-08-07 */ @Service public class TChargingOrderServiceImpl extends ServiceImpl implements TChargingOrderService { private Logger log = LoggerFactory.getLogger(TChargingOrderServiceImpl.class); private static final int WHITELIST_BENEFIT_ALL_FREE = 1; private static final int WHITELIST_BENEFIT_ELECTRIC_FREE = 2; private static final int WHITELIST_BENEFIT_SERVICE_FREE = 3; private static final String SHARE_AFTER_COMMIT_RESOURCE_PREFIX = "chargingOrderShareAfterCommit:"; private static final String SPECIAL_REFUND_TITLE = "特殊退款处理"; private static final int RESERVATION_RECOMMENDATION_HISTORY_DAYS = 30; private static final int ENTERPRISE_DETAIL_PERIOD_BATCH_SIZE = 500; private static final int ENTERPRISE_INVOICE_BATCH_SIZE = 500; private static final int ENTERPRISE_ORDER_ID_BATCH_SIZE = 500; private static final int CHARGING_PERIOD_ACCOUNTING_BATCH_SIZE = 500; private static final String LARGE_SCREEN_CACHE_PREFIX = "large:screen:"; private static final long LARGE_SCREEN_CACHE_SECONDS = 60L; private static final LocalDate LARGE_SCREEN_STAT_DATA_START = LocalDate.of(2020, 1, 1); private static final DateTimeFormatter LARGE_SCREEN_MONTH_FMT = DateTimeFormatter.ofPattern("yyyy-MM"); private static final DateTimeFormatter LARGE_SCREEN_DAY_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private static final DateTimeFormatter LARGE_SCREEN_SQL_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); @Override public ReservationRecommendationVO getReservationRecommendation(Integer siteId) { if (siteId == null) { throw new ServiceException("站点不能为空"); } R> siteResult = siteClient.getSiteByIds(Collections.singletonList(siteId)); List sites = siteResult == null ? null : siteResult.getData(); if (sites == null || sites.isEmpty()) { throw new ServiceException("站点不存在"); } Site site = sites.get(0); if (site.getAccountingStrategyId() == null) { throw new ServiceException("该站点尚未配置充电计费模板"); } R> detailResult = accountingStrategyDetailClient .getListByAccountingStrategyId(site.getAccountingStrategyId()); List details = detailResult == null ? null : detailResult.getData(); if (details == null) { throw new ServiceException("计费模板服务暂不可用"); } LocalDateTime historyEnd = LocalDate.now().atStartOfDay(); LocalDateTime historyStart = historyEnd.minusDays(RESERVATION_RECOMMENDATION_HISTORY_DAYS); List orders = list(Wrappers.lambdaQuery() .select(TChargingOrder::getStartTime, TChargingOrder::getEndTime) .eq(TChargingOrder::getSiteId, siteId) .eq(TChargingOrder::getStatus, 5) .eq(TChargingOrder::getDelFlag, 0) .isNotNull(TChargingOrder::getStartTime) .isNotNull(TChargingOrder::getEndTime) .ge(TChargingOrder::getEndTime, historyStart) .lt(TChargingOrder::getStartTime, historyEnd)); ReservationRecommendationCalculator.Recommendation recommendation = ReservationRecommendationCalculator.recommend(details, orders, historyStart, historyEnd); if (recommendation == null) { throw new ServiceException("该站点计费模板未配置谷时段"); } return new ReservationRecommendationVO(recommendation.getStartTime(), recommendation.getEndTime(), recommendation.getUtilizationRate()); } @Resource private ChargingGunClient chargingGunClient; @Resource private TSettlementConfirmMapper tSettlementConfirmMapper; @Resource private PartnerClient partnerClient; @Resource private SiteClient siteClient; @Resource private AppUserCarClient appUserCarClient; @Resource private AppUserClient appUserClient; @Resource private ChargingPileClient chargingPileClient; @Resource private TChargingOrderAccountingStrategyService chargingOrderAccountingStrategyService; @Resource private TokenService tokenService; @Resource private WxPaymentClient wxPaymentClient; @Resource private AliPaymentClient aliPaymentClient; @Resource private H5AliPaymentClient h5AliPaymentClient; @Resource private IcbcPaymentClient icbcPaymentClient; @Resource private TChargingOrderShareRecordMapper chargingOrderShareRecordMapper; @Value("${icbc.share.online-enabled:false}") private boolean icbcOnlineShareEnabled; @Resource private ChargingOrderSettlementExecutor chargingOrderSettlementExecutor; @Resource private PlatformTransactionManager transactionManager; @Resource private AppCouponClient appCouponClient; @Resource private AppUserWhitelistClient appUserWhitelistClient; @Resource private AppUserVipDetailClient appUserVipDetailClient; @Resource private RedisService redisService; @Resource private UploadRealTimeMonitoringDataService uploadRealTimeMonitoringDataService; @Resource private TCECClient tcecClient; @Resource private SendMessageClient sendMessageClient; @Resource private WxSubscribeMessageService wxSubscribeMessageService; @Resource private TOrderEvaluateService orderEvaluateService; @Resource private AccountingStrategyDetailClient accountingStrategyDetailClient; @Resource private AccountingStrategyDetailOrderClient accountingStrategyDetailOrderClient; @Resource private AccountingStrategyClient accountingStrategyClient; @Resource private PlatformStartChargingReplyService platformStartChargingReplyService; @Resource private TChargingOrderRefundService chargingOrderRefundService; @Resource private PlatformStopChargingReplyClient platformStopChargingReplyClient; @Resource private AccountingStrategyOrderService accountingStrategyOrderService; @Resource private AccountingStrategyDetailOrderService accountingStrategyDetailOrderService; @Resource private InviteUserClient inviteUserClient; @Resource private AppUserIntegralChangeClient appUserIntegralChangeClient; @Resource private IntegralRuleClient integralRuleClient; @Resource private VipClient vipClient; @Resource private OtherClient otherClient; @Resource private UserTagClient userTagClient; @Resource private AppUserTagClient appUserTagClient; @Resource private SecurityDetectionClient securityDetectionClient; //计数器 private Map counter_map = new HashMap<>(); //计数器 private Map boot_failed_map = new HashMap<>(); @Resource private OperatorClient operatorClient; @Resource private TransactionRecordService transactionRecordService; @Resource private ITChargingOrderSummaryDataService chargingOrderSummaryDataService; @Resource private TOrderAppealService orderAppealService; @Autowired private RedissonClient redissonClient; @Resource private ChargingMessageClient chargingMessageClient; @Resource private ChengDuJianGuanClient chengDuJianGuanClient; /** * 获取小程序充电记录列表数据 * * @param query * @return */ @Override public Map getMyChargingOrderList(GetMyChargingOrderList query) { Long appUserId = tokenService.getLoginUserApplet().getUserId(); Map map = new HashMap<>(); int size = this.baseMapper.getMyChargingOrderList(appUserId, 1, null, null).size(); map.put("number", size); Integer pageCurr = query.getPageCurr() == null || query.getPageCurr() < 1 ? 1 : query.getPageCurr(); Integer pageSize = query.getPageSize() == null || query.getPageSize() < 1 ? 10 : query.getPageSize(); query.setPageCurr((pageCurr - 1) * pageSize); query.setPageSize(pageSize); List list = this.baseMapper.getMyChargingOrderList(appUserId, query.getType(), query.getPageCurr(), query.getPageSize()); List orderLists = new ArrayList<>(); for (TChargingOrder tChargingOrder : list) { MyChargingOrderList myChargingOrderList = new MyChargingOrderList(); myChargingOrderList.setId(tChargingOrder.getId().toString()); myChargingOrderList.setSiteId(tChargingOrder.getSiteId()); myChargingOrderList.setStatus(tChargingOrder.getStatus()); Site site = getOrderSite(tChargingOrder.getSiteId()); myChargingOrderList.setTitle(site == null ? "未知电站" : site.getName()); myChargingOrderList.setChargingDegree(tChargingOrder.getChargingCapacity()); String name = getChargingGunName(tChargingOrder.getChargingGunId()); myChargingOrderList.setChargingGunId(tChargingOrder.getChargingGunId()); myChargingOrderList.setName(name); myChargingOrderList.setEndMode(tChargingOrder.getEndMode()); Integer status = tChargingOrder.getStatus(); BigDecimal payMoney = status != null && status < 4 ? tChargingOrder.getRechargeAmount() : tChargingOrder.getPaymentAmount(); myChargingOrderList.setPayMoney(payMoney); if (tChargingOrder.getCreateTime() != null) { myChargingOrderList.setCreateTime(tChargingOrder.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); } long count = orderEvaluateService.count(new LambdaQueryWrapper().eq(TOrderEvaluate::getOrderType, 1) .eq(TOrderEvaluate::getOrderId, tChargingOrder.getId()).eq(TOrderEvaluate::getDelFlag, 0)); myChargingOrderList.setEvaluate(count + 1L); orderLists.add(myChargingOrderList); } list = this.baseMapper.getMyChargingOrderList(appUserId, query.getType(), null, null); map.put("list", orderLists); map.put("totle", list.size()); return map; } private Site getOrderSite(Integer siteId) { if (siteId == null) { return null; } R> result = siteClient.getSiteByIds(Collections.singletonList(siteId)); List sites = result == null ? Collections.emptyList() : result.getData(); return sites == null || sites.isEmpty() ? null : sites.get(0); } private String getChargingGunName(Integer chargingGunId) { if (chargingGunId == null) { return ""; } R result = chargingGunClient.getAllName(chargingGunId); return result == null || result.getData() == null ? "" : result.getData(); } /** * 获取充电订单明细 * * @param id 订单id * @return */ @Override public MyChargingOrderInfo getMyChargingOrderInfo(String id) { TChargingOrder chargingOrder = this.getById(id); MyChargingOrderInfo myChargingOrderInfo = new MyChargingOrderInfo(); myChargingOrderInfo.setId(id); myChargingOrderInfo.setChargingGunId(chargingOrder.getChargingGunId()); myChargingOrderInfo.setCode(chargingOrder.getCode()); myChargingOrderInfo.setCreateTime(chargingOrder.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); myChargingOrderInfo.setStatus(chargingOrder.getStatus()); Site site = siteClient.getSiteByIds(Arrays.asList(chargingOrder.getSiteId())).getData().get(0); myChargingOrderInfo.setTitle(site.getName()); TChargingGun chargingGun = chargingGunClient.getChargingGunById(chargingOrder.getChargingGunId()).getData(); myChargingOrderInfo.setChargeMode(chargingGun.getChargeMode()); String name = chargingGunClient.getAllName(chargingOrder.getChargingGunId()).getData(); myChargingOrderInfo.setName(name); if (Objects.nonNull(chargingOrder.getAppUserCarId())) { TAppUserCar tAppUserCar = appUserCarClient.getCarById(chargingOrder.getAppUserCarId().toString()).getData(); if (null != tAppUserCar) { myChargingOrderInfo.setLicensePlate(tAppUserCar.getLicensePlate()); } else { myChargingOrderInfo.setLicensePlate("无"); } } else { myChargingOrderInfo.setLicensePlate(chargingOrder.getPlateNum()); } if (null != chargingOrder.getStartTime()) { myChargingOrderInfo.setStartTime(chargingOrder.getStartTime().atZone(ZoneId.systemDefault()).toInstant().getEpochSecond() * 1000); } if (null != chargingOrder.getEndTime()) { myChargingOrderInfo.setEndTime(chargingOrder.getEndTime().atZone(ZoneId.systemDefault()).toInstant().getEpochSecond() * 1000); } myChargingOrderInfo.setOrderAmount(chargingOrder.getOrderAmount()); myChargingOrderInfo.setPaymentAmount(chargingOrder.getPaymentAmount()); myChargingOrderInfo.setRechargeAmount(chargingOrder.getRechargeAmount()); myChargingOrderInfo.setCouponDiscountAmount(chargingOrder.getCouponDiscountAmount()); myChargingOrderInfo.setVipDiscountAmount(chargingOrder.getVipDiscountAmount()); myChargingOrderInfo.setRefundAmount(chargingOrder.getRefundAmount()); List stageCost = chargingOrderAccountingStrategyService.list( new LambdaQueryWrapper().eq(TChargingOrderAccountingStrategy::getChargingOrderId, id) .orderByAsc(TChargingOrderAccountingStrategy::getStartTime) ); myChargingOrderInfo.setStageCost(stageCost); List dataList = uploadRealTimeMonitoringDataService.getDataByOrderCode(chargingOrder.getCode()); if (null != dataList) { //在MongoDB中获取数据 SimpleDateFormat sdf = new SimpleDateFormat("HH:mm"); List dataLists = new ArrayList<>(); String t = ""; for (UploadRealTimeMonitoringData uploadRealTimeMonitoringData : dataList) { if (!sdf.format(uploadRealTimeMonitoringData.getCreate_time()).equals(t)) { dataLists.add(uploadRealTimeMonitoringData); t = sdf.format(uploadRealTimeMonitoringData.getCreate_time()); } } List time = dataLists.stream().map(s -> sdf.format(s.getCreate_time())).collect(Collectors.toList()); List outputVoltage = dataLists.stream().map(UploadRealTimeMonitoringData::getOutput_voltage).collect(Collectors.toList()); List outputCurrent = dataLists.stream().map(UploadRealTimeMonitoringData::getOutput_current).collect(Collectors.toList()); List> currentAndVoltage = new ArrayList<>(); for (int i = 0; i < time.size(); i++) { Map map = new HashMap<>(); map.put("time", time.get(i)); //电流 map.put("electricity", outputCurrent.get(i)); //电压 map.put("voltage", outputVoltage.get(i)); currentAndVoltage.add(map); } myChargingOrderInfo.setCurrentAndVoltage(currentAndVoltage); //在MongoDB中获取数据 List soc = dataLists.stream().map(UploadRealTimeMonitoringData::getSoc).collect(Collectors.toList()); List po = dataLists.stream().map(s -> s.getOutput_current().multiply(s.getOutput_voltage()).divide(new BigDecimal(1000), 2, RoundingMode.HALF_DOWN)).collect(Collectors.toList()); List> power = new ArrayList<>(); for (int i = 0; i < time.size(); i++) { Map map = new HashMap<>(); map.put("time", time.get(i)); //soc map.put("soc", soc.get(i)); //功率 map.put("power", po.get(i)); power.add(map); } myChargingOrderInfo.setPower(power); } myChargingOrderInfo.setActionable(myChargingOrderInfo.getEndTime() == null || myChargingOrderInfo.getEndTime() + 604800000L < System.currentTimeMillis() ? 0 : 1); long count = orderAppealService.count(Wrappers.lambdaQuery(TOrderAppeal.class) .eq(TOrderAppeal::getOrderId, id)); myChargingOrderInfo.setIsAppeal(count > 0 ? 0 : 1); return myChargingOrderInfo; } /** * 获取待开票订单列表 * * @param query * @return */ @Override public List getNoInvoicedOrder(GetNoInvoicedOrder query) { Long appUserId = tokenService.getLoginUserApplet().getUserId(); query.setPageCurr((query.getPageCurr() - 1) * query.getPageSize()); List orderList = this.baseMapper.getNoInvoicedOrder(appUserId, query.getMonth(), query.getPageCurr(), query.getPageSize()); List list = new ArrayList<>(); for (TChargingOrder tChargingOrder : orderList) { MyChargingOrderList myChargingOrderList = new MyChargingOrderList(); myChargingOrderList.setId(tChargingOrder.getId().toString()); myChargingOrderList.setSiteId(tChargingOrder.getSiteId()); myChargingOrderList.setStatus(tChargingOrder.getStatus()); Site site = siteClient.getSiteByIds(Arrays.asList(tChargingOrder.getSiteId())).getData().get(0); myChargingOrderList.setTitle(site.getName()); myChargingOrderList.setChargingDegree(tChargingOrder.getChargingCapacity()); String name = chargingGunClient.getAllName(tChargingOrder.getChargingGunId()).getData(); myChargingOrderList.setName(name); myChargingOrderList.setEndMode(tChargingOrder.getEndMode()); BigDecimal payMoney = tChargingOrder.getStatus() < 4 ? tChargingOrder.getRechargeAmount() : tChargingOrder.getPaymentAmount(); myChargingOrderList.setPayMoney(payMoney); myChargingOrderList.setCreateTime(tChargingOrder.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); myChargingOrderList.setElectricCharge(tChargingOrder.getElectrovalence()); myChargingOrderList.setServiceCharge(tChargingOrder.getServiceCharge()); list.add(myChargingOrderList); } return list; } /** * 充电支付 * * @param addChargingOrder * @return */ /** * 判断订单是否使用余额或余额组合支付 * * @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); } private void sendVipChargeFinishMessage(TAppUser appUser, TChargingOrder chargingOrder, Site site) { try { if (appUser == null || chargingOrder == null || appUser.getVipId() == null || !StringUtils.hasLength(appUser.getPhone())) { return; } GetAppUserVipDetail query = new GetAppUserVipDetail(); query.setAppUserId(appUser.getId()); query.setVipId(appUser.getVipId()); TAppUserVipDetail vipDetail = appUserVipDetailClient.getAppUserVipDetail(query).getData(); if (vipDetail == null) { return; } SendMessageDTO dto = new SendMessageDTO(); dto.setType(4); dto.setPhone(appUser.getPhone()); dto.setSite(site == null ? "充电站" : site.getName()); dto.setChargingCapacity(formatSmsNumber(chargingOrder.getChargingCapacity())); BigDecimal fee = chargingOrder.getPaymentAmount() == null ? chargingOrder.getOrderAmount() : chargingOrder.getPaymentAmount(); dto.setChargingFee(formatSmsNumber(fee)); R result = appUserClient.sendMessage(dto); if (result == null || result.getCode() != 200) { log.warn("VIP充电完成短信发送失败,orderCode={}, appUserId={}, result={}", chargingOrder.getCode(), appUser.getId(), result); } } catch (Exception e) { log.warn("VIP充电完成短信发送异常,orderCode={}, appUserId={}", chargingOrder == null ? null : chargingOrder.getCode(), appUser == null ? null : appUser.getId(), e); } } private String formatSmsNumber(BigDecimal value) { if (value == null) { return "0"; } String text = value.setScale(0, RoundingMode.HALF_UP).toPlainString(); return text.length() <= 5 ? text : text.substring(0, 5); } /** * 获取第三方实付金额,兼容早期未写入third_pay_amount的纯第三方支付订单。 * * @param order 充电订单 * @return 第三方可退款本金 */ private BigDecimal getRefundableThirdPayAmount(TChargingOrder order) { if (order == null || isWhitelistAllFreeOrder(order)) { return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); } return ChargingSimulationSupport.thirdPayAmount(order); } /** * 调整用户余额 * * @param userId 用户ID * @param delta 余额变动金额 */ private void changeUserBalance(Long userId, BigDecimal delta) { if (userId == null || delta == null || delta.compareTo(BigDecimal.ZERO) == 0) { return; } TAppUser appUser = appUserClient.getUserById(userId).getData(); if (appUser == null) { throw new RuntimeException("用户不存在"); } appUser.setBalance(money(appUser.getBalance()).add(delta).setScale(2, RoundingMode.HALF_UP)); appUserClient.updateAppUser(appUser); } /** * 记录兑换码余额消费流水 * * @param chargingOrder 充电订单 * @param consumeAmount 消费金额 */ private void recordBalanceConsume(TChargingOrder chargingOrder, BigDecimal consumeAmount) { if (chargingOrder == null || chargingOrder.getAppUserId() == null || chargingOrder.getId() == null || consumeAmount == null || consumeAmount.compareTo(BigDecimal.ZERO) <= 0) { return; } ExchangeCodeConsumeDTO consumeDTO = new ExchangeCodeConsumeDTO(); consumeDTO.setUserId(chargingOrder.getAppUserId()); consumeDTO.setOrderId(chargingOrder.getId()); consumeDTO.setAmount(consumeAmount); R result = otherClient.consumeExchangeCodeBalance(consumeDTO); if (result == null || result.getCode() != 200) { throw new RuntimeException("扣减兑换码余额失败"); } } /** * 记录兑换码余额退款流水 * * @param chargingOrder 充电订单 * @param refundAmount 退款金额 * @param refundNo 退款单号 */ private void recordBalanceRefund(TChargingOrder chargingOrder, BigDecimal refundAmount, String refundNo) { if (chargingOrder == null || chargingOrder.getAppUserId() == null || chargingOrder.getId() == null || refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) { return; } ExchangeCodeConsumeDTO refundDTO = new ExchangeCodeConsumeDTO(); refundDTO.setUserId(chargingOrder.getAppUserId()); refundDTO.setOrderId(chargingOrder.getId()); refundDTO.setAmount(refundAmount); refundDTO.setRefundNo(refundNo); R result = otherClient.refundExchangeCodeBalance(refundDTO); if (result == null || result.getCode() != 200) { throw new RuntimeException("退还兑换码余额失败"); } } /** * 完成人工退款结果落库,并同步余额退款流水 * * @param chargingOrder 充电订单 * @param refund 退款记录 * @param refundAmount 退款总金额 * @param balanceRefundAmount 余额退款金额 * @param thirdRefundAmount 三方退款金额 */ private void finishManualChargingRefund(TChargingOrder chargingOrder, TChargingOrderRefund refund, BigDecimal refundAmount, BigDecimal balanceRefundAmount, BigDecimal thirdRefundAmount) { chargingOrder.setRefundStatus(2); chargingOrder.setRefundAmount(money(chargingOrder.getRefundAmount()).add(refundAmount)); chargingOrder.setBalanceRefundAmount(money(chargingOrder.getBalanceRefundAmount()).add(balanceRefundAmount)); chargingOrder.setThirdRefundAmount(money(chargingOrder.getThirdRefundAmount()).add(thirdRefundAmount)); this.baseMapper.updateById(chargingOrder); refund.setRefundStatus(2); chargingOrderRefundService.save(refund); if (balanceRefundAmount.compareTo(BigDecimal.ZERO) > 0) { 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 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()); } private void validateSelectedChargingCoupon(Long appCouponId, Long userId) { TAppCoupon appCoupon = appCouponClient.getAppCouponById(appCouponId).getData(); if (appCoupon == null || !userId.equals(appCoupon.getAppUserId())) { throw new ServiceException("优惠券不存在或不属于当前用户"); } if (!Integer.valueOf(1).equals(appCoupon.getStatus())) { throw new ServiceException("优惠券已使用"); } LocalDateTime now = LocalDateTime.now(); if ((appCoupon.getStartTime() != null && appCoupon.getStartTime().isAfter(now)) || (appCoupon.getEndTime() != null && appCoupon.getEndTime().isBefore(now))) { throw new ServiceException("优惠券不在有效期内"); } TCoupon coupon = JSON.parseObject(appCoupon.getCouponJson(), TCoupon.class); if (coupon == null || !Integer.valueOf(1).equals(coupon.getType())) { throw new ServiceException("请选择充电优惠券"); } } /** * 判断订单是否为白名单订单 * * @param chargingOrder 充电订单 * @return true=白名单订单 */ private boolean isWhitelistOrder(TChargingOrder chargingOrder) { return chargingOrder != null && Integer.valueOf(1).equals(chargingOrder.getIsWhitelist()); } /** * 判断订单是否为白名单全部免费权益 * * @param chargingOrder 充电订单 * @return true=全部免费白名单订单 */ private boolean isWhitelistAllFreeOrder(TChargingOrder chargingOrder) { return isWhitelistOrder(chargingOrder) && Integer.valueOf(WHITELIST_BENEFIT_ALL_FREE).equals(chargingOrder.getWhitelistBenefitType()); } /** * 按白名单权益拆分电费和服务费减免金额,财务统计需要分开展示两类退款金额 * * @param chargingOrder 充电订单 * @param electrovalence 原始电费 * @param serviceCharge 原始服务费 * @return 数组下标0=电费减免,下标1=服务费减免 */ private BigDecimal[] calculateWhitelistRefundSplit(TChargingOrder chargingOrder, BigDecimal electrovalence, BigDecimal serviceCharge) { return ChargingCouponSupport.calculateWhitelistRefundSplit( isWhitelistOrder(chargingOrder), isWhitelistAllFreeOrder(chargingOrder), electrovalence, serviceCharge); } private TVip getVipSnapshot(TAppUserVipDetail vipDetail, Integer vipId) { if (vipDetail != null && StringUtils.hasLength(vipDetail.getVipJson())) { return JSON.parseObject(vipDetail.getVipJson(), TVip.class); } return vipId == null ? null : vipClient.getInfo1(vipId).getData(); } /** * 将会员配置转换为订单统一使用的 0~1 折扣系数。 * 常规会员的计费策略已按系数保存;内部会员后台按“8折”这种 0~10 数值保存。 */ BigDecimal resolveChargingVipDiscount(TVip vip, TAccountingStrategy accountingStrategy) { if (vip == null) { return null; } BigDecimal discount; if (Integer.valueOf(1).equals(vip.getType())) { discount = accountingStrategy == null ? null : accountingStrategy.getDiscount(); } else { discount = vip.getDiscount() == null ? null : vip.getDiscount().divide(BigDecimal.TEN); } return isEffectiveVipDiscount(discount) ? discount : null; } boolean isEffectiveVipDiscount(BigDecimal discount) { return discount != null && discount.compareTo(BigDecimal.ZERO) > 0 && discount.compareTo(BigDecimal.ONE) < 0; } boolean shouldConsumeVipDiscount(BigDecimal discountAmount) { return discountAmount != null && discountAmount.compareTo(BigDecimal.ZERO) > 0; } @Override public AjaxResult paymentChargingOrder(AddChargingOrder addChargingOrder) { Long userId = tokenService.getLoginUserApplet().getUserId(); TAppUser appUser = appUserClient.getUserById(userId).getData(); Integer payMethod = addChargingOrder.getPayMethod() == null ? 3 : addChargingOrder.getPayMethod(); BigDecimal rechargeAmount = money(addChargingOrder.getPaymentAmount()); TChargingGun tChargingGun = chargingGunClient.getChargingGunById(addChargingOrder.getId()).getData(); TChargingPile chargingPile = chargingPileClient.getChargingPileById(tChargingGun.getChargingPileId()).getData(); TAppUserCar selectedCar = null; if (addChargingOrder.getAppUserCarId() != null) { selectedCar = appUserCarClient.getCarById(addChargingOrder.getAppUserCarId().toString()).getData(); } String selectedVin = selectedCar == null ? null : normalizeVin(selectedCar.getVin()); if (Integer.valueOf(1).equals(chargingPile.getVinFlag()) && (!StringUtils.hasLength(selectedVin) || !selectedVin.matches("[A-HJ-NPR-Z0-9]{17}"))) { return AjaxResult.error("该充电桩需要17位有效车辆VIN,请先完善所选车辆VIN"); } AppUserWhitelistMatchVO whitelist = appUserWhitelistClient.matchWhitelist(userId, tChargingGun.getSiteId()).getData(); boolean whitelistMatched = whitelist != null && Boolean.TRUE.equals(whitelist.getMatched()); if (whitelistMatched) { addChargingOrder.setAppUserCouponId(null); } boolean whitelistAllFree = whitelistMatched && Integer.valueOf(WHITELIST_BENEFIT_ALL_FREE).equals(whitelist.getBenefitType()); BigDecimal userBalance = money(appUser.getBalance()); BigDecimal balancePayAmount = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); if (payMethod == 2 && userBalance.compareTo(rechargeAmount) >= 0) { payMethod = 1; addChargingOrder.setPayMethod(1); } if (isBalancePayMode(payMethod)) { addChargingOrder.setAppUserCouponId(null); } else if (!whitelistMatched && addChargingOrder.getAppUserCouponId() != null) { // 这里只校验优惠券,不抵扣预付金额;实际门槛和优惠金额在充电结束时计算。 validateSelectedChargingCoupon(addChargingOrder.getAppUserCouponId(), userId); } BigDecimal payableAmount = ChargingCouponSupport.calculatePrepaymentAmount(rechargeAmount, whitelistAllFree); BigDecimal thirdPayAmount = payableAmount; if (isBalancePayMode(payMethod)) { if (payMethod == 1 && userBalance.compareTo(payableAmount) < 0) { return AjaxResult.error("余额不足,请选择组合支付或第三方支付"); } balancePayAmount = payMethod == 1 ? payableAmount : userBalance.min(payableAmount).setScale(2, RoundingMode.HALF_UP); thirdPayAmount = payableAmount.subtract(balancePayAmount).setScale(2, RoundingMode.HALF_UP); } payMethod = ChargingPaymentSupport.resolvePayMethod(balancePayAmount, thirdPayAmount, payMethod); addChargingOrder.setPayMethod(payMethod); //检查当前枪是否是正在使用中 TChargingOrder one = this.getOne(new LambdaQueryWrapper().eq(TChargingOrder::getChargingGunId, addChargingOrder.getId()) .in(TChargingOrder::getStatus, Arrays.asList(3)).eq(TChargingOrder::getDelFlag, 0)); if (null != one) { return AjaxResult.error("充电枪正在充电中,启动失败"); } //查询当前充电枪是否有正在支付的订单 one = this.getOne(new LambdaQueryWrapper().eq(TChargingOrder::getChargingGunId, addChargingOrder.getId()) .eq(TChargingOrder::getAppUserId, userId).eq(TChargingOrder::getRechargePaymentStatus, 1) .eq(TChargingOrder::getDelFlag, 0).last("limit 1")); if (null != one) { //查询三方支付数据,支付中直接结束三方订单 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 (trade_state.equals("REFUND") || trade_state.equals("NOTPAY") || trade_state.equals("REVOKED") || trade_state.equals("PAYERROR")) { this.removeById(one.getId()); } else { //结束第三方支付,删除订单 wxPaymentClient.close(one.getCode()); this.removeById(one.getId()); } } } if (!"ICBC".equals(one.getSourceName()) && 2 == one.getRechargePaymentType()) { AliQueryOrder data = aliPaymentClient.query(one.getCode()).getData(); if (null != data) { String trade_state = data.getTradeStatus(); //支付失败,删除无效的订单 if (trade_state.equals("TRADE_CLOSED")) { this.removeById(one.getId()); } if (trade_state.equals("WAIT_BUYER_PAY")) { //结束第三方支付,删除订单 aliPaymentClient.close(one.getCode()); this.removeById(one.getId()); } } } if (!"ICBC".equals(one.getSourceName()) && 3 == one.getRechargePaymentType()) { AliQueryOrder data = h5AliPaymentClient.query(one.getCode()).getData(); if (null != data) { String trade_state = data.getTradeStatus(); //支付失败,删除无效的订单 if (trade_state.equals("TRADE_CLOSED")) { this.removeById(one.getId()); } if (trade_state.equals("WAIT_BUYER_PAY")) { //结束第三方支付,删除订单 h5AliPaymentClient.close(one.getCode()); this.removeById(one.getId()); } } } } //构建新的待支付订单 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); TChargingOrder chargingOrder = new TChargingOrder(); String code = "CD" + Double.valueOf(Math.random() * 1000).intValue() + sdf.format(new Date()); chargingOrder.setCode(code); chargingOrder.setOrderType(1); chargingOrder.setOrderClassification(1); chargingOrder.setAppUserId(userId); chargingOrder.setAppUserCarId(addChargingOrder.getAppUserCarId()); if (selectedCar != null) { chargingOrder.setPlateNum(selectedCar.getLicensePlate()); chargingOrder.setExpectedVin(selectedVin); } chargingOrder.setReservationTime(addChargingOrder.getReservationTime()); chargingOrder.setSiteId(tChargingGun.getSiteId()); chargingOrder.setChargingPileId(tChargingGun.getChargingPileId()); chargingOrder.setChargingGunId(addChargingOrder.getId()); chargingOrder.setRechargePaymentType(addChargingOrder.getPaymentType()); chargingOrder.setRechargePaymentStatus(1); chargingOrder.setPayMethod(payMethod); chargingOrder.setBalancePayAmount(balancePayAmount); chargingOrder.setThirdPayAmount(thirdPayAmount); chargingOrder.setBalanceRefundAmount(BigDecimal.ZERO); chargingOrder.setThirdRefundAmount(BigDecimal.ZERO); chargingOrder.setRechargeAmount(rechargeAmount); chargingOrder.setAppCouponId(addChargingOrder.getAppUserCouponId()); chargingOrder.setCouponDiscountAmount(BigDecimal.ZERO); chargingOrder.setIsWhitelist(whitelistMatched ? 1 : 0); chargingOrder.setWhitelistId(whitelistMatched ? whitelist.getWhitelistId() : null); chargingOrder.setWhitelistBenefitType(whitelistMatched ? whitelist.getBenefitType() : null); chargingOrder.setWhitelistDiscountAmount(BigDecimal.ZERO); chargingOrder.setWhitelistElectricRefundAmount(BigDecimal.ZERO); 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); AjaxResult reservationValidation = validateReservation(site, addChargingOrder.getReservationTime()); if (reservationValidation != null) { return reservationValidation; } Integer accountingStrategyId = tChargingGun.getAccountingStrategyId(); if (null == accountingStrategyId) { //查询站点上面的计费策略 accountingStrategyId = site.getAccountingStrategyId(); } TAccountingStrategy accountingStrategy = accountingStrategyClient.getAccountingStrategyById(accountingStrategyId).getData(); //会员折扣支持直营网点和加盟商站点 if (!whitelistMatched && Integer.valueOf(1).equals(site.getMemberDiscountFlag()) && null != appUser.getVipId()) { GetAppUserVipDetail getAppUserVipDetail = new GetAppUserVipDetail(); getAppUserVipDetail.setAppUserId(appUser.getId()); getAppUserVipDetail.setVipId(appUser.getVipId()); TAppUserVipDetail vipDetail = appUserVipDetailClient.getAppUserVipDetail(getAppUserVipDetail).getData(); TVip vip = getVipSnapshot(vipDetail, appUser.getVipId()); if (null != vip && null != vipDetail) { BigDecimal discount = resolveChargingVipDiscount(vip, accountingStrategy); Integer chargeNum = vipDetail.getChargeNum(); if (null != discount && null != chargeNum && 0 < chargeNum) { chargingOrder.setVipDiscount(discount); } } } this.save(chargingOrder); //添加订单的计费策略 List strategyDetailList = accountingStrategyDetailClient.getListByAccountingStrategyId(accountingStrategyId).getData(); AccountingStrategyOrder accountingStrategyOrder = new AccountingStrategyOrder(); BeanUtils.copyProperties(accountingStrategy, accountingStrategyOrder); accountingStrategyOrder.setChargingOrderId(chargingOrder.getId()); accountingStrategyOrderService.save(accountingStrategyOrder); List list1 = new ArrayList<>(); for (TAccountingStrategyDetail tAccountingStrategyDetail : strategyDetailList) { AccountingStrategyDetailOrder accountingStrategyDetailOrder = new AccountingStrategyDetailOrder(); BeanUtils.copyProperties(tAccountingStrategyDetail, accountingStrategyDetailOrder); accountingStrategyDetailOrder.setChargingOrderId(chargingOrder.getId()); accountingStrategyDetailOrder.setCreateTime(LocalDateTime.now()); accountingStrategyDetailOrder.setDelFlag(false); list1.add(accountingStrategyDetailOrder); } accountingStrategyDetailOrderService.saveBatch(list1); if (thirdPayAmount.compareTo(BigDecimal.ZERO) == 0 && balancePayAmount.compareTo(BigDecimal.ZERO) > 0) { changeUserBalance(userId, balancePayAmount.negate()); recordBalanceConsume(chargingOrder, balancePayAmount); } if (thirdPayAmount.compareTo(BigDecimal.ZERO) == 0) { return completeChargingOrderPayment(chargingOrder, whitelistMatched ? "WHITELIST" : "BALANCE"); } //会员优惠折扣将其计入增加充电时长(增加总充电金额) //如果充电总金额未使用完,则需要退回费用=(原金额/总金额)*(总金额-实际充电金额) if (Integer.valueOf(1).equals(addChargingOrder.getPaymentType())) { IcbcPaymentReq req = new IcbcPaymentReq(); req.setOutTradeNo(chargingOrder.getCode()); req.setAmount(thirdPayAmount); req.setSubject("充电充值"); req.setBody("充电充值"); req.setPaymentType(1); req.setNotifyUrl("/payment/icbc/callBack"); req.setReturnUrl(addChargingOrder.getReturnUrl()); req.setOpenId(appUser.getWxOpenid()); req.setProfitSharing(buildIcbcProfitSharing(site)); R result = icbcPaymentClient.payment(req); if (result == null || R.isError(result)) { String reason = result == null ? "工行支付服务无响应" : firstNotBlank(result.getMsg(), "工行支付服务调用失败"); log.error("调起工行支付失败,订单号:{},原因:{}", chargingOrder.getCode(), reason); throw new RuntimeException("调起工行支付失败:" + reason); } IcbcPaymentResp data = result.getData(); if (isIcbcPaymentCreated(data)) { if (StringUtils.hasLength(data.getTradeNo())) { chargingOrder.setRechargeSerialNumber(data.getTradeNo()); this.updateById(chargingOrder); } return AjaxResult.success(data); } String reason = buildIcbcPaymentFailReason(data); log.error("调起工行支付失败,订单号:{},工行返回:{}", chargingOrder.getCode(), reason); throw new RuntimeException("调起工行支付失败:" + reason); } if (Integer.valueOf(2).equals(addChargingOrder.getPaymentType())) { AliPaymentReq req = new AliPaymentReq(); req.setOutTradeNo(chargingOrder.getCode()); req.setTotalAmount(thirdPayAmount.toString()); req.setSubject("充电充值"); req.setBuyerOpenId(appUser.getAliOpenid()); req.setBody("充电充值"); req.setNotifyUrl("/payment/ali/callBack"); AliPaymentResp data = aliPaymentClient.payment(req).getData(); if (data != null) { return AjaxResult.success(data); } throw new RuntimeException("调起支付宝支付失败"); } if (Integer.valueOf(3).equals(addChargingOrder.getPaymentType())) { AliPaymentReq req = new AliPaymentReq(); req.setOutTradeNo(chargingOrder.getCode()); req.setTotalAmount(thirdPayAmount.toString()); req.setSubject("充电充值"); req.setBuyerOpenId(appUser.getAliOpenid()); req.setBody("充电充值"); req.setNotifyUrl("/payment/aliH5/callBack"); req.setReturnUrl(addChargingOrder.getReturnUrl()); String data = h5AliPaymentClient.payment(req).getData(); if (StringUtils.hasLength(data)) { return AjaxResult.success("操作成功", data); } } throw new RuntimeException("无效的支付方式"); } @Override public ChargingSimulationVO getCurrentSimulationOrder(Integer gunId) { if (gunId == null) { throw new ServiceException("充电枪不能为空"); } Long userId = tokenService.getLoginUserApplet().getUserId(); return buildSimulationVO(findSimulationOrder(userId, gunId)); } @Override public ChargingSimulationVO completeSimulation(ChargingSimulationReq request) { if (request == null || request.getGunId() == null) { throw new ServiceException("充电枪不能为空"); } Long userId = tokenService.getLoginUserApplet().getUserId(); TChargingOrder order = findSimulationOrder(userId, request.getGunId()); List monitoringData = uploadRealTimeMonitoringDataService.getDataByOrderCode(order.getCode()); if (monitoringData != null && !monitoringData.isEmpty()) { throw new ServiceException("订单已存在真实充电监控数据,禁止模拟结算"); } BigDecimal electricityFee = money(request.getElectricityFee()); BigDecimal serviceFee = money(request.getServiceFee()); BigDecimal simulationTotal = electricityFee.add(serviceFee).setScale(2, RoundingMode.HALF_UP); BigDecimal availableAmount = ChargingSimulationSupport.availableAmount(order); if (simulationTotal.compareTo(availableAmount) > 0) { throw new ServiceException("模拟消费金额不能超过订单可消费金额" + availableAmount.toPlainString() + "元"); } List tariffs = accountingStrategyDetailOrderService.list( new LambdaQueryWrapper() .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() .eq(TChargingOrder::getAppUserId, userId) .eq(TChargingOrder::getChargingGunId, gunId) .eq(TChargingOrder::getRechargePaymentStatus, 2) .in(TChargingOrder::getStatus, Arrays.asList(2, 3, 4)) .eq(TChargingOrder::getDelFlag, 0) .isNull(TChargingOrder::getRefundStatus) .orderByDesc(TChargingOrder::getId) .last("limit 1")); if (order == null) { throw new ServiceException("当前枪没有属于你的可模拟已支付订单"); } return order; } private ChargingSimulationVO buildSimulationVO(TChargingOrder order) { ChargingSimulationVO result = new ChargingSimulationVO(); result.setOrderId(order.getId()); result.setOrderCode(order.getCode()); result.setOrderStatus(order.getStatus()); result.setPaymentMode(ChargingSimulationSupport.paymentMode(order)); result.setPayMethod(order.getPayMethod()); result.setRechargePaymentType(order.getRechargePaymentType()); result.setSourceName(order.getSourceName()); result.setRechargeAmount(order.getRechargeAmount()); result.setChargeAmount(order.getChargeAmount()); result.setAvailableAmount(ChargingSimulationSupport.availableAmount(order)); result.setBalancePayAmount(order.getBalancePayAmount()); result.setThirdPayAmount(order.getThirdPayAmount()); result.setElectricityFee(order.getElectrovalence()); result.setServiceFee(order.getServiceCharge()); result.setPaymentAmount(order.getPaymentAmount()); result.setRefundAmount(order.getRefundAmount()); result.setBalanceRefundAmount(order.getBalanceRefundAmount()); result.setThirdRefundAmount(order.getThirdRefundAmount()); result.setRefundStatus(order.getRefundStatus()); result.setIcbcOrderId(order.getRechargeSerialNumber()); TChargingOrderShareRecord shareRecord = chargingOrderShareRecordMapper.selectOne( new LambdaQueryWrapper() .eq(TChargingOrderShareRecord::getChargingOrderId, order.getId()) .eq(TChargingOrderShareRecord::getPayChannel, "ICBC") .eq(TChargingOrderShareRecord::getDelFlag, false) .orderByDesc(TChargingOrderShareRecord::getId) .last("limit 1")); if (shareRecord == null) { shareRecord = chargingOrderShareRecordMapper.selectOne( new LambdaQueryWrapper() .eq(TChargingOrderShareRecord::getChargingOrderId, order.getId()) .eq(TChargingOrderShareRecord::getDelFlag, false) .orderByDesc(TChargingOrderShareRecord::getId) .last("limit 1")); } if (shareRecord != null) { result.setShareAmount(shareRecord.getShareAmount()); result.setShareStatus(shareRecord.getOnlineShareStatus()); result.setSubOrderId(shareRecord.getSubOrderId()); result.setSubOrderSeq(shareRecord.getSubOrderSeq()); result.setSubOrderTrxid(shareRecord.getSubOrderTrxid()); } return result; } @Override public IcbcPaymentResp queryIcbcAdminPayment(String orderCode) { TChargingOrder order = loadIcbcAdminOrder(orderCode, false); return requireFeignData(icbcPaymentClient.query(order.getCode()), "工行支付查询失败"); } @Transactional @Override public AjaxResult confirmIcbcPayment(String orderCode) { if (!StringUtils.hasLength(orderCode)) { return AjaxResult.error("订单号不能为空"); } Long userId = tokenService.getLoginUserApplet().getUserId(); TChargingOrder order = this.getOne(new LambdaQueryWrapper() .eq(TChargingOrder::getCode, orderCode) .eq(TChargingOrder::getAppUserId, userId) .eq(TChargingOrder::getDelFlag, 0) .last("limit 1")); if (order == null) { return AjaxResult.error("支付订单不存在"); } if (Integer.valueOf(2).equals(order.getRechargePaymentStatus())) { return AjaxResult.success(Collections.singletonMap("confirmed", true)); } if (!isIcbcOrder(order)) { return AjaxResult.error("当前订单不是工行支付订单"); } R queryResult = icbcPaymentClient.query(orderCode); IcbcPaymentResp payment = queryResult == null ? null : queryResult.getData(); if (!IcbcAdminFundOperationSupport.isBankPaid(payment) || payment == null || !orderCode.equals(payment.getOrderId()) || !StringUtils.hasLength(payment.getTradeNo())) { return AjaxResult.success(Collections.singletonMap("confirmed", false)); } AjaxResult callbackResult = chargingOrderCallback(1, orderCode, payment.getTradeNo(), "1"); if (!Integer.valueOf(200).equals(callbackResult.get("code"))) { return callbackResult; } return AjaxResult.success(Collections.singletonMap("confirmed", true)); } @Override public IcbcRefundResp refundIcbcAdminPayment(IcbcAdminFundOperationReq request) { if (request == null) { throw new ServiceException("请求参数不能为空"); } TChargingOrder order = loadIcbcAdminOrder(request.getOrderCode(), false); BigDecimal amount; try { amount = IcbcAdminFundOperationSupport.requireAmount(request.getAmount()); } catch (IllegalArgumentException e) { throw new ServiceException(e.getMessage()); } BigDecimal paidAmount = money(order.getThirdPayAmount()).compareTo(BigDecimal.ZERO) > 0 ? money(order.getThirdPayAmount()) : money(order.getRechargeAmount()); if (amount.compareTo(paidAmount) > 0) { throw new ServiceException("退款金额不能超过订单第三方支付金额" + paidAmount.toPlainString() + "元"); } RLock lock = redissonClient.getLock("icbcAdminFund:order:" + order.getCode()); if (!lock.tryLock()) { throw new ServiceException("该订单正在执行工行资金操作,请勿重复提交"); } try { IcbcPaymentResp payment = requireFeignData(icbcPaymentClient.query(order.getCode()), "工行支付查询失败"); if (!IcbcAdminFundOperationSupport.isBankPaid(payment)) { throw new ServiceException("工行查询未确认该订单支付成功,禁止退款"); } String orderId = extractIcbcOrderId(payment); order.setRechargeSerialNumber(orderId); // 仅用于本次请求通过退款参数校验,不更新本地订单状态。 order.setRechargePaymentStatus(2); RBucket 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 queryIcbcAdminSubOrders(String orderCode) { TChargingOrder order = loadIcbcAdminOrder(orderCode, true); List records = chargingOrderShareRecordMapper.selectList( new LambdaQueryWrapper() .eq(TChargingOrderShareRecord::getChargingOrderId, order.getId()) .eq(TChargingOrderShareRecord::getPayChannel, "ICBC") .eq(TChargingOrderShareRecord::getDelFlag, false) .orderByAsc(TChargingOrderShareRecord::getId)); List result = new ArrayList<>(); for (TChargingOrderShareRecord record : records) { IcbcAdminSubOrderVO item = buildIcbcAdminSubOrderVO(record); if (!Boolean.TRUE.equals(record.getNeedOnlineShare())) { item.setQueryStatus("SKIP"); item.setErrorMessage(record.getFailReason()); result.add(item); continue; } try { IcbcSubOrderQueryReq query = IcbcAdminFundOperationSupport.buildSubOrderQueryRequest(order, record); item.applyQuery(requireFeignData(icbcPaymentClient.querySubOrder(query), "工行子订单查询失败")); } catch (Exception e) { item.setQueryStatus("FAIL"); item.setErrorMessage(e.getMessage()); } result.add(item); } return result; } @Override public IcbcSubOrderRefundResp refundIcbcAdminSubOrder(IcbcAdminFundOperationReq request) { if (request == null || request.getShareRecordId() == null) { throw new ServiceException("请选择需要退款的分账子订单"); } TChargingOrder order = loadIcbcAdminOrder(request.getOrderCode(), true); TChargingOrderShareRecord shareRecord = chargingOrderShareRecordMapper.selectById(request.getShareRecordId()); if (shareRecord == null || !order.getId().equals(shareRecord.getChargingOrderId())) { throw new ServiceException("分账记录不属于当前订单"); } BigDecimal amount; try { amount = IcbcAdminFundOperationSupport.requireAmount(request.getAmount()); } catch (IllegalArgumentException e) { throw new ServiceException(e.getMessage()); } if (shareRecord.getShareAmount() != null && amount.compareTo(shareRecord.getShareAmount()) > 0) { throw new ServiceException("分账退款金额不能超过分账金额" + shareRecord.getShareAmount().toPlainString() + "元"); } RLock lock = redissonClient.getLock("icbcAdminFund:order:" + order.getCode()); if (!lock.tryLock()) { throw new ServiceException("该分账子订单正在退款,请勿重复提交"); } try { String suffix = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")); RBucket pendingRefund = redissonClient.getBucket("icbcAdminFund:pending:sub:" + shareRecord.getId()); String generatedToken = "ICBCSUBREFUND" + suffix + ":ICBCSUBREFUNDNO" + suffix; String pendingValue = pendingRefund.get(); boolean reusingPending = StringUtils.hasLength(pendingValue); String operationToken; try { operationToken = IcbcAdminFundOperationSupport.resolveRefundNo(pendingValue, amount, generatedToken); } catch (IllegalArgumentException e) { throw new ServiceException(e.getMessage()); } String[] refundNumbers = operationToken.split(":", 2); if (refundNumbers.length != 2) { throw new ServiceException("工行分账退款幂等记录格式错误"); } IcbcSubOrderRefundReq refundRequest; try { refundRequest = IcbcAdminFundOperationSupport.buildSubOrderRefundRequest( order, shareRecord, amount, refundNumbers[0], refundNumbers[1]); } catch (IllegalArgumentException e) { throw new ServiceException(e.getMessage()); } if (!reusingPending) { pendingRefund.set(IcbcAdminFundOperationSupport.buildPendingRefundValue(operationToken, amount)); } IcbcSubOrderRefundResp response = requireFeignData( icbcPaymentClient.refundSubOrder(refundRequest), "工行分账退款调用失败"); if (IcbcAdminFundOperationSupport.shouldClearPendingRefund( reusingPending, response.getStatus(), isDefinitiveIcbcResponse(response.getRawResponse()))) { pendingRefund.delete(); } return response; } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } private TChargingOrder loadIcbcAdminOrder(String orderCode, boolean requireLocalPaid) { if (!StringUtils.hasLength(orderCode)) { throw new ServiceException("订单号不能为空"); } TChargingOrder order = this.getOne(new LambdaQueryWrapper() .eq(TChargingOrder::getCode, orderCode) .eq(TChargingOrder::getDelFlag, 0) .last("limit 1")); try { if (requireLocalPaid) { IcbcAdminFundOperationSupport.validateOrder(order); } else { IcbcAdminFundOperationSupport.validateIcbcOrder(order); } } catch (IllegalArgumentException e) { throw new ServiceException(e.getMessage()); } return order; } private String extractIcbcOrderId(IcbcPaymentResp payment) { try { JSONObject response = JSON.parseObject(payment.getRawResponse()); String orderId = response == null ? null : response.getString("order_id"); if (StringUtils.hasLength(orderId)) { return orderId; } } catch (Exception ignored) { } throw new ServiceException("工行支付查询未返回真实order_id,禁止退款"); } private boolean isDefinitiveIcbcResponse(String rawResponse) { if (!StringUtils.hasLength(rawResponse)) { return false; } try { JSONObject response = JSON.parseObject(rawResponse); return response != null && (response.containsKey("returnCode") || response.containsKey("return_code")); } catch (Exception ignored) { return false; } } private IcbcAdminSubOrderVO buildIcbcAdminSubOrderVO(TChargingOrderShareRecord record) { IcbcAdminSubOrderVO item = new IcbcAdminSubOrderVO(); item.setShareRecordId(record.getId()); item.setOrderCode(record.getChargingOrderCode()); item.setShareAmount(record.getShareAmount()); item.setOnlineShareStatus(record.getOnlineShareStatus()); item.setSubOrderId(record.getSubOrderId()); item.setSubMerId(record.getSubMerId()); item.setSubMerPrtclNo(record.getSubMerPrtclNo()); item.setOriTrxDate(record.getOriTrxDate()); item.setBankOrderId(record.getIcbcOrderId()); item.setSubOrderSeq(record.getSubOrderSeq()); item.setSubOrderTrxid(record.getSubOrderTrxid()); return item; } private T requireFeignData(R 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(); } /** * 支付成功后的回调处理逻辑 * * @param paymentType 支付方式:1=微信,2=支付宝 * @param out_trade_no 业务流水号 * @param transaction_id 三方支付流水号 * @param attach 附加数据 * @return */ @Transactional @Override public AjaxResult chargingOrderCallback(Integer paymentType, String out_trade_no, String transaction_id, String attach) { TChargingOrder chargingOrder = this.getOne(new LambdaQueryWrapper().eq(TChargingOrder::getCode, out_trade_no)); if (chargingOrder == null) { return AjaxResult.error("支付回调订单不存在"); } return completeChargingOrderPayment(chargingOrder, transaction_id); } /** * 完成订单支付状态和充电额度初始化。 * 本地余额、白名单零元支付直接传入刚保存的订单,避免读写分离下立即回查读不到新订单。 */ AjaxResult completeChargingOrderPayment(TChargingOrder chargingOrder, String transactionId) { if (chargingOrder.getRechargePaymentStatus() == 2) { if (isIcbcOrder(chargingOrder) && StringUtils.hasLength(transactionId) && !transactionId.equals(chargingOrder.getRechargeSerialNumber())) { chargingOrder.setRechargeSerialNumber(transactionId); this.updateById(chargingOrder); } return AjaxResult.success(); } LocalDateTime callbackPayTime = LocalDateTime.now(); int claimed = this.baseMapper.update(null, new LambdaUpdateWrapper() .eq(TChargingOrder::getId, chargingOrder.getId()) .eq(TChargingOrder::getRechargePaymentStatus, 1) .set(TChargingOrder::getRechargePaymentStatus, 2) .set(TChargingOrder::getRechargeSerialNumber, transactionId) .set(TChargingOrder::getStatus, 2) .set(TChargingOrder::getPayTime, callbackPayTime)); if (claimed == 0) { return AjaxResult.success(); } if (isBalancePayMode(chargingOrder.getPayMethod()) && money(chargingOrder.getThirdPayAmount()).compareTo(BigDecimal.ZERO) > 0 && money(chargingOrder.getBalancePayAmount()).compareTo(BigDecimal.ZERO) > 0) { TAppUser appUser = appUserClient.getUserById(chargingOrder.getAppUserId()).getData(); if (money(appUser.getBalance()).compareTo(money(chargingOrder.getBalancePayAmount())) < 0) { throw new RuntimeException("余额不足,支付回调处理失败"); } changeUserBalance(chargingOrder.getAppUserId(), money(chargingOrder.getBalancePayAmount()).negate()); recordBalanceConsume(chargingOrder, money(chargingOrder.getBalancePayAmount())); } chargingOrder.setRechargePaymentStatus(2); chargingOrder.setRechargeSerialNumber(transactionId); chargingOrder.setStatus(2); chargingOrder.setPayTime(callbackPayTime); //添加安全检测数据到缓存中,每步安全检测完成后需要更新缓存数据 PreChargeCheck preChargeCheck = new PreChargeCheck(); preChargeCheck.setElectronicLockLock(false); preChargeCheck.setInsulationTesting(false); preChargeCheck.setSecureConnectionDetection(false); preChargeCheck.setStartupSuccess(1); preChargeCheck.setStartCommandSent(false); preChargeCheck.setAddTime(chargingOrder.getReservationTime() != null ? chargingOrder.getReservationTime() : LocalDateTime.now()); String key = "AQJC_" + chargingOrder.getChargingGunId(); redisService.setCacheObject(key, preChargeCheck, 24L, TimeUnit.HOURS); //根据当前充值的金额和计费模板算出充电的金额 BigDecimal rechargeAmount = chargingOrder.getRechargeAmount(); BigDecimal rechargeAmount1 = new BigDecimal(rechargeAmount.toString()); SimpleDateFormat sdf = new SimpleDateFormat("HHmm"); Long now = Long.valueOf(sdf.format(new Date())); Long nowTimeMillis = System.currentTimeMillis(); //根据支付金额,获取出使用的计费策略明细 //计算电费金额和服务费 BigDecimal serviceCharge = BigDecimal.ZERO; BigDecimal discountAmount = BigDecimal.ZERO; BigDecimal discount = chargingOrder.getVipDiscount(); //先根据额定功率计算出每秒充电度数,然后计算出支付金额能充多少度电 TChargingGun tChargingGun = chargingGunClient.getChargingGunById(chargingOrder.getChargingGunId()).getData(); //1秒充电度数 BigDecimal s_degrees = tChargingGun.getRatedPower().divide(new BigDecimal(3600), 6, RoundingMode.HALF_EVEN); TAppUser appUser = appUserClient.getUserById(chargingOrder.getAppUserId()).getData(); //判断会员是否还有充电优惠次数,计算会员优惠的折扣金额 if (!isWhitelistOrder(chargingOrder) && null != appUser.getVipId() && isEffectiveVipDiscount(discount)) { GetAppUserVipDetail getAppUserVipDetail = new GetAppUserVipDetail(); getAppUserVipDetail.setAppUserId(chargingOrder.getAppUserId()); getAppUserVipDetail.setVipId(appUser.getVipId()); TAppUserVipDetail data = appUserVipDetailClient.getAppUserVipDetail(getAppUserVipDetail).getData(); if (null != data && data.getChargeNum() != null && data.getChargeNum() > 0) { //计算折扣 List list = accountingStrategyDetailOrderClient.getAllAccountingStrategyDetailOrder(chargingOrder.getId()).getData(); list.get(list.size() - 1).setEndTime("23:59"); //将数据叠加两份,处理跨天的情况 list.addAll(list); boolean sta = false; for (AccountingStrategyDetailOrder accountingStrategyDetailOrder : list) { Integer start = Integer.valueOf(accountingStrategyDetailOrder.getStartTime().replaceAll(":", "")); Integer end = Integer.valueOf(accountingStrategyDetailOrder.getEndTime().replaceAll(":", "")); String[] split = accountingStrategyDetailOrder.getEndTime().split(":"); if (sta || (now >= start && now < end)) { sta = true; Calendar end_calendar = Calendar.getInstance(); end_calendar.set(Calendar.HOUR_OF_DAY, Integer.valueOf(split[0])); end_calendar.set(Calendar.MINUTE, Integer.valueOf(split[1])); //到此策略结束的秒数 if (null == nowTimeMillis) { String[] split1 = accountingStrategyDetailOrder.getStartTime().split(":"); Calendar start_calendar = Calendar.getInstance(); start_calendar.set(Calendar.HOUR_OF_DAY, Integer.valueOf(split1[0])); start_calendar.set(Calendar.MINUTE, Integer.valueOf(split1[1])); nowTimeMillis = start_calendar.getTimeInMillis(); } //间隔秒数 long m = (end_calendar.getTimeInMillis() - nowTimeMillis) / 1000; //每秒需要支付的服务费金额 BigDecimal total_until = accountingStrategyDetailOrder.getElectrovalence().add(accountingStrategyDetailOrder.getServiceCharge()); BigDecimal s_total_amount = s_degrees.multiply(total_until); //每秒需要支付的服务费金额 BigDecimal s_server_amount = s_degrees.multiply(accountingStrategyDetailOrder.getServiceCharge()); //计算剩余金额能充多长时间的电 long times = rechargeAmount1.divide(s_total_amount, 0, RoundingMode.DOWN).longValue(); if (times > m) { //充电时间跨度两个计费策略,需要继续对下一个策略进行计算 serviceCharge = serviceCharge.add(s_server_amount.multiply(new BigDecimal(m))); rechargeAmount1 = rechargeAmount1.subtract(s_total_amount.multiply(new BigDecimal(m))); nowTimeMillis = null; } else { serviceCharge = serviceCharge.add(s_server_amount.multiply(new BigDecimal(times))); break; } } } discountAmount = serviceCharge.multiply(BigDecimal.ONE.subtract(discount)); //计算会员最大优惠金额 TVip vip = getVipSnapshot(data, appUser.getVipId()); BigDecimal maximumDeduction = vip == null ? null : vip.getMaximumDeduction(); //普通会员有最高优惠限制 if (vip != null && Integer.valueOf(1).equals(vip.getType()) && maximumDeduction != null && maximumDeduction.compareTo(BigDecimal.ZERO) >= 0 && discountAmount.compareTo(maximumDeduction) > 0) { discountAmount = maximumDeduction; } discountAmount = discountAmount.setScale(4, RoundingMode.HALF_EVEN); if (shouldConsumeVipDiscount(discountAmount)) { data.setChargeNum(data.getChargeNum() - 1); appUserVipDetailClient.updateAppUserVipDetail(data); } } } //机子上显示的金额为用户充值的金额+会员折扣金额 BigDecimal account_balance = chargingOrder.getRechargeAmount().add(discountAmount).setScale(4, RoundingMode.HALF_EVEN); chargingOrder.setChargeAmount(account_balance); chargingOrder.setVipDiscountAmount(discountAmount); this.updateById(chargingOrder); return AjaxResult.success(); } private AjaxResult validateReservation(Site site, LocalDateTime reservationTime) { if (reservationTime == null) { return null; } if (!Integer.valueOf(1).equals(site.getSupportOrder())) { return AjaxResult.error("该电站暂不支持预约充电"); } if (reservationTime.isBefore(LocalDateTime.now().minusMinutes(1))) { return AjaxResult.error("预约时间不能早于当前时间"); } if (site.getReservationAheadMinute() != null && site.getReservationAheadMinute() >= 0 && reservationTime.isAfter(LocalDateTime.now().plusMinutes(site.getReservationAheadMinute()))) { return AjaxResult.error("预约时间不能超过最早可提前预约时限"); } List ranges; try { ranges = ReservationTimeRangeSupport.parse(site.getReservationTimeRanges(), site.getReservationStartTime(), site.getReservationEndTime()); } catch (IllegalArgumentException e) { return AjaxResult.error("电站预约时段配置不正确"); } if (ranges.isEmpty()) { return AjaxResult.error("电站尚未配置预约时段"); } if (!ReservationTimeRangeSupport.contains(ranges, reservationTime.toLocalTime())) { return AjaxResult.error("预约时间不在允许预约时段内(" + ReservationTimeRangeSupport.describe(ranges) + ")"); } return null; } private boolean isSafetyCheckPassed(PreChargeCheck check) { return check != null && Boolean.TRUE.equals(check.getElectronicLockLock()) && Boolean.TRUE.equals(check.getInsulationTesting()) && Boolean.TRUE.equals(check.getSecureConnectionDetection()); } private String normalizeVin(String vin) { return StringUtils.hasLength(vin) ? vin.replaceAll("\\s+", "").toUpperCase(Locale.ROOT) : null; } private boolean vinMatches(String expectedVin, String actualVin) { if (!StringUtils.hasLength(expectedVin) || !StringUtils.hasLength(actualVin)) { return false; } return expectedVin.equals(actualVin) || expectedVin.equals(new StringBuilder(actualVin).reverse().toString()); } @Scheduled(fixedDelay = 5000L) public void startDueReservationOrders() { LocalDateTime now = LocalDateTime.now(); List dueOrders = this.list(new LambdaQueryWrapper() .eq(TChargingOrder::getRechargePaymentStatus, 2) .eq(TChargingOrder::getStatus, 2) .eq(TChargingOrder::getOrderSource, 1) .eq(TChargingOrder::getDelFlag, 0) .isNotNull(TChargingOrder::getReservationTime) .ge(TChargingOrder::getReservationTime, now.minusHours(24)) .le(TChargingOrder::getReservationTime, now) .orderByAsc(TChargingOrder::getReservationTime) .last("limit 100")); for (TChargingOrder order : dueOrders) { try { PreChargeCheck check = redisService.getCacheObject("AQJC_" + order.getChargingGunId()); if (isSafetyCheckPassed(check)) { startChargingAfterSafety(order, check); } } catch (Exception e) { log.error("预约充电到时启动失败,orderCode={}", order.getCode(), e); } } } private void startChargingAfterSafety(TChargingOrder chargingOrder, PreChargeCheck check) { RLock lock = redissonClient.getLock("START_AFTER_SAFETY:" + chargingOrder.getId()); try { if (!lock.tryLock(1, 10, TimeUnit.SECONDS)) { return; } TChargingOrder latest = this.getById(chargingOrder.getId()); if (latest == null || latest.getStatus() != 2 || Boolean.TRUE.equals(check.getStartCommandSent())) { return; } if (!ReservationOrderSupport.isStartDue(latest.getReservationTime(), LocalDateTime.now())) { return; } TChargingGun gun = chargingGunClient.getChargingGunById(latest.getChargingGunId()).getData(); TChargingPile pile = chargingPileClient.getChargingPileById(gun.getChargingPileId()).getData(); String actualVin = normalizeVin(check.getActualVin()); String expectedVin = normalizeVin(latest.getExpectedVin()); if (Integer.valueOf(1).equals(pile.getVinFlag()) && !vinMatches(expectedVin, actualVin)) { check.setStartupSuccess(3); check.setFailureCause(9); latest.setActualVin(actualVin); this.updateById(latest); redisService.setCacheObject("AQJC_" + latest.getChargingGunId(), check, 24L, TimeUnit.HOURS); refund(latest.getCode()); return; } PlatformStartCharging command = new PlatformStartCharging(); command.setTransaction_serial_number(latest.getCode()); command.setCharging_pile_code(pile.getCode()); command.setCharging_gun_code(gun.getCode()); command.setCard_number(latest.getId().toString()); command.setAccount_balance(latest.getChargeAmount()); check.setStartCommandSent(true); latest.setActualVin(actualVin); this.updateById(latest); redisService.setCacheObject("AQJC_" + latest.getChargingGunId(), check, 24L, TimeUnit.HOURS); log.info(latest.getCode() + ":安全检测及VIN校验通过,下发启动充电命令:" + command); sendMessageClient.platformStartCharging(command); ScheduledExecutorService resultScheduler = Executors.newScheduledThreadPool(1); resultScheduler.scheduleAtFixedRate(() -> { if (timingDetection(latest.getId())) { resultScheduler.shutdown(); } }, 10, 1, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new ServiceException("启动充电锁等待被中断"); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } /** * 定时检测mongodb数据库数据 * * @param id * @return */ public boolean timingDetection(Long id) { TChargingOrder chargingOrder = this.getById(id); if (chargingOrder.getStatus() != 2) { return true; } String code = chargingOrder.getCode(); String key = "AQJC_" + chargingOrder.getChargingGunId(); List data = platformStartChargingReplyService.getPlatformStartChargingReply(code); log.info(code + ":-------------------开始检查调起充电结果-------------------" + data.toString()); Site site = siteClient.getSiteByIds(Arrays.asList(chargingOrder.getSiteId())).getData().get(0); if (!data.isEmpty()) { PlatformStartChargingReply platformStartChargingReply = data.get(1); Integer startup_result = platformStartChargingReply.getStartup_result(); Integer failure_cause = platformStartChargingReply.getFailure_cause(); Integer counter = counter_map.get(code); PreChargeCheck preChargeCheck1 = redisService.getCacheObject(key); //15分钟内还未插枪则取消充电,退回金额。 if (failure_cause == 5 && (null == counter || counter < 60 * 15)) { counter = (null == counter ? 0 : counter) + 1; counter_map.put(code, counter); //启动失败 preChargeCheck1.setStartupSuccess(3); preChargeCheck1.setFailureCause(failure_cause); redisService.setCacheObject(key, preChargeCheck1, 24L, TimeUnit.HOURS); return false; } //清除计时器中的无效数据 counter_map.remove(code); if (0 == startup_result) { //启动失败 preChargeCheck1.setStartupSuccess(3); preChargeCheck1.setFailureCause(failure_cause); chargingOrder.setStatus(2); chargingOrder.setEndMode(0); this.updateById(chargingOrder); //启动失败后取消订单,退款操作 refund(code); } else { //启动成功 preChargeCheck1.setStartupSuccess(2); chargingOrder.setStatus(3); chargingOrder.setStartTime(LocalDateTime.now()); this.updateById(chargingOrder); } redisService.setCacheObject(key, preChargeCheck1, 24L, TimeUnit.HOURS); //推送三方平台订单状态 if (2 == chargingOrder.getOrderSource() && 1 == site.getIsOpenSynchronization()) { tcecClient.notificationEquipChargeStatus(chargingOrder.getStartChargeSeq(), chargingOrder.getOperatorId()); } ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); threadPoolExecutor.execute(() -> { if (chargingOrder.getStatus() == 3) { //推送监管平台订单状态 ChargingOrderVo chargingOrderVo = new ChargingOrderVo(); BeanUtils.copyProperties(chargingOrder, chargingOrderVo); chargingMessageClient.pushOrderStatus(chargingOrderVo); } //推送成都监管 NotificationConnectorChargeStatusVo vo = new NotificationConnectorChargeStatusVo(); vo.setOrderCode(chargingOrder.getCode()); vo.setChargingGunId(chargingOrder.getChargingGunId()); chengDuJianGuanClient.notificationConnectorChargeStatus(vo); }); return true; } else { PreChargeCheck preChargeCheck1 = redisService.getCacheObject(key); UploadRealTimeMonitoringData uploadRealTimeMonitoringData = uploadRealTimeMonitoringDataService.getDataByOrderCode(code, "desc"); if (null != uploadRealTimeMonitoringData) { //启动成功 preChargeCheck1.setStartupSuccess(2); chargingOrder.setStatus(3); chargingOrder.setStartTime(LocalDateTime.now()); this.updateById(chargingOrder); redisService.setCacheObject(key, preChargeCheck1, 24L, TimeUnit.HOURS); //推送三方平台订单状态 if (2 == chargingOrder.getOrderSource() && 1 == site.getIsOpenSynchronization()) { tcecClient.notificationEquipChargeStatus(chargingOrder.getStartChargeSeq(), chargingOrder.getOperatorId()); } ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); threadPoolExecutor.execute(() -> { if (chargingOrder.getStatus() == 3) { //推送监管平台订单状态 ChargingOrderVo chargingOrderVo = new ChargingOrderVo(); BeanUtils.copyProperties(chargingOrder, chargingOrderVo); chargingMessageClient.pushOrderStatus(chargingOrderVo); } //推送成都监管 NotificationConnectorChargeStatusVo vo = new NotificationConnectorChargeStatusVo(); vo.setOrderCode(chargingOrder.getCode()); vo.setChargingGunId(chargingOrder.getChargingGunId()); chengDuJianGuanClient.notificationConnectorChargeStatus(vo); }); return true; } else { Integer counter = boot_failed_map.get(code); log.info(code + ":-------------------未上传开启充电结果-------------------" + counter); //15分钟内未启动成功,退回金额。 if (null == counter || counter < 60 * 15) { counter = (null == counter ? 0 : counter) + 1; boot_failed_map.put(code, counter); //启动失败 preChargeCheck1.setStartupSuccess(1); preChargeCheck1.setFailureCause(0); redisService.setCacheObject(key, preChargeCheck1, 24L, TimeUnit.HOURS); return false; } //清除计时器中的无效数据 boot_failed_map.remove(code); TChargingOrder order = new TChargingOrder(); order.setId(id); order.setAppUserId(chargingOrder.getAppUserId()); //启动失败 preChargeCheck1.setStartupSuccess(3); preChargeCheck1.setFailureCause(0); order.setStatus(2); order.setEndMode(0); this.updateById(order); //启动失败后取消订单,退款操作 refund(code); redisService.setCacheObject(key, preChargeCheck1, 24L, TimeUnit.HOURS); //推送三方平台订单状态 if (2 == chargingOrder.getOrderSource() && 1 == site.getIsOpenSynchronization()) { tcecClient.notificationEquipChargeStatus(chargingOrder.getStartChargeSeq(), chargingOrder.getOperatorId()); } ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); threadPoolExecutor.execute(() -> { //推送成都监管 NotificationConnectorChargeStatusVo vo = new NotificationConnectorChargeStatusVo(); vo.setOrderCode(chargingOrder.getCode()); vo.setChargingGunId(chargingOrder.getChargingGunId()); chengDuJianGuanClient.notificationConnectorChargeStatus(vo); }); return true; } } } /** * 修改安全检测数据 * * @param securityDetection */ @Override public void securityDetection(SecurityDetectionVO securityDetection) { GetChargingGunByCode code = new GetChargingGunByCode(); code.setCharging_pile_code(securityDetection.getCharging_pile_code()); code.setCharging_gun_code(securityDetection.getCharging_gun_code()); TChargingGun chargingGun = chargingGunClient.getChargingGunByCode(code).getData(); PreChargeCheck preChargeCheck1 = redisService.getCacheObject("AQJC_" + chargingGun.getId()); if (null != preChargeCheck1) { preChargeCheck1.setElectronicLockLock(Integer.valueOf(1).equals(securityDetection.getElectronic_lock_lock())); preChargeCheck1.setInsulationTesting(Integer.valueOf(0).equals(securityDetection.getNsulation_detection())); preChargeCheck1.setSecureConnectionDetection(Integer.valueOf(1).equals(securityDetection.getSecure_connection())); preChargeCheck1.setActualVin(normalizeVin(securityDetection.getVin())); redisService.setCacheObject("AQJC_" + chargingGun.getId(), preChargeCheck1, 24L, TimeUnit.HOURS); if (isSafetyCheckPassed(preChargeCheck1)) { TChargingOrder pendingOrder = this.getOne(new LambdaQueryWrapper() .eq(TChargingOrder::getChargingGunId, chargingGun.getId()) .eq(TChargingOrder::getRechargePaymentStatus, 2) .eq(TChargingOrder::getStatus, 2) .eq(TChargingOrder::getDelFlag, 0) .orderByDesc(TChargingOrder::getCreateTime) .last("limit 1")); if (pendingOrder != null) { startChargingAfterSafety(pendingOrder, preChargeCheck1); } } } } /** * 启动充电应答 * * @param message */ @Override public void startChargeSuccessfully(PlatformStartChargingReplyMessageVO message) { Integer startup_result = message.getStartup_result(); Integer failure_cause = message.getFailure_cause(); TChargingOrder order = this.getOne(new LambdaQueryWrapper().eq(TChargingOrder::getCode, message.getTransaction_serial_number())); if (order.getStatus() != 2) { return; } Site site = siteClient.getSiteByIds(Arrays.asList(order.getSiteId())).getData().get(0); String code = order.getCode(); PreChargeCheck preChargeCheck1 = redisService.getCacheObject("AQJC_" + order.getChargingGunId()); if (null != preChargeCheck1) { preChargeCheck1.setElectronicLockLock(true); preChargeCheck1.setInsulationTesting(true); preChargeCheck1.setStartupSuccess(1); } if (0 == startup_result) { //启动失败 preChargeCheck1.setStartupSuccess(3); preChargeCheck1.setFailureCause(failure_cause); order.setStatus(2); order.setEndMode(0); this.updateById(order); //启动失败后取消订单,退款操作 refund(code); } else { //启动成功 preChargeCheck1.setStartupSuccess(2); order.setStatus(3); order.setStartTime(LocalDateTime.now()); this.updateById(order); TChargingGun chargingGun = chargingGunClient.getChargingGunById(order.getChargingGunId()).getData(); chargingGun.setStatus(4); chargingGunClient.updateChargingGunById(chargingGun); if (1 == site.getIsOpenSynchronization()) { //推送状态给三方平台 tcecClient.pushChargingGunStatus(chargingGun.getFullNumber(), 4); } wxSubscribeMessageService.sendChargeStart(order, site, chargingGun); } redisService.setCacheObject("AQJC_" + order.getChargingGunId(), preChargeCheck1, 24L, TimeUnit.HOURS); if (3 == order.getStatus()) { //推送监管平台订单状态 new Thread(() -> { ChargingOrderVo chargingOrderVo = new ChargingOrderVo(); BeanUtils.copyProperties(order, chargingOrderVo); chargingMessageClient.pushOrderStatus(chargingOrderVo); }).start(); } //推送三方平台启动充电结果 if (2 == order.getOrderSource()) { StartChargeResult result = new StartChargeResult(); result.setStartChargeSeq(order.getStartChargeSeq()); result.setStartChargeSeqStat(3 == order.getStatus() ? 2 : 4); result.setConnectorID(order.getChargingGunId().toString()); result.setStartTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); Operator operator = operatorClient.getOperatorById(order.getOperatorId()).getData(); result.setOperatorID(operator.getOperatorId()); if (1 == site.getIsOpenSynchronization()) { tcecClient.notificationStartChargeResult(result); tcecClient.notificationEquipChargeStatus(order.getStartChargeSeq(), order.getOperatorId()); } } } /** * 启动失败后的退款,取消订单 * * @param code */ @Transactional public void refund(String code) { RLock lock = redissonClient.getLock("REFUND_ORDER_LOCK:" + code); try { boolean tryLock = lock.tryLock(30, 30, TimeUnit.SECONDS); if (tryLock) { log.info(code + ":-------------------充电启动失败,执行退款-------------------"); TChargingOrder chargingOrder = this.getOne(new LambdaQueryWrapper().eq(TChargingOrder::getCode, code)); if (chargingOrder.getOrderSource() == 1 && chargingOrder.getStatus() == 2 && (chargingOrder.getRefundStatus() == null || chargingOrder.getRefundStatus() != 2)) { Integer rechargePaymentType = chargingOrder.getRechargePaymentType(); BigDecimal rechargeAmount = money(chargingOrder.getRechargeAmount()); BigDecimal totalRefundAmount = isBalancePayMode(chargingOrder.getPayMethod()) ? money(chargingOrder.getBalancePayAmount()).add(money(chargingOrder.getThirdPayAmount())).setScale(2, RoundingMode.HALF_UP) : rechargeAmount; // 白名单全部免费启动失败同样只记录预估金额,避免未支付订单进入实际退款流程。 BigDecimal recordedRefundAmount = isWhitelistAllFreeOrder(chargingOrder) ? rechargeAmount : totalRefundAmount; BigDecimal channelRefundAmount = isWhitelistAllFreeOrder(chargingOrder) ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : totalRefundAmount; BigDecimal thirdRefundAmount = channelRefundAmount.min(getRefundableThirdPayAmount(chargingOrder)).setScale(2, RoundingMode.HALF_UP); BigDecimal balanceRefundAmount = channelRefundAmount.subtract(thirdRefundAmount).min(money(chargingOrder.getBalancePayAmount())).setScale(2, RoundingMode.HALF_UP); //构建退款明细 TChargingOrderRefund chargingOrderRefund = new TChargingOrderRefund(); chargingOrderRefund.setChargingOrderId(chargingOrder.getId()); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); chargingOrderRefund.setRefundCode("CDF" + sdf.format(new Date()) + Double.valueOf(Math.random() * 1000).intValue()); chargingOrderRefund.setRefundAmount(recordedRefundAmount); chargingOrderRefund.setBalanceRefundAmount(balanceRefundAmount); chargingOrderRefund.setThirdRefundAmount(thirdRefundAmount); chargingOrderRefund.setRefundStatus(1); chargingOrderRefund.setPayType(rechargePaymentType); chargingOrderRefund.setRefundStatus(1); chargingOrderRefund.setCode(code); chargingOrderRefund.setRefundTitle("充电失败"); chargingOrderRefund.setRefundContent("充电失败"); chargingOrderRefund.setRefundReason("充电失败"); chargingOrderRefund.setRefundRemark(isWhitelistAllFreeOrder(chargingOrder) ? "白名单全部免费,启动失败记录预估退款金额" : "全额退款"); chargingOrderRefund.setRefundTotalAmount(recordedRefundAmount); chargingOrderRefund.setPayAmount(rechargeAmount); chargingOrder.setBalanceRefundAmount(balanceRefundAmount); chargingOrder.setThirdRefundAmount(thirdRefundAmount); chargingOrder.setRefundAmount(recordedRefundAmount); chargingOrder.setRefundStatus(thirdRefundAmount.compareTo(BigDecimal.ZERO) > 0 ? 1 : 2); if (thirdRefundAmount.compareTo(BigDecimal.ZERO) == 0) { if (balanceRefundAmount.compareTo(BigDecimal.ZERO) > 0) { changeUserBalance(chargingOrder.getAppUserId(), balanceRefundAmount); recordBalanceRefund(chargingOrder, balanceRefundAmount, chargingOrderRefund.getRefundCode()); } chargingOrderRefund.setRefundStatus(2); chargingOrderRefund.setRefundSerialNumber(isWhitelistAllFreeOrder(chargingOrder) ? "WHITELIST" : "BALANCE"); chargingOrderRefund.setRefundTime(LocalDateTime.now()); chargingOrderRefundService.save(chargingOrderRefund); this.updateById(chargingOrder); 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()); 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(chargingOrder).multiply(new BigDecimal(100)).intValue()); amount.setCurrency("CNY"); model.setAmount(amount); R> orderR = wxPaymentClient.refundOrderR(model); if (200 == orderR.getCode()) { chargingOrderRefundService.save(chargingOrderRefund); //手续费 Map amount1 = (Map) orderR.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); //回退会员折扣次数 if (chargingOrder.getVipDiscountAmount().compareTo(BigDecimal.ZERO) > 0) { TAppUser appUser = appUserClient.getUserById(chargingOrder.getAppUserId()).getData(); GetAppUserVipDetail getAppUserVipDetail = new GetAppUserVipDetail(); getAppUserVipDetail.setAppUserId(chargingOrder.getAppUserId()); getAppUserVipDetail.setVipId(appUser.getVipId()); TAppUserVipDetail data = appUserVipDetailClient.getAppUserVipDetail(getAppUserVipDetail).getData(); if (null != data) { data.setChargeNum(data.getChargeNum() + 1); appUserVipDetailClient.updateAppUserVipDetail(data); } } } } if (2 == rechargePaymentType) { RefundReq dto = new RefundReq(); dto.setOutTradeNo(chargingOrder.getCode()); dto.setOutRequestNo(chargingOrderRefund.getCode()); dto.setRefundAmount(thirdRefundAmount.toString()); dto.setRefundReason("充电失败,取消充电订单"); RefundResp resp = aliPaymentClient.refund(dto).getData(); if (null != resp) { chargingOrderRefundService.save(chargingOrderRefund); //回退会员折扣次数 if (chargingOrder.getVipDiscountAmount().compareTo(BigDecimal.ZERO) > 0) { TAppUser appUser = appUserClient.getUserById(chargingOrder.getAppUserId()).getData(); GetAppUserVipDetail getAppUserVipDetail = new GetAppUserVipDetail(); getAppUserVipDetail.setAppUserId(chargingOrder.getAppUserId()); getAppUserVipDetail.setVipId(appUser.getVipId()); TAppUserVipDetail data = appUserVipDetailClient.getAppUserVipDetail(getAppUserVipDetail).getData(); if (null != data) { data.setChargeNum(data.getChargeNum() + 1); appUserVipDetailClient.updateAppUserVipDetail(data); } } try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } chargingOrderStartupFailureWxRefund(chargingOrderRefund.getRefundCode(), resp.getTradeNo(), "SUCCESS", null); } } if (3 == rechargePaymentType) { RefundReq dto = new RefundReq(); dto.setOutTradeNo(chargingOrder.getRechargeSerialNumber()); dto.setOutRequestNo(chargingOrderRefund.getCode()); dto.setRefundAmount(thirdRefundAmount.toString()); dto.setRefundReason("充电失败,取消充电订单"); RefundResp resp = h5AliPaymentClient.refund(dto).getData(); if (null != resp && "10000".equals(resp.getCode())) { chargingOrderRefundService.save(chargingOrderRefund); //回退会员折扣次数 if (chargingOrder.getVipDiscountAmount().compareTo(BigDecimal.ZERO) > 0) { TAppUser appUser = appUserClient.getUserById(chargingOrder.getAppUserId()).getData(); GetAppUserVipDetail getAppUserVipDetail = new GetAppUserVipDetail(); getAppUserVipDetail.setAppUserId(chargingOrder.getAppUserId()); getAppUserVipDetail.setVipId(appUser.getVipId()); TAppUserVipDetail data = appUserVipDetailClient.getAppUserVipDetail(getAppUserVipDetail).getData(); if (null != data) { data.setChargeNum(data.getChargeNum() + 1); appUserVipDetailClient.updateAppUserVipDetail(data); } } try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } chargingOrderStartupFailureWxRefund(chargingOrderRefund.getRefundCode(), resp.getTradeNo(), "SUCCESS", null); } } } } } catch (InterruptedException e) { throw new RuntimeException(e); } finally { lock.unlock(); } } @Transactional public void refund1(String code, BigDecimal money) { log.info(code + ":-------------------充电成功,执行手动退款-------------------"); TChargingOrder chargingOrder = this.getOne(new LambdaQueryWrapper().eq(TChargingOrder::getCode, code)); if (chargingOrder.getOrderSource() == 1) { BigDecimal rechargeAmount = chargingOrder.getRechargeAmount(); Integer rechargePaymentType = chargingOrder.getRechargePaymentType(); //构建退款明细 TChargingOrderRefund chargingOrderRefund = new TChargingOrderRefund(); chargingOrderRefund.setChargingOrderId(chargingOrder.getId()); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); chargingOrderRefund.setRefundCode("CDF" + sdf.format(new Date()) + Double.valueOf(Math.random() * 1000).intValue()); chargingOrderRefund.setRefundAmount(money); BigDecimal refundableThirdAmount = getRefundableThirdPayAmount(chargingOrder) .subtract(money(chargingOrder.getThirdRefundAmount())).max(BigDecimal.ZERO); BigDecimal refundableBalanceAmount = money(chargingOrder.getBalancePayAmount()) .subtract(money(chargingOrder.getBalanceRefundAmount())).max(BigDecimal.ZERO); BigDecimal thirdRefundAmount = money.min(refundableThirdAmount).setScale(2, RoundingMode.HALF_UP); BigDecimal balanceRefundAmount = money.subtract(thirdRefundAmount) .min(refundableBalanceAmount).setScale(2, RoundingMode.HALF_UP); chargingOrderRefund.setBalanceRefundAmount(balanceRefundAmount); chargingOrderRefund.setThirdRefundAmount(thirdRefundAmount); chargingOrderRefund.setRefundStatus(1); chargingOrderRefund.setPayType(rechargePaymentType); chargingOrderRefund.setRefundStatus(1); chargingOrderRefund.setCode(code); chargingOrderRefund.setRefundTitle("充电完成退款"); chargingOrderRefund.setRefundContent("充电完成退款"); chargingOrderRefund.setRefundReason("充电完成退款"); chargingOrderRefund.setRefundRemark("充电完成退款,手动退款"); chargingOrderRefund.setRefundTotalAmount(money); chargingOrderRefund.setPayAmount(money); if (thirdRefundAmount.compareTo(BigDecimal.ZERO) == 0) { chargingOrderRefund.setRefundSerialNumber("BALANCE"); 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()); 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(chargingOrder).multiply(new BigDecimal(100)).intValue()); amount.setCurrency("CNY"); model.setAmount(amount); R> orderR = wxPaymentClient.refundOrderR(model); if (200 == orderR.getCode()) { chargingOrderRefundService.save(chargingOrderRefund); //手续费 Map amount1 = (Map) orderR.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); } } if (2 == rechargePaymentType) { RefundReq dto = new RefundReq(); dto.setOutTradeNo(chargingOrder.getCode()); dto.setOutRequestNo(chargingOrderRefund.getRefundCode()); dto.setRefundAmount(thirdRefundAmount.toString()); dto.setRefundReason("充电完成退款"); RefundResp resp = aliPaymentClient.refund(dto).getData(); if (null != resp) { chargingOrderRefundService.save(chargingOrderRefund); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } chargingOrderStartupFailureWxRefund(chargingOrderRefund.getRefundCode(), resp.getTradeNo(), "SUCCESS", null); } } if (3 == rechargePaymentType) { RefundReq dto = new RefundReq(); dto.setOutTradeNo(chargingOrder.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); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } chargingOrderStartupFailureWxRefund(chargingOrderRefund.getRefundCode(), resp.getTradeNo(), "SUCCESS", null); } } } } /** * 启动失败后微信退款的回调处理 * * @param out_refund_no * @param refund_id * @param tradeState * @param success_time * @return */ @Transactional public AjaxResult chargingOrderStartupFailureWxRefund(String out_refund_no, String refund_id, String tradeState, String success_time) { RLock lock = redissonClient.getLock("chargingOrderStartupFailureWxRefund:" + out_refund_no); try { boolean tryLock = lock.tryLock(10, 10, TimeUnit.SECONDS); if (tryLock) { TChargingOrderRefund one = chargingOrderRefundService.getOne(new LambdaQueryWrapper().eq(TChargingOrderRefund::getRefundCode, out_refund_no)); if (null == one || one.getRefundStatus() == null || one.getRefundStatus() != 1) { return AjaxResult.success(); } if (!"SUCCESS".equals(tradeState)) { return AjaxResult.success(); } TChargingOrder order = this.getById(one.getChargingOrderId()); if (order == null || (order.getRefundStatus() != null && order.getRefundStatus() == 2)) { return AjaxResult.success(); } one.setRefundSerialNumber(refund_id); one.setRefundStatus(2); one.setRefundTime(LocalDateTime.now()); chargingOrderRefundService.updateById(one); if (money(one.getBalanceRefundAmount()).compareTo(BigDecimal.ZERO) > 0) { changeUserBalance(order.getAppUserId(), money(one.getBalanceRefundAmount())); recordBalanceRefund(order, money(one.getBalanceRefundAmount()), one.getRefundCode()); } TChargingOrder chargingOrder = new TChargingOrder(); chargingOrder.setId(one.getChargingOrderId()); chargingOrder.setAppUserId(order.getAppUserId()); chargingOrder.setRefundStatus(2); chargingOrder.setRefundSerialNumber(refund_id); chargingOrder.setRefundTime(LocalDateTime.now()); this.updateById(chargingOrder); triggerChargingOrderShare(this.getById(order.getId())); } } catch (InterruptedException e) { throw new RuntimeException(e); } finally { lock.unlock(); } return AjaxResult.success(); } /** * 获取充电中的详情 * * @param id * @return */ @Override public ChargingDetails getChargingDetails(Integer id) { Long userId = tokenService.getLoginUserApplet().getUserId(); TChargingOrder one = this.getOne(new LambdaQueryWrapper().eq(TChargingOrder::getAppUserId, userId).eq(TChargingOrder::getChargingGunId, id) .eq(TChargingOrder::getRechargePaymentStatus, 2) .in(TChargingOrder::getStatus, Arrays.asList(2, 3, 4)) .eq(TChargingOrder::getDelFlag, 0) .orderByDesc(TChargingOrder::getCreateTime) .last(" limit 0, 1")); if (null == one || !one.getAppUserId().equals(userId)) { return null; } ChargingDetails chargingDetails = new ChargingDetails(); chargingDetails.setId(one.getId().toString()); TChargingGun chargingGun = chargingGunClient.getChargingGunById(id).getData(); TChargingPile chargingPile = chargingPileClient.getChargingPileById(chargingGun.getChargingPileId()).getData(); Site site = siteClient.getSiteByIds(Arrays.asList(chargingPile.getSiteId())).getData().get(0); chargingDetails.setName(site.getName() + "-" + chargingPile.getName()); chargingDetails.setCode(one.getCode()); chargingDetails.setReservationTime(one.getReservationTime()); boolean reservationPending = ReservationOrderSupport.isPending( one.getStatus(), one.getReservationTime(), LocalDateTime.now()); chargingDetails.setStatus(reservationPending ? 1 : one.getStatus()); chargingDetails.setChargingCost(one.getResidualAmount()); UploadRealTimeMonitoringData data = uploadRealTimeMonitoringDataService.getLastDataById(one.getCode()); if (null != data) { chargingDetails.setChargeCurrent(data.getOutput_current()); chargingDetails.setChargeVoltage(data.getOutput_voltage()); BigDecimal power = data.getOutput_current().multiply(data.getOutput_voltage()).divide(new BigDecimal(1000), new MathContext(4, RoundingMode.HALF_EVEN)); chargingDetails.setChargePower(power); chargingDetails.setCompletionRatio(data.getSoc()); chargingDetails.setRemainingChargeTime(data.getTime_remaining()); chargingDetails.setChargedDegrees(data.getCharging_degree()); Integer h = Integer.valueOf(data.getCumulative_charging_time() / 60); Integer m = Integer.valueOf(data.getCumulative_charging_time() % 60); chargingDetails.setChargedTime(String.format("%02d", h) + ":" + String.format("%02d", m)); if (null != one.getAppUserCarId()) { TAppUserCar appUserCar = appUserCarClient.getCarById(one.getAppUserCarId().toString()).getData(); // 续航 = 续航里程 * soc BigDecimal multiply = BigDecimal.ZERO; if (NumberUtil.isNumber(appUserCar.getEndurance())) { new BigDecimal(data.getSoc()).divide(new BigDecimal(100)).multiply(new BigDecimal(appUserCar.getEndurance())); } chargingDetails.setEndurance(multiply.setScale(2, RoundingMode.HALF_EVEN)); } } return chargingDetails; } /** * 停止充电操作 * * @param id 订单id * @return */ @Override public AjaxResult stopCharging(String id) { TChargingOrder order = this.getById(id); if (order == null) { return AjaxResult.error("充电订单不存在"); } Long userId = tokenService.getLoginUserApplet().getUserId(); if (!order.getAppUserId().equals(userId)) { log.info("【" + order.getCode() + "】停止或取消充电失败,用户权限不足。当前登录用户id:{},订单用户id:{}", userId, order.getAppUserId()); return AjaxResult.error("权限不足"); } if (ReservationOrderSupport.isPending(order.getStatus(), order.getReservationTime(), LocalDateTime.now())) { return cancelPendingReservation(order); } Integer status = order.getStatus(); if (status == 4 || status == 5) { return AjaxResult.error("不能重复操作"); } if (status != 3) { return AjaxResult.error("还未开始充电"); } order.setEndTime(LocalDateTime.now()); order.setStatus(4); order.setEndMode(1); this.updateById(order); log.info("【" + order.getCode() + "】主动停止充电成功,订单状态已被修改。"); String code1 = order.getCode(); TChargingGun chargingGun = chargingGunClient.getChargingGunById(order.getChargingGunId()).getData(); //异步线程处理停机 ExecutorService cachedThreadPool = Executors.newFixedThreadPool(1); cachedThreadPool.execute(() -> { //调用硬件停止充电,停止成功后开始计算费用退款 TChargingPile chargingPile = chargingPileClient.getChargingPileById(order.getChargingPileId()).getData(); PlatformStopCharging platformStopCharging = new PlatformStopCharging(); platformStopCharging.setCharging_pile_code(chargingPile.getCode()); platformStopCharging.setCharging_gun_code(chargingGun.getCode()); sendMessageClient.platformStopCharging(platformStopCharging); log.info(code1 + ":-------------------远程停止充电请求-------------------"); log.info(platformStopCharging.toString()); }); Site site = siteClient.getSiteByIds(Arrays.asList(order.getSiteId())).getData().get(0); //推送三方平台 if (2 == order.getOrderSource() && 1 == site.getIsOpenSynchronization()) { tcecClient.notificationEquipChargeStatus(order.getStartChargeSeq(), order.getOperatorId()); } ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); threadPoolExecutor.execute(() -> { //推送成都监管 NotificationConnectorChargeStatusVo vo = new NotificationConnectorChargeStatusVo(); vo.setOrderCode(order.getCode()); vo.setChargingGunId(order.getChargingGunId()); chengDuJianGuanClient.notificationConnectorChargeStatus(vo); }); return AjaxResult.success(); } private AjaxResult cancelPendingReservation(TChargingOrder order) { RLock lock = redissonClient.getLock("START_AFTER_SAFETY:" + order.getId()); boolean locked = false; try { locked = lock.tryLock(1, 120, TimeUnit.SECONDS); if (!locked) { return AjaxResult.error("预约订单正在处理中,请稍后重试"); } TChargingOrder latest = this.getById(order.getId()); if (latest == null || !ReservationOrderSupport.isPending( latest.getStatus(), latest.getReservationTime(), LocalDateTime.now())) { return AjaxResult.error("预约已到时间或订单状态已变化,无法取消"); } refund(latest.getCode()); TChargingOrder cancelled = this.getById(latest.getId()); if (cancelled != null) { cancelled.setStatus(-1); cancelled.setEndMode(1); cancelled.setEndTime(LocalDateTime.now()); this.updateById(cancelled); } redisService.deleteObject("AQJC_" + latest.getChargingGunId()); log.info("【{}】用户取消预约充电,预约时间:{}", latest.getCode(), latest.getReservationTime()); return AjaxResult.success("预约充电已取消,退款将原路退回"); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return AjaxResult.error("取消预约被中断,请稍后重试"); } finally { if (locked && lock.isHeldByCurrentThread()) { lock.unlock(); } } } /** * 结束充电后处理用户标签数据 * * @param chargingOrder */ public void editUserTag(TChargingOrder chargingOrder) { if (chargingOrder == null || chargingOrder.getAppUserId() == null) { return; } //处理用户标签数据 R> tagResult = userTagClient.getAllUserTag(); List data = tagResult == null ? null : tagResult.getData(); if (data == null || data.isEmpty()) { return; } //累计充电次数 List userTagList1 = data.stream().filter(s -> Integer.valueOf(1).equals(s.getStandardCondition())).collect(Collectors.toList()); for (TUserTag tUserTag : userTagList1) { JSONObject conditions = JSON.parseObject(tUserTag.getConditions()); Integer times = conditions == null ? null : conditions.getInteger("times"); if (times == null || times <= 0 || tUserTag.getCreateTime() == null) { continue; } TAppUserTag appUserTag = new TAppUserTag(); appUserTag.setAppUserId(chargingOrder.getAppUserId()); appUserTag.setUserTagId(tUserTag.getId()); TAppUserTag data1 = appUserTagClient.getUserTag(appUserTag).getData(); String start = tUserTag.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); String end = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); long count1 = this.count(new LambdaQueryWrapper().eq(TChargingOrder::getAppUserId, chargingOrder.getAppUserId()) .eq(TChargingOrder::getRechargePaymentStatus, 2).between(TChargingOrder::getStartTime, start, end).eq(TChargingOrder::getDelFlag, 0)); if (null == data1 && count1 >= times) { data1 = new TAppUserTag(); data1.setAppUserId(chargingOrder.getAppUserId()); data1.setUserTagId(tUserTag.getId()); data1.setCreateTime(LocalDateTime.now()); appUserTagClient.addUserTag(data1); } } //充电频率 List userTagList2 = data.stream().filter(s -> Integer.valueOf(2).equals(s.getStandardCondition())).collect(Collectors.toList()); for (TUserTag tUserTag : userTagList2) { TAppUserTag appUserTag = new TAppUserTag(); appUserTag.setAppUserId(chargingOrder.getAppUserId()); appUserTag.setUserTagId(tUserTag.getId()); TAppUserTag data1 = appUserTagClient.getUserTag(appUserTag).getData(); JSONObject conditions = JSON.parseObject(tUserTag.getConditions()); Integer day = conditions == null ? null : conditions.getInteger("day"); Integer times = conditions == null ? null : conditions.getInteger("times"); if (day == null || day <= 0 || times == null || times <= 0) { continue; } LocalDateTime endTime = LocalDateTime.now(); String start = endTime.minusDays(day).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); String end = endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); long count1 = this.count(new LambdaQueryWrapper().eq(TChargingOrder::getAppUserId, chargingOrder.getAppUserId()) .eq(TChargingOrder::getRechargePaymentStatus, 2).between(TChargingOrder::getStartTime, start, end).eq(TChargingOrder::getDelFlag, 0)); if (null == data1 && count1 >= times) { data1 = new TAppUserTag(); data1.setAppUserId(chargingOrder.getAppUserId()); data1.setUserTagId(tUserTag.getId()); data1.setCreateTime(LocalDateTime.now()); appUserTagClient.addUserTag(data1); } } } /** * 推荐奖励(被推荐首单奖励) * * @param chargingOrder */ public void referralReward(TChargingOrder chargingOrder) { //处理推荐奖励(被推荐首单奖励) TAppUser appUser = appUserClient.getUserById(chargingOrder.getAppUserId()).getData(); long count = this.count(new LambdaQueryWrapper().eq(TChargingOrder::getAppUserId, chargingOrder.getAppUserId()) .in(TChargingOrder::getStatus, Arrays.asList(4, 5)).eq(TChargingOrder::getDelFlag, 0)); if (null != appUser.getInviteUserId() && 1 == count) { TIntegralRule integralRule = integralRuleClient.getSet().getData(); String inviteUsersToEarnPoints = integralRule.getInviteUsersToEarnPoints(); JSONObject jsonObject = JSON.parseObject(inviteUsersToEarnPoints); Integer num1 = jsonObject.getInteger("num1"); TAppUser appUser1 = appUserClient.getUserById(appUser.getInviteUserId()).getData(); if (null != appUser1.getVipId()) { GetAppUserVipDetail getAppUserVipDetail = new GetAppUserVipDetail(); getAppUserVipDetail.setAppUserId(appUser1.getId()); getAppUserVipDetail.setVipId(appUser1.getVipId()); TAppUserVipDetail vipDetail = appUserVipDetailClient.getAppUserVipDetail(getAppUserVipDetail).getData(); TVip vip = getVipSnapshot(vipDetail, appUser1.getVipId()); Integer doubleIntegration = vip == null ? null : vip.getDoubleIntegration(); //双倍积分 if (Integer.valueOf(1).equals(doubleIntegration)) { num1 *= 2; } } GetInviteUser query = new GetInviteUser(); query.setAppUserId(appUser1.getId()); query.setBeInvitedAppUserId(chargingOrder.getAppUserId()); TInviteUser inviteUser = inviteUserClient.getInviteUser(query).getData(); if (null == inviteUser) { inviteUser = new TInviteUser(); inviteUser.setAppUserId(appUser1.getId()); inviteUser.setBeInvitedAppUserId(chargingOrder.getAppUserId()); inviteUser.setAward(num1); inviteUser.setCreateTime(LocalDateTime.now()); inviteUserClient.saveInviteUser(inviteUser); } else { inviteUser.setAward(num1); inviteUserClient.updateInviteUser(inviteUser); } if (num1 > 0) { TAppUserIntegralChange appUserIntegralChange = new TAppUserIntegralChange(); String code = Double.valueOf(Math.random() * 1000).intValue() + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")); appUserIntegralChange.setCode(code); appUserIntegralChange.setAppUserId(appUser1.getId()); appUserIntegralChange.setChangeType(4); appUserIntegralChange.setHistoricalIntegral(appUser1.getPoints()); appUserIntegralChange.setCurrentIntegral(appUser1.getPoints() + num1); appUserIntegralChange.setCreateTime(LocalDateTime.now()); appUserIntegralChange.setOrderCode(chargingOrder.getCode()); appUserIntegralChange.setExtension(appUser.getId().toString()); appUserIntegralChangeClient.addAppUserIntegralChange(appUserIntegralChange); appUser1.setPoints(appUser1.getPoints() + num1); appUserClient.updateAppUser(appUser1); } } } /** * 停止充电应答结果处理 * * @param platformStopChargingReply */ @Override public void terminateSuccessfulResponse(PlatformStopChargingReplyVO platformStopChargingReply) { GetChargingGunByCode code = new GetChargingGunByCode(); code.setCharging_gun_code(platformStopChargingReply.getCharging_gun_code()); code.setCharging_pile_code(platformStopChargingReply.getCharging_pile_code()); TChargingGun chargingGun = chargingGunClient.getChargingGunByCode(code).getData(); TChargingOrder order = this.getOne(new LambdaQueryWrapper().eq(TChargingOrder::getChargingGunId, chargingGun.getId()) .orderByDesc(TChargingOrder::getEndTime).eq(TChargingOrder::getRechargePaymentStatus, 2).last(" limit 0, 1")); if (null != order && order.getStatus() != 4) { return; } String code1 = order.getCode(); if (0 == platformStopChargingReply.getStop_result()) { String failure_cause = ""; switch (platformStopChargingReply.getFailure_cause()) { case 0: failure_cause = "无"; break; case 1: failure_cause = "设备编号不匹配"; break; case 2: failure_cause = "枪未处于充电状态"; break; case 3: failure_cause = "其他"; break; } log.info(code1 + ":停机失败:订单号:{},失败原因:{}", order.getCode(), failure_cause); } else { TChargingOrder chargingOrder = new TChargingOrder(); chargingOrder.setId(order.getId()); chargingOrder.setAppUserId(order.getAppUserId()); chargingOrder.setEndMode(1); this.updateById(chargingOrder); } } @Override public TCharingOrderVO chargingOrder(ChargingOrderQuery dto) { TCharingOrderVO summary = chargingOrderSummary(dto); summary.setList(chargingOrderList(dto)); return summary; } @Override public PageInfo chargingOrderList(ChargingOrderQuery dto) { String[] timeRange = parseChargingOrderTimeRange(dto); String startTime1 = timeRange[0]; String startTime2 = timeRange[1]; String endTime1 = timeRange[2]; String endTime2 = timeRange[3]; int pageCurr = dto.getPageCurr() == null ? 1 : dto.getPageCurr(); int pageSize = dto.getPageSize() == null ? 50 : dto.getPageSize(); PageInfo pageInfo = new PageInfo<>(pageCurr, pageSize); Long userId = tokenService.getLoginUser().getUserid(); SysUser sysUser = sysUserClient.getSysUser(userId).getData(); Integer roleType = sysUser.getRoleType(); setChargingOrderAuthSiteIds(dto, sysUser, userId, roleType); if (StringUtils.hasLength(dto.getPhone()) && (dto.getUserIds() == null || dto.getUserIds().isEmpty())) { List data = appUserClient.getUserIdsByPhone(dto.getPhone()).getData(); if (data == null || data.isEmpty()) { pageInfo.setTotal(0L); pageInfo.setRecords(Collections.emptyList()); return pageInfo; } dto.setUserIds(data); } // 手动分页:不传 IPage(避免分页拦截器生成无法下推的全量 COUNT),COUNT 与 ORDER BY + LIMIT 均由 SQL 下推到分片 long offset = (long) (pageCurr - 1) * pageSize; long total = this.baseMapper.chargingOrderCount(dto, startTime1, startTime2, endTime1, endTime2); pageInfo.setTotal(total); List list = total == 0L ? Collections.emptyList() : this.baseMapper.chargingOrder(dto, startTime1, startTime2, endTime1, endTime2, offset, pageSize); fillChargingOrderBalanceIncome(list); fillChargingOrderShareIncome(list); fillChargingOrderPageExtra(list, dto.getSiteIds(), sysUser, roleType); pageInfo.setRecords(list); return pageInfo; } @Override public TCharingOrderVO chargingOrderSummary(ChargingOrderQuery dto) { TCharingOrderVO tCharingOrderVO = new TCharingOrderVO(); fillEmptyChargingOrderSummary(tCharingOrderVO); if (isChargingOrderFullSweep(dto)) { // 无任何筛选即全量扫描:汇总聚合跨分片 JOIN(accounting_strategy,分片键 charging_order_id 与 t_charging_order 不一致) // 且需把全量订单 id 反查 share/balance,MyCat 只能全量拉回代理层,必然超时。汇总没有 LIMIT 可下推,直接返回空汇总。 return tCharingOrderVO; } String[] timeRange = parseChargingOrderTimeRange(dto); String startTime1 = timeRange[0]; String startTime2 = timeRange[1]; String endTime1 = timeRange[2]; String endTime2 = timeRange[3]; Long userId = tokenService.getLoginUser().getUserid(); SysUser sysUser = sysUserClient.getSysUser(userId).getData(); setChargingOrderAuthSiteIds(dto, sysUser, userId, sysUser.getRoleType()); Map map = this.baseMapper.chargingOrderBaseSummary(dto, startTime1, startTime2, endTime1, endTime2); if (null != map) { tCharingOrderVO.setTotal(getSummaryDecimal(map, "total")); tCharingOrderVO.setTime(getSummaryLong(map, "time")); tCharingOrderVO.setIncome(getSummaryDecimal(map, "income")); tCharingOrderVO.setElectronicMoney(getSummaryDecimal(map, "electronicMoney")); tCharingOrderVO.setServiceMoney(getSummaryDecimal(map, "serviceMoney")); tCharingOrderVO.setWechatIncome(getSummaryDecimal(map, "wechatIncome")); tCharingOrderVO.setAlipayIncome(getSummaryDecimal(map, "alipayIncome")); tCharingOrderVO.setWhitelistChargingIncome(getSummaryDecimal(map, "whitelistChargingIncome")); tCharingOrderVO.setWhitelistRefund(getSummaryDecimal(map, "whitelistRefund")); } List summaryOrderIds = this.baseMapper.chargingOrderSummaryOrderIds(dto, startTime1, startTime2, endTime1, endTime2); Map shareIncomeMap = getChargingOrderShareIncomeSummary(summaryOrderIds); tCharingOrderVO.setPlatformIncomeTotal(getSummaryDecimal(shareIncomeMap, "platformIncomeTotal")); tCharingOrderVO.setPartnerIncomeTotal(getSummaryDecimal(shareIncomeMap, "partnerIncomeTotal")); Map balanceMap = getExchangeCodeBalanceSummary(summaryOrderIds); tCharingOrderVO.setBalanceIncome(getSummaryDecimal(balanceMap, "balanceIncome")); tCharingOrderVO.setBalanceGiftIncome(getSummaryDecimal(balanceMap, "balanceGiftIncome")); return tCharingOrderVO; } private String[] parseChargingOrderTimeRange(ChargingOrderQuery dto) { String startTime1 = null; String startTime2 = null; String endTime1 = null; String endTime2 = null; if (StringUtils.hasLength(dto.getStartTime())) { String[] split = dto.getStartTime().split(" - "); startTime1 = split[0]; startTime2 = split[1]; } if (StringUtils.hasLength(dto.getEndTime())) { String[] split = dto.getEndTime().split(" - "); endTime1 = split[0]; endTime2 = split[1]; } return new String[]{startTime1, startTime2, endTime1, endTime2}; } /** 是否全量扫描:用户未提供任何可收窄的筛选条件(时间/订单号/手机号/站点/桩/枪/状态等),无界聚合无法下推。 */ private boolean isChargingOrderFullSweep(ChargingOrderQuery dto) { return !StringUtils.hasLength(dto.getStartTime()) && !StringUtils.hasLength(dto.getEndTime()) && !StringUtils.hasLength(dto.getCode()) && !StringUtils.hasLength(dto.getPhone()) && dto.getSiteId() == null && dto.getPileId() == null && dto.getGunId() == null && dto.getOrderType() == null && dto.getStatus() == null && dto.getIsWhitelist() == null && dto.getPaymentType() == null && dto.getOrderSource() == null && (dto.getUserIds() == null || dto.getUserIds().isEmpty()); } private void fillChargingOrderPageExtra(List list, List siteIds, SysUser sysUser, Integer roleType) { if (list == null || list.isEmpty()) { return; } List orderIds = list.stream().map(ChargingOrderVO::getId).filter(Objects::nonNull).collect(Collectors.toList()); List pageSiteIds = list.stream().map(ChargingOrderVO::getSiteId).filter(Objects::nonNull).distinct().collect(Collectors.toList()); List pageGunIds = list.stream().map(ChargingOrderVO::getChargingGunId).filter(Objects::nonNull).distinct().collect(Collectors.toList()); List pageGuns = pageGunIds.isEmpty() ? new ArrayList<>() : Optional.ofNullable(chargingGunClient.getChargingGunByIds(pageGunIds).getData()).orElse(new ArrayList<>()); Map gunMap = pageGuns.stream() .filter(item -> item.getId() != null) .collect(Collectors.toMap(TChargingGun::getId, item -> item, (a, b) -> a)); List pagePiles = pageSiteIds.isEmpty() ? new ArrayList<>() : Optional.ofNullable(chargingPileClient.getChargingPileBySiteIds(pageSiteIds).getData()).orElse(new ArrayList<>()); Map pileMap = pagePiles.stream() .filter(item -> item.getId() != null) .collect(Collectors.toMap(TChargingPile::getId, item -> item, (a, b) -> a)); List pageSites = pageSiteIds.isEmpty() ? new ArrayList<>() : Optional.ofNullable(siteClient.getSiteByIds(pageSiteIds).getData()).orElse(new ArrayList<>()); Map siteMap = pageSites.stream() .filter(item -> item.getId() != null) .collect(Collectors.toMap(Site::getId, item -> item, (a, b) -> a)); // 列表 SQL 不再跨库 JOIN app_user/partner(避免分片表无筛选全量广播),手机号与合作商名在此回填 Map phoneMap = new HashMap<>(); List pageUserIds = list.stream().map(ChargingOrderVO::getAppUserId).filter(Objects::nonNull).distinct().collect(Collectors.toList()); if (!pageUserIds.isEmpty()) { List pageUsers = Optional.ofNullable(appUserClient.getUserByIds(pageUserIds).getData()).orElse(new ArrayList<>()); for (TAppUser user : pageUsers) { if (user != null && user.getId() != null) { phoneMap.put(user.getId(), user.getPhone()); } } } Map partnerNameMap = new HashMap<>(); List pagePartnerIds = siteMap.values().stream().map(Site::getPartnerId).filter(Objects::nonNull).distinct().collect(Collectors.toList()); for (Integer partnerId : pagePartnerIds) { Partner partner = Optional.ofNullable(partnerClient.getPartnerById(partnerId).getData()).orElse(null); if (partner != null && partner.getId() != null) { partnerNameMap.put(partner.getId(), partner.getName()); } } Map> strategyMap = orderIds.isEmpty() ? new HashMap<>() : chargingOrderAccountingStrategyService.lambdaQuery() .in(TChargingOrderAccountingStrategy::getChargingOrderId, orderIds) .list() .stream() .collect(Collectors.groupingBy(TChargingOrderAccountingStrategy::getChargingOrderId)); Map authRecordMap = new HashMap<>(); if (roleType == 2 && siteIds != null) { for (Integer siteId : siteIds) { List permission = partnerClient.getChargingOrderMenu(sysUser.getObjectId(), siteId).getData(); authRecordMap.put(siteId, permission != null && !permission.isEmpty() && Boolean.TRUE.equals(permission.get(0))); } } for (ChargingOrderVO chargingOrderVO : list) { if (roleType == 2 && chargingOrderVO.getSiteId() != null) { chargingOrderVO.setAuthRecord(Boolean.TRUE.equals(authRecordMap.get(chargingOrderVO.getSiteId()))); } chargingOrderVO.setPhone(phoneMap.get(chargingOrderVO.getAppUserId())); chargingOrderVO.setCommissionAmount(chargingOrderVO.getRechargeAmount() != null ? chargingOrderVO.getRechargeAmount().multiply(new BigDecimal("0.006")) : new BigDecimal("0")); chargingOrderVO.setPlatFormMoney(chargingOrderVO.getRechargeAmount() != null ? chargingOrderVO.getRechargeAmount().multiply(new BigDecimal("0.006")).setScale(2, BigDecimal.ROUND_DOWN) : new BigDecimal("0")); chargingOrderVO.setUid(chargingOrderVO.getId() + ""); Site site = siteMap.get(chargingOrderVO.getSiteId()); if (site != null) { chargingOrderVO.setSiteName(site.getName()); chargingOrderVO.setPartnerName(partnerNameMap.get(site.getPartnerId())); } TChargingGun gun = gunMap.get(chargingOrderVO.getChargingGunId()); TChargingPile pile = pileMap.get(chargingOrderVO.getChargingPileId()); if (pile != null && gun != null) { chargingOrderVO.setTerminalName(pile.getName() + "-" + gun.getName()); } List strategies = strategyMap.getOrDefault(chargingOrderVO.getId(), new ArrayList<>()); if (chargingOrderVO.getStartTime() != null && chargingOrderVO.getEndTime() != null) { long between = ChronoUnit.SECONDS.between(chargingOrderVO.getStartTime(), chargingOrderVO.getEndTime()); chargingOrderVO.setChargingSecond(between); BigDecimal electronicReduce = strategies.stream().map(TChargingOrderAccountingStrategy::getPeriodElectricPrice).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); chargingOrderVO.setElectrovalence(electronicReduce); BigDecimal serviceReduce = strategies.stream().map(TChargingOrderAccountingStrategy::getPeriodServicePrice).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add); if (!Objects.equals(chargingOrderVO.getOrderSource(), 2)) { chargingOrderVO.setServiceCharge(serviceReduce); chargingOrderVO.setSharingAmount(BigDecimal.ZERO); } else { chargingOrderVO.setServiceCharge(serviceReduce.multiply(new BigDecimal("0.8"))); chargingOrderVO.setSharingAmount(serviceReduce.multiply(new BigDecimal("0.2"))); } } chargingOrderVO.setCount(strategies.size()); chargingOrderVO.setLicensePlate(chargingOrderVO.getPlateNum()); } } /** * 填充充电桩订单列表余额收入,余额收入按真实充值余额统计,赠送余额单独展示。 */ private void fillChargingOrderBalanceIncome(List list) { if (list == null || list.isEmpty()) { return; } List orderIds = list.stream().map(ChargingOrderVO::getId).filter(Objects::nonNull).collect(Collectors.toList()); if (orderIds.isEmpty()) { return; } Map> incomeMap = new HashMap<>(); List> rows = this.baseMapper.exchangeCodeBalanceIncomeByOrderIds(orderIds); if (rows != null) { for (Map row : rows) { Long orderId = getSummaryLong(row, "orderId"); if (orderId != null) { incomeMap.put(orderId, row); } } } for (ChargingOrderVO item : list) { Map row = incomeMap.get(item.getId()); item.setBalanceIncome(getSummaryDecimal(row, "balanceIncome")); item.setBalanceGiftIncome(getSummaryDecimal(row, "balanceGiftIncome")); } } private void setChargingOrderAuthSiteIds(ChargingOrderQuery dto, SysUser sysUser, Long userId, Integer roleType) { List siteIds = new ArrayList<>(); if (dto.getSiteId() == null) { if (userId != null) { List data = siteClient.getSiteListByUserId(userId).getData(); for (GetSiteListDTO datum : data) { siteIds.add(datum.getId()); } } } else { siteIds.add(dto.getSiteId()); } if (siteIds.isEmpty()) { siteIds.add(-1); } else if (roleType == 2) { List integers = new ArrayList<>(); for (Integer siteId : siteIds) { List t1 = partnerClient.getChargingOrderMenu(sysUser.getObjectId(), siteId).getData(); Boolean b = t1.get(1); if (b) { integers.add(siteId); } } siteIds = integers; } if (siteIds.isEmpty()) { siteIds.add(-1); } dto.setSiteIds(siteIds); } /** * 查询充电桩订单余额收入明细,余额收入按真实充值余额统计,赠送收入单独展示。 */ @Override public List balanceIncomeDetail(ChargingOrderQuery dto) { if (isChargingOrderFullSweep(dto)) { // 与汇总同根因:全量扫描会把全量订单 id 反查 exchange_code_use_record,MyCat 超时,直接返回空明细 return new ArrayList<>(); } String startTime1 = null; String startTime2 = null; String endTime1 = null; String endTime2 = null; if (StringUtils.hasLength(dto.getStartTime())) { String[] split = dto.getStartTime().split(" - "); startTime1 = split[0]; startTime2 = split[1]; } if (StringUtils.hasLength(dto.getEndTime())) { String[] split = dto.getEndTime().split(" - "); endTime1 = split[0]; endTime2 = split[1]; } if (StringUtils.hasLength(dto.getPhone())) { List data = appUserClient.getUserIdsByPhone(dto.getPhone()).getData(); if (data == null || data.isEmpty()) { return new ArrayList<>(); } dto.setUserIds(data); } Long userId = tokenService.getLoginUser().getUserid(); SysUser sysUser = sysUserClient.getSysUser(userId).getData(); setChargingOrderAuthSiteIds(dto, sysUser, userId, sysUser.getRoleType()); List orderIds = this.baseMapper.chargingOrderSummaryOrderIds(dto, startTime1, startTime2, endTime1, endTime2); if (orderIds == null || orderIds.isEmpty()) { return new ArrayList<>(); } Map detailMap = new LinkedHashMap<>(); int batchSize = 500; for (int i = 0; i < orderIds.size(); i += batchSize) { List batchIds = orderIds.subList(i, Math.min(i + batchSize, orderIds.size())); List batchList = this.baseMapper.exchangeCodeBalanceIncomeDetail(batchIds); if (batchList == null || batchList.isEmpty()) { continue; } for (ChargingOrderBalanceIncomeDetailVO item : batchList) { Long packageId = item.getPackageId() == null ? 0L : item.getPackageId(); ChargingOrderBalanceIncomeDetailVO detail = detailMap.get(packageId); if (detail == null) { detail = new ChargingOrderBalanceIncomeDetailVO(); detail.setPackageId(item.getPackageId()); detail.setPackageName(item.getPackageName()); detail.setBalanceIncome(BigDecimal.ZERO); detail.setGiftIncome(BigDecimal.ZERO); detailMap.put(packageId, detail); } detail.setBalanceIncome(detail.getBalanceIncome().add(item.getBalanceIncome() == null ? BigDecimal.ZERO : item.getBalanceIncome())); detail.setGiftIncome(detail.getGiftIncome().add(item.getGiftIncome() == null ? BigDecimal.ZERO : item.getGiftIncome())); } } List result = new ArrayList<>(); for (ChargingOrderBalanceIncomeDetailVO detail : detailMap.values()) { BigDecimal balanceIncome = (detail.getBalanceIncome() == null ? BigDecimal.ZERO : detail.getBalanceIncome()).setScale(2, RoundingMode.HALF_EVEN); BigDecimal giftIncome = (detail.getGiftIncome() == null ? BigDecimal.ZERO : detail.getGiftIncome()).setScale(2, RoundingMode.HALF_EVEN); if (balanceIncome.compareTo(BigDecimal.ZERO) == 0 && giftIncome.compareTo(BigDecimal.ZERO) == 0) { continue; } detail.setBalanceIncome(balanceIncome); detail.setGiftIncome(giftIncome); result.add(detail); } return result; } private void fillEmptyChargingOrderSummary(TCharingOrderVO tCharingOrderVO) { tCharingOrderVO.setTotal(BigDecimal.ZERO); tCharingOrderVO.setTime(0L); tCharingOrderVO.setIncome(BigDecimal.ZERO); tCharingOrderVO.setElectronicMoney(BigDecimal.ZERO); tCharingOrderVO.setServiceMoney(BigDecimal.ZERO); tCharingOrderVO.setPlatformIncomeTotal(BigDecimal.ZERO); tCharingOrderVO.setPartnerIncomeTotal(BigDecimal.ZERO); tCharingOrderVO.setWechatIncome(BigDecimal.ZERO); tCharingOrderVO.setAlipayIncome(BigDecimal.ZERO); tCharingOrderVO.setBalanceIncome(BigDecimal.ZERO); tCharingOrderVO.setBalanceGiftIncome(BigDecimal.ZERO); tCharingOrderVO.setWhitelistChargingIncome(BigDecimal.ZERO); tCharingOrderVO.setWhitelistRefund(BigDecimal.ZERO); } /** * 汇总充电桩订单余额收入,兑换码使用记录的usedAmount包含赠送余额,SQL中需扣除giftUsedAmount。 */ private Map getExchangeCodeBalanceSummary(List orderIds) { Map totalMap = new HashMap<>(); totalMap.put("balanceIncome", BigDecimal.ZERO); totalMap.put("balanceGiftIncome", BigDecimal.ZERO); if (orderIds == null || orderIds.isEmpty()) { return totalMap; } BigDecimal balanceIncome = BigDecimal.ZERO; BigDecimal balanceGiftIncome = BigDecimal.ZERO; int batchSize = 500; for (int i = 0; i < orderIds.size(); i += batchSize) { List batchIds = orderIds.subList(i, Math.min(i + batchSize, orderIds.size())); Map batchMap = this.baseMapper.exchangeCodeBalanceSummary(batchIds); if (batchMap != null) { balanceIncome = balanceIncome.add(getSummaryDecimal(batchMap, "balanceIncome")); balanceGiftIncome = balanceGiftIncome.add(getSummaryDecimal(batchMap, "balanceGiftIncome")); } } totalMap.put("balanceIncome", balanceIncome); totalMap.put("balanceGiftIncome", balanceGiftIncome); return totalMap; } private Map getChargingOrderShareIncomeSummary(List orderIds) { Map totalMap = new HashMap<>(); totalMap.put("platformIncomeTotal", BigDecimal.ZERO); totalMap.put("partnerIncomeTotal", BigDecimal.ZERO); 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 batchIds = orderIds.subList(i, Math.min(i + batchSize, orderIds.size())); Map 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 map, String key) { if (map == null) { return BigDecimal.ZERO; } Object value = map.get(key); if (value == null) { return BigDecimal.ZERO; } if (value instanceof BigDecimal) { return ((BigDecimal) value).setScale(2, RoundingMode.HALF_EVEN); } if (value instanceof Number) { return BigDecimal.valueOf(((Number) value).doubleValue()).setScale(2, RoundingMode.HALF_EVEN); } return new BigDecimal(value.toString()).setScale(2, RoundingMode.HALF_EVEN); } private Long getSummaryLong(Map map, String key) { if (map == null) { return 0L; } Object value = map.get(key); if (value == null) { return 0L; } if (value instanceof Number) { return ((Number) value).longValue(); } return Long.parseLong(value.toString()); } @Override public R> payOrderQuery(PayOrderQueryDto payOrderQueryDto) { if (payOrderQueryDto == null) { payOrderQueryDto = new PayOrderQueryDto(); } PageInfo pageInfo = new PageInfo<>(payOrderQueryDto.getPageCurr(), payOrderQueryDto.getPageSize()); // 开票申请没有关联明细时必须返回空结果,不能退化成支付订单全量查询。 if (payOrderQueryDto.getInvoiceId() != null && (payOrderQueryDto.getIds() == null || payOrderQueryDto.getIds().isEmpty())) { pageInfo.setRecords(Collections.emptyList()); return R.ok(pageInfo); } List list = this.baseMapper.payOrderQuery(pageInfo, payOrderQueryDto); if (list == null || list.isEmpty()) { pageInfo.setRecords(Collections.emptyList()); return R.ok(pageInfo); } for (PayOrderDto payOrderDto : list) { if (payOrderDto == null) { continue; } if (Integer.valueOf(2).equals(payOrderDto.getType()) || Integer.valueOf(3).equals(payOrderDto.getType())) { payOrderDto.setFinalAmount(payOrderDto.getOrderAmount()); } if (Integer.valueOf(1).equals(payOrderDto.getType()) && Integer.valueOf(3).equals(payOrderDto.getStatus())) { payOrderDto.setFinalAmount(payOrderDto.getOrderAmount()); payOrderDto.setOrderAmount(payOrderDto.getOrderAmount()); payOrderDto.setPaymentAmount(payOrderDto.getOrderAmount()); } } pageInfo.setRecords(list); return R.ok(pageInfo); } @Override public R> enterpriseChargingAnalysis(EnterpriseChargingAnalysisQuery query) { int pageCurr = query.getPageCurr() == null ? 1 : query.getPageCurr(); int pageSize = query.getPageSize() == null ? 50 : query.getPageSize(); PageInfo pageInfo = new PageInfo<>(pageCurr, pageSize); // 逆反查聚合(不再跨分片 JOIN,构建块见 TChargingOrderMapper.xml enterpriseInvoiceRows 等): // 1) t_order_invoice 单表查企业发票(10 片按 app_user_id 分片,抬头级条件可下推) // 2) t_order_invoice_detail 按 order_invoice_id IN 路由查充电明细 // 3) 命中订单级条件(站点/时间等)→ 命中订单 id // 4) t_charging_order 按 id IN 走主键取 start_time/site_id // 5) t_site 单表取站点名 // 6) 按公司聚合 + 手工分页(不再传 IPage,避免分页拦截器生成无法下推的全量 COUNT) List invoices = this.baseMapper.enterpriseInvoiceRows(query); if (invoices == null || invoices.isEmpty()) { pageInfo.setRecords(Collections.emptyList()); return R.ok(pageInfo); } Map> detailsByInvoice = new HashMap<>(); Map detailByOrder = new LinkedHashMap<>(); List invoiceIds = invoices.stream() .map(EnterpriseChargingAggRow.Invoice::getId) .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); for (int start = 0; start < invoiceIds.size(); start += ENTERPRISE_INVOICE_BATCH_SIZE) { int end = Math.min(start + ENTERPRISE_INVOICE_BATCH_SIZE, invoiceIds.size()); List details = this.baseMapper.enterpriseInvoiceDetailRows(invoiceIds.subList(start, end)); if (details == null) { continue; } for (EnterpriseChargingAggRow.Detail detail : details) { if (detail.getOrderId() == null) { continue; } detailsByInvoice.computeIfAbsent(detail.getInvoiceId(), k -> new ArrayList<>()).add(detail); detailByOrder.putIfAbsent(detail.getOrderId(), detail); } } if (detailByOrder.isEmpty()) { pageInfo.setRecords(Collections.emptyList()); return R.ok(pageInfo); } List candidateOrderIds = new ArrayList<>(detailByOrder.keySet()); boolean orderFilter = hasEnterpriseOrderFilter(query); Set matchedOrderIds = null; if (orderFilter) { matchedOrderIds = new HashSet<>(); for (int start = 0; start < candidateOrderIds.size(); start += ENTERPRISE_ORDER_ID_BATCH_SIZE) { int end = Math.min(start + ENTERPRISE_ORDER_ID_BATCH_SIZE, candidateOrderIds.size()); List matched = this.baseMapper.enterpriseAnalysisMatchingOrderIds( candidateOrderIds.subList(start, end), query); if (matched != null) { matchedOrderIds.addAll(matched); } } if (matchedOrderIds.isEmpty()) { pageInfo.setRecords(Collections.emptyList()); return R.ok(pageInfo); } } List effectiveOrderIds = orderFilter ? new ArrayList<>(matchedOrderIds) : candidateOrderIds; Map orderMap = new HashMap<>(); for (int start = 0; start < effectiveOrderIds.size(); start += ENTERPRISE_ORDER_ID_BATCH_SIZE) { int end = Math.min(start + ENTERPRISE_ORDER_ID_BATCH_SIZE, effectiveOrderIds.size()); List orders = this.baseMapper.enterpriseAnalysisChargingOrders(effectiveOrderIds.subList(start, end)); if (orders == null) { continue; } for (EnterpriseChargingAggRow.Order order : orders) { orderMap.put(order.getOrderId(), order); } } Set siteIds = orderMap.values().stream() .map(EnterpriseChargingAggRow.Order::getSiteId) .filter(Objects::nonNull) .collect(Collectors.toSet()); Map siteNameMap = new HashMap<>(); if (!siteIds.isEmpty()) { List sites = this.baseMapper.enterpriseAnalysisSites(new ArrayList<>(siteIds)); if (sites != null) { for (EnterpriseChargingAggRow.Site site : sites) { siteNameMap.put(site.getSiteId(), site.getSiteName()); } } } Map companyAggs = new LinkedHashMap<>(); for (EnterpriseChargingAggRow.Invoice invoice : invoices) { List details = detailsByInvoice.getOrDefault(invoice.getId(), Collections.emptyList()); if (details.isEmpty()) { // 无充电明细(order_type=1)的发票不计,等同原 INNER JOIN tod.order_type=1 语义 continue; } List invoiceOrders = new ArrayList<>(); BigDecimal detailAmount = BigDecimal.ZERO; for (EnterpriseChargingAggRow.Detail detail : details) { if (orderFilter && !matchedOrderIds.contains(detail.getOrderId())) { continue; } detailAmount = detailAmount.add(nullToZero(detail.getInvoiceAmount())); EnterpriseChargingAggRow.Order order = orderMap.get(detail.getOrderId()); if (order != null) { invoiceOrders.add(order); } } if (orderFilter && invoiceOrders.isEmpty()) { // 订单级筛选后该发票无命中订单 → 剔除,等同原 LEFT JOIN + 订单级 WHERE 语义 continue; } BigDecimal invoiceAmount = detailAmount.signum() > 0 ? detailAmount : nullToZero(invoice.getTotalAmount()); String key = companyKey(invoice.getCompanyName(), invoice.getSocialCreditCode()); EnterpriseCompanyAgg agg = companyAggs.get(key); if (agg == null) { agg = new EnterpriseCompanyAgg(invoice.getCompanyName(), invoice.getSocialCreditCode(), invoice.getCompanyAddress(), invoice.getCompanyPhone()); companyAggs.put(key, agg); } agg.invoiceCount++; agg.invoiceAmount = agg.invoiceAmount.add(invoiceAmount); for (EnterpriseChargingAggRow.Order order : invoiceOrders) { if (order.getStartTime() != null) { agg.startTimes.add(order.getStartTime()); if (agg.latestChargingTime == null || order.getStartTime().isAfter(agg.latestChargingTime)) { agg.latestChargingTime = order.getStartTime(); } } if (order.getSiteId() != null) { agg.siteOrderIds.computeIfAbsent(order.getSiteId(), k -> new HashSet<>()).add(order.getOrderId()); } } } if (companyAggs.isEmpty()) { pageInfo.setRecords(Collections.emptyList()); return R.ok(pageInfo); } List all = companyAggs.values().stream() .sorted(Comparator.comparingLong((EnterpriseCompanyAgg agg) -> agg.invoiceCount).reversed()) .map(agg -> buildEnterpriseAnalysisVO(agg, siteNameMap)) .collect(Collectors.toList()); pageInfo.setTotal(all.size()); long offset = (long) (pageCurr - 1L) * pageSize; if (offset >= all.size()) { pageInfo.setRecords(Collections.emptyList()); } else { int toIndex = (int) Math.min(offset + pageSize, all.size()); pageInfo.setRecords(all.subList((int) offset, toIndex)); } return R.ok(pageInfo); } private boolean hasEnterpriseOrderFilter(EnterpriseChargingAnalysisQuery query) { return query.getSiteType() != null || query.getPartnerId() != null || query.getSiteId() != null || query.getCreateTime1() != null || query.getCreateTime2() != null || (query.getCode() != null && !query.getCode().isEmpty()) || query.getOrderType() != null || (query.getPhone() != null && !query.getPhone().isEmpty()) || query.getStatus() != null || (query.getPartnerName() != null && !query.getPartnerName().isEmpty()) || query.getOrderSource() != null || query.getStartTime1() != null || query.getStartTime2() != null || query.getEndTime1() != null || query.getEndTime2() != null; } private EnterpriseChargingAnalysisVO buildEnterpriseAnalysisVO(EnterpriseCompanyAgg agg, Map siteNameMap) { EnterpriseChargingAnalysisVO vo = new EnterpriseChargingAnalysisVO(); vo.setCompanyName(agg.companyName); vo.setSocialCreditCode(agg.socialCreditCode); vo.setCompanyAddress(agg.companyAddress); vo.setCompanyPhone(agg.companyPhone); vo.setInvoiceCount(agg.invoiceCount); vo.setInvoiceAmount(agg.invoiceAmount); vo.setLatestChargingTime(agg.latestChargingTime); String topSiteNames = agg.siteOrderIds.entrySet().stream() .sorted(Comparator.comparingInt((Map.Entry> e) -> e.getValue().size()).reversed()) .limit(3) .map(e -> siteNameMap.getOrDefault(e.getKey(), "")) .filter(org.springframework.util.StringUtils::hasText) .collect(Collectors.joining("、")); vo.setTopSiteNames(topSiteNames); vo.setAverageChargingInterval(calculateAverageChargingInterval(new ArrayList<>(agg.startTimes))); return vo; } private static class EnterpriseCompanyAgg { private final String companyName; private final String socialCreditCode; private final String companyAddress; private final String companyPhone; private long invoiceCount; private BigDecimal invoiceAmount = BigDecimal.ZERO; private LocalDateTime latestChargingTime; private final Map> siteOrderIds = new HashMap<>(); private final TreeSet startTimes = new TreeSet<>(); private EnterpriseCompanyAgg(String companyName, String socialCreditCode, String companyAddress, String companyPhone) { this.companyName = companyName; this.socialCreditCode = socialCreditCode; this.companyAddress = companyAddress; this.companyPhone = companyPhone; } } private String companyKey(String companyName, String socialCreditCode) { return Objects.toString(companyName, "") + "\u0000" + Objects.toString(socialCreditCode, ""); } @Override public R> enterpriseChargingDetail(EnterpriseChargingAnalysisQuery query) { PageInfo pageInfo = new PageInfo<>(query.getPageCurr(), query.getPageSize()); Map invoiceDetailMap = getEnterpriseInvoiceDetailMap(query); if (invoiceDetailMap.isEmpty()) { pageInfo.setTotal(0L); pageInfo.setRecords(Collections.emptyList()); return R.ok(pageInfo); } List orderIds = new ArrayList<>(invoiceDetailMap.keySet()); long total = countEnterpriseChargingDetails(orderIds, query); pageInfo.setTotal(total); if (total == 0L) { pageInfo.setRecords(Collections.emptyList()); return R.ok(pageInfo); } List list = queryEnterpriseChargingDetailPage( orderIds, query, pageInfo.offset(), pageInfo.getSize()); fillEnterpriseInvoiceAmounts(list, invoiceDetailMap); fillEnterpriseChargingPeriodCounts(list); pageInfo.setRecords(list); return R.ok(pageInfo); } @Override public List enterpriseChargingDetailExport(EnterpriseChargingAnalysisQuery query) { Map invoiceDetailMap = getEnterpriseInvoiceDetailMap(query); if (invoiceDetailMap.isEmpty()) { return Collections.emptyList(); } List list = queryEnterpriseChargingDetailPage( new ArrayList<>(invoiceDetailMap.keySet()), query, 0L, Long.MAX_VALUE); fillEnterpriseInvoiceAmounts(list, invoiceDetailMap); fillEnterpriseChargingPeriodCounts(list); return list; } private long countEnterpriseChargingDetails(List orderIds, EnterpriseChargingAnalysisQuery query) { long total = 0L; for (int start = 0; start < orderIds.size(); start += ENTERPRISE_ORDER_ID_BATCH_SIZE) { int end = Math.min(start + ENTERPRISE_ORDER_ID_BATCH_SIZE, orderIds.size()); Long batchTotal = this.baseMapper.enterpriseChargingDetailCount(orderIds.subList(start, end), query); if (batchTotal != null) { total += batchTotal; } } return total; } private List queryEnterpriseChargingDetailPage(List orderIds, EnterpriseChargingAnalysisQuery query, long offset, long pageSize) { if (orderIds == null || orderIds.isEmpty() || pageSize <= 0L) { return Collections.emptyList(); } long requiredRows = pageSize == Long.MAX_VALUE || offset > Long.MAX_VALUE - pageSize ? Long.MAX_VALUE : offset + pageSize; List candidates = new ArrayList<>(); for (int start = 0; start < orderIds.size(); start += ENTERPRISE_ORDER_ID_BATCH_SIZE) { int end = Math.min(start + ENTERPRISE_ORDER_ID_BATCH_SIZE, orderIds.size()); List batchOrderIds = orderIds.subList(start, end); long batchLimit = Math.min(requiredRows, batchOrderIds.size()); candidates.addAll(this.baseMapper.enterpriseChargingDetail(batchOrderIds, query, 0L, batchLimit)); } candidates.sort(Comparator .comparing(EnterpriseChargingDetailVO::getCreateTime, Comparator.nullsLast(Comparator.reverseOrder())) .thenComparing(EnterpriseChargingDetailVO::getId, Comparator.nullsLast(Comparator.reverseOrder()))); int fromIndex = (int) Math.min(offset, candidates.size()); long requestedEnd = pageSize == Long.MAX_VALUE || fromIndex > Long.MAX_VALUE - pageSize ? Long.MAX_VALUE : fromIndex + pageSize; int toIndex = (int) Math.min(requestedEnd, candidates.size()); return new ArrayList<>(candidates.subList(fromIndex, toIndex)); } private Map getEnterpriseInvoiceDetailMap(EnterpriseChargingAnalysisQuery query) { List invoiceIds = this.baseMapper.enterpriseInvoiceIds(query); if (invoiceIds == null || invoiceIds.isEmpty()) { return Collections.emptyMap(); } Map detailMap = new LinkedHashMap<>(); for (int start = 0; start < invoiceIds.size(); start += ENTERPRISE_INVOICE_BATCH_SIZE) { int end = Math.min(start + ENTERPRISE_INVOICE_BATCH_SIZE, invoiceIds.size()); List details = this.baseMapper.enterpriseInvoiceChargingDetails(invoiceIds.subList(start, end)); if (details == null) { continue; } for (TOrderInvoiceDetail detail : details) { if (detail == null || detail.getOrderId() == null) { continue; } detailMap.merge(detail.getOrderId(), detail, this::mergeEnterpriseInvoiceDetail); } } return detailMap; } private TOrderInvoiceDetail mergeEnterpriseInvoiceDetail(TOrderInvoiceDetail current, TOrderInvoiceDetail addition) { current.setElectrovalence(nullToZero(current.getElectrovalence()).add(nullToZero(addition.getElectrovalence()))); current.setServiceCharge(nullToZero(current.getServiceCharge()).add(nullToZero(addition.getServiceCharge()))); return current; } private void fillEnterpriseInvoiceAmounts(List list, Map invoiceDetailMap) { if (list == null || list.isEmpty()) { return; } for (EnterpriseChargingDetailVO detail : list) { TOrderInvoiceDetail invoiceDetail = invoiceDetailMap.get(detail.getId()); detail.setElectrovalence(invoiceDetail == null ? BigDecimal.ZERO : nullToZero(invoiceDetail.getElectrovalence())); detail.setServiceCharge(invoiceDetail == null ? BigDecimal.ZERO : nullToZero(invoiceDetail.getServiceCharge())); } } private void fillEnterpriseChargingPeriodCounts(List list) { if (list == null || list.isEmpty()) { return; } List orderIds = list.stream() .map(EnterpriseChargingDetailVO::getId) .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); Map periodCountMap = new HashMap<>(); for (int start = 0; start < orderIds.size(); start += ENTERPRISE_DETAIL_PERIOD_BATCH_SIZE) { int end = Math.min(start + ENTERPRISE_DETAIL_PERIOD_BATCH_SIZE, orderIds.size()); List> rows = this.baseMapper.enterpriseChargingPeriodCounts(orderIds.subList(start, end)); if (rows == null) { continue; } for (Map row : rows) { Long orderId = mapLong(row, "orderId", "orderid", "charging_order_id"); Integer periodCount = mapInteger(row, "periodCount", "periodcount"); if (orderId != null) { periodCountMap.put(orderId, periodCount == null ? 0 : periodCount); } } } for (EnterpriseChargingDetailVO detail : list) { detail.setPeriodCount(periodCountMap.getOrDefault(detail.getId(), 0)); } } private Long mapLong(Map row, String... keys) { Object value = mapValue(row, keys); return value instanceof Number ? ((Number) value).longValue() : null; } private Integer mapInteger(Map row, String... keys) { Object value = mapValue(row, keys); return value instanceof Number ? ((Number) value).intValue() : null; } private Object mapValue(Map row, String... keys) { if (row == null) { return null; } for (String key : keys) { if (row.containsKey(key)) { return row.get(key); } } for (Map.Entry entry : row.entrySet()) { for (String key : keys) { if (entry.getKey() != null && entry.getKey().equalsIgnoreCase(key)) { return entry.getValue(); } } } return null; } private EnterpriseChargingAnalysisQuery buildEnterpriseRowQuery(EnterpriseChargingAnalysisQuery source, EnterpriseChargingAnalysisVO row) { EnterpriseChargingAnalysisQuery rowQuery = new EnterpriseChargingAnalysisQuery(); BeanUtils.copyProperties(source, rowQuery); rowQuery.setCompanyName(null); rowQuery.setExactCompanyName(row.getCompanyName()); rowQuery.setSocialCreditCode(row.getSocialCreditCode()); return rowQuery; } private BigDecimal calculateAverageChargingInterval(List times) { if (times == null || times.size() < 2) { return BigDecimal.ZERO; } long totalSeconds = 0L; for (int i = 1; i < times.size(); i++) { long seconds = Duration.between(times.get(i - 1), times.get(i)).getSeconds(); if (seconds > 0) { totalSeconds += seconds; } } return BigDecimal.valueOf(totalSeconds) .divide(BigDecimal.valueOf(times.size() - 1L), 4, RoundingMode.HALF_UP) .divide(BigDecimal.valueOf(3600), 2, RoundingMode.HALF_UP); } private static final int SERIAL_ORDER_ID_SEARCH_LIMIT = 5000; private static final int SITE_ORDER_ID_SEARCH_LIMIT = 50000; @Override public R> getRefundList(ChargingRefundDto chargingRefundDto) { int pageCurr = chargingRefundDto.getPageCurr() == null ? 1 : chargingRefundDto.getPageCurr(); int pageSize = chargingRefundDto.getPageSize() == null ? 50 : chargingRefundDto.getPageSize(); PageInfo pageInfo = new PageInfo<>(pageCurr, pageSize); long offset = (long) (pageCurr - 1) * pageSize; // 每个 UNION 分支须取 offset+pageSize 行,外层再按 offset,pageSize 切页,否则翻页时池子不足导致丢数据 int branchLimit = (int) Math.min(offset + pageSize, Integer.MAX_VALUE); boolean filterBySerial = StringUtils.hasLength(chargingRefundDto.getSerialNumber()); boolean filterBySite = chargingRefundDto.getSiteIds() != null && !chargingRefundDto.getSiteIds().isEmpty(); List list; if (!filterBySerial && !filterBySite) { // 无筛选:手工分页。分页拦截器会对 UNION 查询生成全量 COUNT 且无法把 ORDER BY/LIMIT 下推到分片, // 这里改为分支内下推(getRefundListPage)加分支 COUNT 求和(countRefundList) pageInfo.setTotal(this.baseMapper.countRefundList(chargingRefundDto)); list = this.baseMapper.getRefundListPage(chargingRefundDto, offset, pageSize, branchLimit); } else { // 筛选:逆反查。先按条件查订单 id(t_charging_order 等可下推/走索引), // 再按退款表分片键 IN 过滤(可路由),替代跨分片 LEFT JOIN RefundOrderIds ids = resolveRefundOrderIds(chargingRefundDto); if (ids.isEmpty()) { pageInfo.setRecords(Collections.emptyList()); return R.ok(pageInfo); } pageInfo.setTotal(this.baseMapper.countRefundListByOrderIds(chargingRefundDto, ids.type1OrderIds, ids.type2OrderIds, ids.type3OrderIds)); list = this.baseMapper.getRefundListByOrderIds(chargingRefundDto, ids.type1OrderIds, ids.type2OrderIds, ids.type3OrderIds, offset, pageSize, branchLimit); } enrichRefundOrderInfo(list); for (TChargingOrderRefund refund : list) { refund.setUid(refund.getType() + "-" + refund.getId()); } pageInfo.setRecords(list); return R.ok(pageInfo); } /** * 逆反查订单 id:serialNumber 匹配充电/商城/会员订单 id(各自 LIMIT 防爆), * siteIds 匹配充电订单 id;两者同时存在时取交集。 * 返回约定:null = 该类型不过滤;空列表 = 该类型无匹配(SQL 侧 AND 1=0)。 */ private RefundOrderIds resolveRefundOrderIds(ChargingRefundDto chargingRefundDto) { List type1OrderIds = null; List type2OrderIds = null; List type3OrderIds = null; if (StringUtils.hasLength(chargingRefundDto.getSerialNumber())) { // 模糊匹配结果可能很大(短流水号),截断上限防 IN 列表爆炸;匹配不到则列表为空 type1OrderIds = safeIds(this.baseMapper.listChargingOrderIdsBySerialNumber( chargingRefundDto.getSerialNumber(), SERIAL_ORDER_ID_SEARCH_LIMIT)); type2OrderIds = safeIds(this.baseMapper.listShoppingOrderIdsBySerialNumber( chargingRefundDto.getSerialNumber(), SERIAL_ORDER_ID_SEARCH_LIMIT)); type3OrderIds = safeIds(this.baseMapper.listVipOrderIdsBySerialNumber( chargingRefundDto.getSerialNumber(), SERIAL_ORDER_ID_SEARCH_LIMIT)); warnIfTruncated(chargingRefundDto.getSerialNumber(), type1OrderIds, SERIAL_ORDER_ID_SEARCH_LIMIT, "充电订单"); warnIfTruncated(chargingRefundDto.getSerialNumber(), type2OrderIds, SERIAL_ORDER_ID_SEARCH_LIMIT, "商城订单"); warnIfTruncated(chargingRefundDto.getSerialNumber(), type3OrderIds, SERIAL_ORDER_ID_SEARCH_LIMIT, "会员订单"); } if (chargingRefundDto.getSiteIds() != null && !chargingRefundDto.getSiteIds().isEmpty()) { List siteMatched = safeIds(this.baseMapper.listChargingOrderIdsBySiteIds( chargingRefundDto.getSiteIds(), SITE_ORDER_ID_SEARCH_LIMIT)); warnIfTruncated(String.valueOf(chargingRefundDto.getSiteIds()), siteMatched, SITE_ORDER_ID_SEARCH_LIMIT, "站点订单"); if (type1OrderIds == null) { type1OrderIds = siteMatched; } else { type1OrderIds.retainAll(new HashSet<>(siteMatched)); } } return new RefundOrderIds(type1OrderIds, type2OrderIds, type3OrderIds); } /** 反查结果达到上限说明可能被截断,告警便于排查(截断会导致部分退款不在列表内) */ private void warnIfTruncated(String condition, List ids, int limit, String orderType) { if (ids.size() >= limit) { log.warn("退款列表逆反查:条件[{}]匹配{}数达到上限{},结果可能被截断", condition, orderType, limit); } } private static List safeIds(List ids) { return ids == null ? new ArrayList<>() : new ArrayList<>(ids); } /** 逆反查结果:null = 不过滤;空列表 = 无匹配 */ private static class RefundOrderIds { private final List type1OrderIds; private final List type2OrderIds; private final List type3OrderIds; private RefundOrderIds(List type1OrderIds, List type2OrderIds, List type3OrderIds) { this.type1OrderIds = type1OrderIds; this.type2OrderIds = type2OrderIds; this.type3OrderIds = type3OrderIds; } /** 三个类型全部明确无匹配(非 null 且为空)时,整页为空,可短路 */ private boolean isEmpty() { return type1OrderIds != null && type1OrderIds.isEmpty() && type2OrderIds != null && type2OrderIds.isEmpty() && type3OrderIds != null && type3OrderIds.isEmpty(); } } private void enrichRefundOrderInfo(List refunds) { if (refunds == null || refunds.isEmpty()) { return; } Map> orderIdsByType = refunds.stream() .filter(Objects::nonNull) .filter(refund -> refund.getType() != null && StringUtils.hasLength(refund.getOrderId())) .collect(Collectors.groupingBy(TChargingOrderRefund::getType, Collectors.mapping(refund -> Long.valueOf(refund.getOrderId()), Collectors.toList()))); Map chargingOrderMap = orderIdsByType.getOrDefault(1, Collections.emptyList()).isEmpty() ? Collections.emptyMap() : indexByLongId(listByIds(orderIdsByType.get(1)), TChargingOrder::getId); Map shoppingOrderMap = orderIdsByType.getOrDefault(2, Collections.emptyList()).isEmpty() ? Collections.emptyMap() : indexByLongId(shoppingOrderService.listByIds(orderIdsByType.get(2)), TShoppingOrder::getId); Map vipOrderMap = orderIdsByType.getOrDefault(3, Collections.emptyList()).isEmpty() ? Collections.emptyMap() : indexByLongId(vipOrderService.listByIds(orderIdsByType.get(3)), TVipOrder::getId); for (TChargingOrderRefund refund : refunds) { if (refund == null || refund.getType() == null || !StringUtils.hasLength(refund.getOrderId())) { continue; } Long orderId = Long.valueOf(refund.getOrderId()); if (refund.getType() == 1) { TChargingOrder order = chargingOrderMap.get(orderId); if (order != null) { refund.setSerailNumber(order.getRechargeSerialNumber()); refund.setPaymentAmount(order.getPaymentAmount()); refund.setPayTime(order.getCreateTime()); refund.setUserId(order.getAppUserId() == null ? null : String.valueOf(order.getAppUserId())); refund.setSiteId(order.getSiteId()); refund.setPayMethod(order.getPayMethod()); } } else if (refund.getType() == 2) { TShoppingOrder order = shoppingOrderMap.get(orderId); if (order != null) { refund.setSerailNumber(order.getSerialNumber()); refund.setPaymentAmount(order.getPaymentAmount()); refund.setPayTime(order.getCreateTime()); refund.setUserId(order.getAppUserId() == null ? null : String.valueOf(order.getAppUserId())); refund.setPayMethod(3); } } else if (refund.getType() == 3) { TVipOrder order = vipOrderMap.get(orderId); if (order != null) { refund.setSerailNumber(order.getSerialNumber()); refund.setPaymentAmount(order.getPaymentAmount()); refund.setPayTime(order.getCreateTime()); refund.setUserId(order.getAppUserId() == null ? null : String.valueOf(order.getAppUserId())); refund.setPayMethod(3); } } } } @Override public List chargingOrderGroup(ChargingPercentProvinceDto chargingPercentProvinceDto) { return this.baseMapper.chargingOrderGroup(chargingPercentProvinceDto); } @Override public List charge(LocalDate sixBefore, List siteIds, Boolean partnerView) { String sixBeforeValue = sixBefore == null ? null : sixBefore.toString(); List result = new ArrayList<>(); if (Boolean.TRUE.equals(partnerView)) { List partnerIncome = this.baseMapper.chargePartner(sixBeforeValue, siteIds); if (partnerIncome != null) { result.addAll(partnerIncome); } } else { List settlementIncome = this.baseMapper.chargeSettlement(sixBeforeValue, siteIds); List orderHistoryIncome = this.baseMapper.chargeOrderHistory(sixBeforeValue, siteIds); result.addAll(mergeMissingMonthlyIncome(settlementIncome, orderHistoryIncome)); String siteIdsCsv = siteIds == null ? null : siteIds.stream() .filter(Objects::nonNull) .map(String::valueOf) .collect(Collectors.joining(",")); List currentMonthIncome = this.baseMapper.chargeCurrentMonth(siteIdsCsv); if (currentMonthIncome != null) { result.addAll(currentMonthIncome); } } if (result != null) { for (SixChargingDto item : result) { if (item == null) { continue; } if (item.getMonth() != null) { try { int month = Integer.parseInt(item.getMonth()); if (month >= 1 && month <= 12) { item.setMonth(String.format("%02d", month)); } } catch (NumberFormatException ignored) { } } if (item.getElectrovalence() != null) { item.setElectrovalence(item.getElectrovalence().setScale(2, RoundingMode.HALF_UP)); } if (item.getServiceCharge() != null) { item.setServiceCharge(item.getServiceCharge().setScale(2, RoundingMode.HALF_UP)); } } } return result; } static List mergeMissingMonthlyIncome(List settlementIncome, List orderHistoryIncome) { List merged = new ArrayList<>(); Set settledMonths = new HashSet<>(); if (settlementIncome != null) { for (SixChargingDto item : settlementIncome) { if (item == null) { continue; } merged.add(item); Integer month = incomeMonth(item); if (month != null) { settledMonths.add(month); } } } if (orderHistoryIncome != null) { for (SixChargingDto item : orderHistoryIncome) { Integer month = incomeMonth(item); if (item != null && month != null && settledMonths.add(month)) { merged.add(item); } } } return merged; } private static Integer incomeMonth(SixChargingDto item) { if (item == null || item.getMonth() == null) { return null; } try { int month = Integer.parseInt(item.getMonth()); return month >= 1 && month <= 12 ? month : null; } catch (NumberFormatException ignored) { return null; } } @Override public List circle(List siteIds, LocalDate sixBefore, Boolean partnerView) { List result = this.baseMapper.circle(siteIds, sixBefore, partnerView); if (result != null) { for (SixCircleDto item : result) { if (item != null && item.getPaymentAmount() != null) { item.setPaymentAmount(item.getPaymentAmount().setScale(2, RoundingMode.HALF_UP)); } } } return result; } @Override public Map countAll(List siteIds, Boolean partnerView, LocalDate sixBefore) { Map result = this.baseMapper.countAll(siteIds, partnerView, sixBefore); if (!Boolean.TRUE.equals(partnerView) && result != null) { result.put("commission_amount", this.baseMapper.sumSuccessfulShareAmount(siteIds, sixBefore)); } return result; } /** * 统计近六月订单收入支付方式占比;服务层聚合避免复杂CASE SQL在当前数据库驱动中转换异常。 */ @Override public List payTypeCircle(LocalDate sixBefore, List 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 orders = this.lambdaQuery() .select(TChargingOrder::getRechargePaymentType, TChargingOrder::getPayMethod, TChargingOrder::getThirdPayAmount, TChargingOrder::getThirdRefundAmount, TChargingOrder::getBalancePayAmount, TChargingOrder::getBalanceRefundAmount, TChargingOrder::getPaymentAmount, TChargingOrder::getRechargeAmount, TChargingOrder::getRefundAmount) .eq(TChargingOrder::getDelFlag, 0) .eq(TChargingOrder::getRechargePaymentStatus, 2) .eq(TChargingOrder::getStatus, 5) .ge(sixBefore != null, TChargingOrder::getCreateTime, sixBefore == null ? null : sixBefore.atStartOfDay()) .in(TChargingOrder::getSiteId, siteIds) .list(); BigDecimal wechatAmount = BigDecimal.ZERO; BigDecimal alipayAmount = BigDecimal.ZERO; BigDecimal balanceAmount = BigDecimal.ZERO; for (TChargingOrder order : orders) { BigDecimal thirdPayAmount = getThirdPayNetAmount(order); BigDecimal balancePayAmount = getBalancePayNetAmount(order); Integer rechargePaymentType = order.getRechargePaymentType(); if (Integer.valueOf(1).equals(rechargePaymentType)) { wechatAmount = wechatAmount.add(thirdPayAmount); } if (Integer.valueOf(2).equals(rechargePaymentType) || Integer.valueOf(3).equals(rechargePaymentType)) { alipayAmount = alipayAmount.add(thirdPayAmount); } 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 buildPartnerShareCircleData(List> rows) { BigDecimal wechatAmount = BigDecimal.ZERO; BigDecimal alipayAmount = BigDecimal.ZERO; BigDecimal balanceAmount = BigDecimal.ZERO; if (rows != null) { for (Map 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); } /** * 构造固定三类支付方式数据,保证前端饼图在无订单时也有稳定结构。 */ private List buildPayTypeCircleData(BigDecimal wechatAmount, BigDecimal alipayAmount, BigDecimal balanceAmount) { List data = new ArrayList<>(); data.add(buildIncomeCircleDto("微信支付", wechatAmount)); data.add(buildIncomeCircleDto("支付宝支付", alipayAmount)); data.add(buildIncomeCircleDto("余额支付", balanceAmount)); return data; } /** * 构造单个饼图项并统一金额精度。 */ private SixIncomeCircleDto buildIncomeCircleDto(String name, BigDecimal paymentAmount) { SixIncomeCircleDto dto = new SixIncomeCircleDto(); dto.setName(name); dto.setPaymentAmount(nullToZero(paymentAmount).setScale(2, RoundingMode.HALF_EVEN)); return dto; } /** * 计算第三方渠道实收:优先使用拆分字段,兼容旧订单回退到paymentAmount/rechargeAmount。 */ private BigDecimal getThirdPayNetAmount(TChargingOrder order) { BigDecimal payAmount = order.getThirdPayAmount(); Integer payMethod = order.getPayMethod(); BigDecimal totalPaidAmount = firstNonNull(order.getPaymentAmount(), order.getRechargeAmount()); if (isNullOrZero(payAmount)) { if (Integer.valueOf(3).equals(payMethod)) { payAmount = totalPaidAmount; } else if (Integer.valueOf(2).equals(payMethod) && totalPaidAmount != null) { payAmount = nonNegative(totalPaidAmount.subtract(nullToZero(order.getBalancePayAmount()))); } } BigDecimal refundAmount = order.getThirdRefundAmount(); if (refundAmount == null && Integer.valueOf(3).equals(payMethod)) { refundAmount = order.getRefundAmount(); } return nonNegative(nullToZero(payAmount).subtract(nullToZero(refundAmount))); } /** * 计算余额渠道实收:组合支付优先使用拆分字段,兼容旧余额订单回退到paymentAmount。 */ private BigDecimal getBalancePayNetAmount(TChargingOrder order) { BigDecimal payAmount = order.getBalancePayAmount(); Integer payMethod = order.getPayMethod(); BigDecimal totalPaidAmount = firstNonNull(order.getPaymentAmount(), order.getRechargeAmount()); if (isNullOrZero(payAmount)) { if (Integer.valueOf(1).equals(payMethod)) { payAmount = totalPaidAmount; } else if (Integer.valueOf(2).equals(payMethod) && totalPaidAmount != null) { payAmount = nonNegative(totalPaidAmount.subtract(nullToZero(order.getThirdPayAmount()))); } } BigDecimal refundAmount = order.getBalanceRefundAmount(); if (refundAmount == null && (Integer.valueOf(1).equals(payMethod) || Integer.valueOf(2).equals(payMethod))) { refundAmount = order.getRefundAmount(); } return nonNegative(nullToZero(payAmount).subtract(nullToZero(refundAmount))); } private BigDecimal firstNonNull(BigDecimal first, BigDecimal second) { return first != null ? first : second; } private boolean isNullOrZero(BigDecimal value) { return value == null || value.compareTo(BigDecimal.ZERO) == 0; } private BigDecimal nullToZero(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; } private BigDecimal nonNegative(BigDecimal value) { return value.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : value; } @Override public List> getSumByType(LocalDate start, LocalDate end, List siteIds) { LocalDateTime start1 = start.atStartOfDay(); LocalDateTime end1 = end.atTime(LocalTime.MAX); return this.baseMapper.getSumByType(start1, end1, siteIds); } @Override public List> getDateData(LocalDate start, LocalDate end, List siteIds) { LocalDateTime start1 = start.atStartOfDay(); LocalDateTime end1 = end.atTime(LocalTime.MAX); return this.baseMapper.getDateData(start1, end1, siteIds); } @Override public List> getWeekData(LocalDate start, LocalDate end, List siteIds) { LocalDateTime start1 = start.atStartOfDay(); LocalDateTime end1 = end.atTime(LocalTime.MAX); return this.baseMapper.getWeekData(start1, end1, siteIds); } @Override public List> getMonthData(LocalDate start, LocalDate end, List siteIds) { LocalDateTime start1 = start.atStartOfDay(); LocalDateTime end1 = end.atTime(LocalTime.MAX); return this.baseMapper.getMonthData(start1, end1, siteIds); } @Override public List> getYearData(LocalDate start, LocalDate end, List siteIds) { LocalDateTime start1 = start.atStartOfDay(); LocalDateTime end1 = end.atTime(LocalTime.MAX); return this.baseMapper.getYearData(start1, end1, siteIds); } @Resource private SysUserClient sysUserClient; @Override public ChargingOrderTimeVO chargingList(ChargingListQuery dto) { return chargingPeriodStatistics(dto, true, true); } @Override public PageInfo chargingListPage(ChargingListQuery dto) { return chargingPeriodStatistics(dto, true, false).getList(); } @Override public ChargingOrderTimeVO chargingListSummary(ChargingListQuery dto) { return chargingPeriodStatistics(dto, false, true); } private ChargingOrderTimeVO chargingPeriodStatistics(ChargingListQuery dto, boolean includeList, boolean includeSummary) { String startTime1 = null; String startTime2 = null; String endTime1 = null; String endTime2 = null; if (StringUtils.hasLength(dto.getStartTime())) { String[] split = dto.getStartTime().split(" - "); startTime1 = split[0]; String[] split1 = split[1].split(":"); startTime2 = split1[0] + ":" + split1[1] + ":59"; } if (StringUtils.hasLength(dto.getEndTime())) { String[] split = dto.getEndTime().split(" - "); endTime1 = split[0]; String[] split1 = split[1].split(":"); endTime2 = split1[0] + ":" + split1[1] + ":59"; } ChargingOrderTimeVO chargingOrderTimeVO = new ChargingOrderTimeVO(); if (StringUtils.hasLength(dto.getPhone())) { List data = appUserClient.getUserIdsByPhone(dto.getPhone()).getData(); if (data != null) { if (!data.isEmpty()) { dto.setUserIds(data); } } else { dto.setUserIds(new ArrayList()); } if (dto.getUserIds().isEmpty()) { List carIds = dto.getUserIds(); carIds.add(-1L); dto.setUserIds(carIds); } } if (StringUtils.hasLength(dto.getLicensePlate())) { List data = appUserCarClient.getAppUserCarByLicensePlates(dto.getLicensePlate()).getData(); if (data != null) { if (!data.isEmpty()) { dto.setCarIds(data); } } else { dto.setCarIds(new ArrayList()); } if (dto.getCarIds().isEmpty()) { List carIds = dto.getCarIds(); carIds.add(-1L); dto.setCarIds(carIds); } } Long userId = tokenService.getLoginUser().getUserid(); // 查询当前登陆人按钮权限 SysUser sysUser = sysUserClient.getSysUser(tokenService.getLoginUser().getUserid()).getData(); Integer roleType = sysUser.getRoleType(); //如果没传siteId,获取当前登陆人所有的siteIds List siteIds = new ArrayList<>(); if (dto.getSiteId() == null) { if (userId != null) { List data = siteClient.getSiteListByUserId(userId).getData(); for (GetSiteListDTO datum : data) { siteIds.add(datum.getId()); } } } else { siteIds.add(dto.getSiteId()); } if (siteIds.isEmpty()) { siteIds.add(-1); } else { if (roleType == 2) { List integers = new ArrayList<>(); for (Integer siteId : siteIds) { // 校验有没有这个站点的权限 List t1 = partnerClient.getChargingOrderMenu(sysUser.getObjectId(), siteId).getData(); Boolean b = t1.get(1); if (b) { integers.add(siteId); } } siteIds = integers; } } dto.setSiteIds(siteIds); PageInfo pageInfo = null; List list = new ArrayList<>(); if (includeList) { pageInfo = new PageInfo<>(dto.getPageCurr(), dto.getPageSize()); List> maps = this.baseMapper.chargingList1(pageInfo, dto, startTime1, startTime2, endTime1, endTime2); List> accountingSummaries = loadChargingPeriodAccountingSummaries(maps); mergeChargingPeriodAccountingRows(maps, accountingSummaries); Map siteMap = loadChargingPeriodSites(maps); Map pileMap = loadChargingPeriodPiles(maps); Map gunMap = loadChargingPeriodGuns(maps); Map userMap = loadChargingPeriodUsers(maps); Map carMap = loadChargingPeriodCars(maps); for (Map map : maps) { if (null == map) { continue; } ChargingOrderListVO chargingOrderListVO = new ChargingOrderListVO(); if (roleType == 2) { List data = partnerClient.getChargingListMenu(sysUser.getObjectId(), Integer.valueOf(map.get("siteId").toString())).getData(); chargingOrderListVO.setAuthInfo(data.get(0)); chargingOrderListVO.setAuthCurve(data.get(1)); } 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())); Site site = siteMap.get(chargingOrderListVO.getSiteId()); chargingOrderListVO.setSiteName(site == null ? "" : site.getName()); chargingOrderListVO.setOrderType(map.get("orderType").toString()); Integer pileId = getNullableInteger(map, "chargingPileId"); Integer gunId = getNullableInteger(map, "chargingGunId"); TChargingPile pile = pileMap.get(pileId); TChargingGun gun = gunMap.get(gunId); String pileName = pile == null || pile.getName() == null ? "" : pile.getName(); String gunName = gun == null || gun.getName() == null ? "" : gun.getName(); chargingOrderListVO.setTerminalName(pileName.isEmpty() ? gunName : (gunName.isEmpty() ? pileName : pileName + "-" + gunName)); chargingOrderListVO.setElectricity(null == map.get("electricity") ? BigDecimal.ZERO : new BigDecimal(map.get("electricity").toString())); chargingOrderListVO.setChargingCapacity(chargingOrderListVO.getElectricity()); 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 = 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.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())); if (null != map.get("tripartitePlatformName")) { chargingOrderListVO.setTripartitePlatformName(map.get("tripartitePlatformName").toString()); } if (null != map.get("startTime")) { chargingOrderListVO.setStartTime(LocalDateTime.parse(map.get("startTime").toString(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); } if (null != map.get("endTime")) { chargingOrderListVO.setEndTime(LocalDateTime.parse(map.get("endTime").toString(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); } if (null != map.get("chargingSecond")) { chargingOrderListVO.setChargingSecond(Long.valueOf(map.get("chargingSecond").toString())); } else { chargingOrderListVO.setChargingSecond(0L); } if (null != map.get("startSoc")) { chargingOrderListVO.setEndSoc(map.get("startSoc").toString()); } if (null != map.get("endSoc")) { chargingOrderListVO.setEndSoc(map.get("endSoc").toString()); } if (null != map.get("status")) { chargingOrderListVO.setStatus(Integer.valueOf(map.get("status").toString())); } if (null != map.get("endMode")) { chargingOrderListVO.setEndMode(Integer.valueOf(map.get("endMode").toString())); } TAppUser appUser = userMap.get(getNullableLong(map, "appUserId")); if (appUser != null) chargingOrderListVO.setPhone(appUser.getPhone()); TAppUserCar appUserCar = carMap.get(getNullableLong(map, "appUserCarId")); if (appUserCar != null) chargingOrderListVO.setLicensePlate(appUserCar.getLicensePlate()); chargingOrderListVO.setIsWhitelist(null == map.get("isWhitelist") ? 0 : Integer.valueOf(map.get("isWhitelist").toString())); // 白名单退款金额由电费退款和服务费退款组成,列表展示总额,财务导出继续保留拆分字段。 chargingOrderListVO.setWhitelistElectricRefundAmount(getSummaryDecimal(map, "whitelistElectricRefundAmount")); chargingOrderListVO.setWhitelistServiceRefundAmount(getSummaryDecimal(map, "whitelistServiceRefundAmount")); chargingOrderListVO.setWhitelistRefundAmount(getSummaryDecimal(map, "whitelistRefundAmount")); BigDecimal jian = null == map.get("jian") ? BigDecimal.ZERO : new BigDecimal(map.get("jian").toString()); BigDecimal feng = null == map.get("feng") ? BigDecimal.ZERO : new BigDecimal(map.get("feng").toString()); BigDecimal ping = null == map.get("ping") ? BigDecimal.ZERO : new BigDecimal(map.get("ping").toString()); BigDecimal gu = null == map.get("gu") ? BigDecimal.ZERO : new BigDecimal(map.get("gu").toString()); BigDecimal total = jian.add(feng).add(ping).add(gu); if (total.compareTo(new BigDecimal("0")) != 0) { chargingOrderListVO.setElectronicProportion( jian.divide(total, 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")) + "%(尖)/" + feng.divide(total, 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")) + "%(峰)/" + ping.divide(total, 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")) + "%(平)/" + gu.divide(total, 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")) + "%(谷)"); } else { chargingOrderListVO.setElectronicProportion( "0" + "%(尖)/" + "0" + "%(峰)/" + "0" + "%(平)/" + "0" + "%(谷)"); } list.add(chargingOrderListVO); } fillChargingPeriodShareIncome(list); } if (includeSummary) { // 尖峰平谷充电到账 BigDecimal jianElectronic = new BigDecimal("0"); BigDecimal fengElectronic = new BigDecimal("0"); BigDecimal pingElectronic = new BigDecimal("0"); BigDecimal guElectronic = new BigDecimal("0"); // 尖峰平谷服务费 BigDecimal jianService = new BigDecimal("0"); BigDecimal fengService = new BigDecimal("0"); BigDecimal pingService = new BigDecimal("0"); BigDecimal guService = new BigDecimal("0"); // 尖峰平谷充电量 度数 BigDecimal jian = new BigDecimal("0"); BigDecimal feng = new BigDecimal("0"); BigDecimal ping = new BigDecimal("0"); BigDecimal gu = new BigDecimal("0"); Map map = this.baseMapper.chargingListSum(dto, startTime1, startTime2, endTime1, endTime2); if (map == null) { map = new HashMap<>(); } jianElectronic = getSummaryDecimal(map, "jianElectronic"); fengElectronic = getSummaryDecimal(map, "fengElectronic"); pingElectronic = getSummaryDecimal(map, "pingElectronic"); guElectronic = getSummaryDecimal(map, "guElectronic"); jianService = getSummaryDecimal(map, "jianService"); fengService = getSummaryDecimal(map, "fengService"); pingService = getSummaryDecimal(map, "pingService"); guService = getSummaryDecimal(map, "guService"); jian = getSummaryDecimal(map, "jian"); feng = getSummaryDecimal(map, "feng"); ping = getSummaryDecimal(map, "ping"); gu = getSummaryDecimal(map, "gu"); //订单数量 Integer summaryOrderCount = getNullableInteger(map, "orderCount"); int orderCount = summaryOrderCount == null ? 0 : summaryOrderCount; // 计算充电总度数 BigDecimal electronic = getSummaryDecimal(map, "electronic"); // 支付金额 BigDecimal paymentAmount = getSummaryDecimal(map, "paymentAmount"); // 电费 BigDecimal electrovalence = getSummaryDecimal(map, "electrovalence"); // 服务费 BigDecimal serviceCharge = getSummaryDecimal(map, "serviceCharge"); 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)); List summaryOrderIds = this.baseMapper.chargingListSummaryOrderIds( dto, startTime1, startTime2, endTime1, endTime2); Map shareIncomeMap = getChargingOrderShareIncomeSummary(summaryOrderIds); chargingOrderTimeVO.setPlatformIncome(getSummaryDecimal(shareIncomeMap, "platformIncomeTotal")); chargingOrderTimeVO.setPartnerIncome(getSummaryDecimal(shareIncomeMap, "partnerIncomeTotal")); // 计算尖峰平谷充电到账占比 BigDecimal add = jianElectronic.add(fengElectronic).add(pingElectronic).add(guElectronic); // 计算尖峰平谷服务费占比 BigDecimal add1 = jianService.add(fengService).add(pingService).add(guService); // 计算尖峰平谷充电量占比 BigDecimal add2 = jian.add(feng).add(ping).add(gu); if (add.compareTo(new BigDecimal("0")) != 0) { chargingOrderTimeVO.setCapacityProportion(jian + "度/" + feng + "度/" + ping + "度/" + gu + "度-" + jian.divide(add2, 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")) + "%/" + feng.divide(add2, 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")) + "%/" + ping.divide(add2, 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")) + "%/" + gu.divide(add2, 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")) + "%"); chargingOrderTimeVO.setServiceProportion(jianService + "元/" + fengService + "元/" + pingService + "元/" + guService + "元-" + jianService.divide(add1, 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")) + "%/" + fengService.divide(add1, 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")) + "%/" + pingService.divide(add1, 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")) + "%/" + guService.divide(add1, 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")) + "%"); chargingOrderTimeVO.setElectronicProportion(jianElectronic + "元/" + fengElectronic + "元/" + pingElectronic + "元/" + guElectronic + "元-" + jianElectronic.divide(add, 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")) + "%/" + fengElectronic.divide(add, 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")) + "%/" + pingElectronic.divide(add, 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")) + "%/" + guElectronic.divide(add, 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")) + "%"); } else { chargingOrderTimeVO.setCapacityProportion(jianElectronic + "度/" + fengElectronic + "度/" + pingElectronic + "度/" + guElectronic + "度-" + "0" + "%/" + "0" + "%/" + "0" + "%/" + "0" + "%"); chargingOrderTimeVO.setServiceProportion(jianService + "元/" + fengService + "元/" + pingService + "元/" + guService + "元-" + "0" + "%/" + "0" + "%/" + "0" + "%/" + "0" + "%"); chargingOrderTimeVO.setElectronicProportion(jian + "元/" + feng + "元/" + ping + "元/" + gu + "元-" + "0" + "%/" + "0" + "%/" + "0" + "%/" + "0" + "%"); } } if (includeList) { pageInfo.setRecords(list); chargingOrderTimeVO.setList(pageInfo); } return chargingOrderTimeVO; } private List> loadChargingPeriodAccountingSummaries(List> rows) { List orderIds = collectLongIds(rows, "id"); if (orderIds.isEmpty()) { return Collections.emptyList(); } List> summaries = new ArrayList<>(); for (int start = 0; start < orderIds.size(); start += CHARGING_PERIOD_ACCOUNTING_BATCH_SIZE) { int end = Math.min(start + CHARGING_PERIOD_ACCOUNTING_BATCH_SIZE, orderIds.size()); List> batch = this.baseMapper.chargingListAccountingSummary(orderIds.subList(start, end)); if (batch != null) { summaries.addAll(batch); } } return summaries; } static void mergeChargingPeriodAccountingRows(List> orders, List> summaries) { if (orders == null || orders.isEmpty()) { return; } Map> summaryByOrderId = new HashMap<>(); if (summaries != null) { for (Map summary : summaries) { Long orderId = mapLongIgnoreCase(summary, "orderId"); if (orderId != null) { summaryByOrderId.put(orderId, summary); } } } String[] amountKeys = {"periodElectricPrice", "periodServicePrice", "jian", "feng", "ping", "gu"}; for (Map order : orders) { Map summary = summaryByOrderId.get(mapLongIgnoreCase(order, "id")); for (String key : amountKeys) { Object value = mapValueIgnoreCase(summary, key); order.put(key, value == null ? BigDecimal.ZERO : value); } } } private static Long mapLongIgnoreCase(Map row, String key) { Object value = mapValueIgnoreCase(row, key); if (value instanceof Number) { return ((Number) value).longValue(); } return value == null ? null : Long.valueOf(value.toString()); } private static Object mapValueIgnoreCase(Map row, String key) { if (row == null) { return null; } if (row.containsKey(key)) { return row.get(key); } for (Map.Entry entry : row.entrySet()) { if (entry.getKey() != null && entry.getKey().equalsIgnoreCase(key)) { return entry.getValue(); } } return null; } private Map loadChargingPeriodSites(List> rows) { List ids = collectIntegerIds(rows, "siteId"); if (ids.isEmpty()) return Collections.emptyMap(); R> result = siteClient.getSiteByIds(ids); return indexByIntegerId(result == null ? null : result.getData(), Site::getId); } private Map loadChargingPeriodPiles(List> rows) { List siteIds = collectIntegerIds(rows, "siteId"); if (siteIds.isEmpty()) return Collections.emptyMap(); R> result = chargingPileClient.getChargingPileBySiteIds(siteIds); return indexByIntegerId(result == null ? null : result.getData(), TChargingPile::getId); } private Map loadChargingPeriodGuns(List> rows) { List ids = collectIntegerIds(rows, "chargingGunId"); if (ids.isEmpty()) return Collections.emptyMap(); R> result = chargingGunClient.getChargingGunByIds(ids); return indexByIntegerId(result == null ? null : result.getData(), TChargingGun::getId); } private Map loadChargingPeriodUsers(List> rows) { List ids = collectLongIds(rows, "appUserId"); if (ids.isEmpty()) return Collections.emptyMap(); R> result = appUserClient.getUserByIds(ids); return indexByLongId(result == null ? null : result.getData(), TAppUser::getId); } private Map loadChargingPeriodCars(List> rows) { List ids = collectLongIds(rows, "appUserCarId"); if (ids.isEmpty()) return Collections.emptyMap(); R> result = appUserCarClient.getCarByIds(ids); return indexByLongId(result == null ? null : result.getData(), TAppUserCar::getId); } private List collectIntegerIds(List> rows, String key) { if (rows == null) return Collections.emptyList(); return rows.stream().map(row -> getNullableInteger(row, key)).filter(Objects::nonNull).distinct().collect(Collectors.toList()); } private List collectLongIds(List> rows, String key) { if (rows == null) return Collections.emptyList(); return rows.stream().map(row -> getNullableLong(row, key)).filter(Objects::nonNull).distinct().collect(Collectors.toList()); } private Integer getNullableInteger(Map row, String key) { Object value = row == null ? null : row.get(key); return value == null ? null : Integer.valueOf(value.toString()); } private Long getNullableLong(Map row, String key) { Object value = row == null ? null : row.get(key); return value == null ? null : Long.valueOf(value.toString()); } private Map indexByIntegerId(List items, java.util.function.Function idGetter) { if (items == null) return Collections.emptyMap(); return items.stream().filter(Objects::nonNull).filter(item -> idGetter.apply(item) != null) .collect(Collectors.toMap(idGetter, item -> item, (left, right) -> left)); } private Map indexByLongId(List items, java.util.function.Function idGetter) { if (items == null) return Collections.emptyMap(); return items.stream().filter(Objects::nonNull).filter(item -> idGetter.apply(item) != null) .collect(Collectors.toMap(idGetter, item -> item, (left, right) -> left)); } @Override public List export(ChargingListQuery dto) { String startTime1 = null; String startTime2 = null; String endTime1 = null; String endTime2 = null; if (StringUtils.hasLength(dto.getStartTime())) { String[] split = dto.getStartTime().split(" - "); startTime1 = split[0]; startTime2 = split[1]; } if (StringUtils.hasLength(dto.getEndTime())) { String[] split = dto.getEndTime().split(" - "); endTime1 = split[0]; endTime2 = split[1]; } if (StringUtils.hasLength(dto.getPhone())) { List data = appUserClient.getUserIdsByPhone(dto.getPhone()).getData(); if (data != null) { if (!data.isEmpty()) { dto.setUserIds(data); } } else { dto.setUserIds(new ArrayList()); } if (dto.getUserIds().isEmpty()) { List carIds = dto.getUserIds(); carIds.add(-1L); dto.setUserIds(carIds); } } if (StringUtils.hasLength(dto.getLicensePlate())) { List data = appUserCarClient.getAppUserCarByLicensePlates(dto.getLicensePlate()).getData(); if (data != null) { if (!data.isEmpty()) { dto.setCarIds(data); } } else { dto.setCarIds(new ArrayList()); } if (dto.getCarIds().isEmpty()) { List carIds = dto.getCarIds(); carIds.add(-1L); dto.setCarIds(carIds); } } Long userId = tokenService.getLoginUser().getUserid(); // 查询当前登陆人按钮权限 SysUser sysUser = sysUserClient.getSysUser(tokenService.getLoginUser().getUserid()).getData(); Integer roleType = sysUser.getRoleType(); //如果没传siteId,获取当前登陆人所有的siteIds List siteIds = new ArrayList<>(); if (dto.getSiteId() == null) { if (userId != null) { List data = siteClient.getSiteListByUserId(userId).getData(); for (GetSiteListDTO datum : data) { siteIds.add(datum.getId()); } } } else { siteIds.add(dto.getSiteId()); } if (siteIds.isEmpty()) { siteIds.add(-1); } else { if (roleType == 2) { List integers = new ArrayList<>(); for (Integer siteId : siteIds) { // 校验有没有这个站点的权限 List t1 = partnerClient.getChargingOrderMenu(sysUser.getObjectId(), siteId).getData(); Boolean b = t1.get(1); if (b) { integers.add(siteId); } } siteIds = integers; } } dto.setSiteIds(siteIds); List list1 = this.baseMapper.chargingList(dto, startTime1, startTime2, endTime1, endTime2); fillChargingPeriodExportBalanceIncome(list1); fillChargingPeriodShareIncome(list1); return list1; } private void fillChargingPeriodShareIncome(List list) { if (list == null || list.isEmpty()) { return; } List orderIds = list.stream() .map(ChargingOrderListVO::getId) .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); Map> incomeMap = new HashMap<>(); int batchSize = 500; for (int i = 0; i < orderIds.size(); i += batchSize) { List batchIds = orderIds.subList(i, Math.min(i + batchSize, orderIds.size())); List> rows = this.baseMapper.chargingOrderShareIncomeByOrderIds(batchIds); if (rows == null) { continue; } for (Map row : rows) { Long orderId = getSummaryLong(row, "orderId"); if (orderId != null) { incomeMap.put(orderId, row); } } } for (ChargingOrderListVO item : list) { applyChargingPeriodShareIncome(item, incomeMap.get(item.getId())); } } void applyChargingPeriodShareIncome(ChargingOrderListVO item, Map row) { if (item == null) { return; } item.setPlatformIncome(getSummaryDecimal(row, "platformIncome")); item.setPartnerIncome(getSummaryDecimal(row, "partnerIncome")); } /** * 充电时段统计导出补充余额收入;兑换码usedAmount包含赠送金额,需沿用订单列表口径拆分。 */ private void fillChargingPeriodExportBalanceIncome(List list) { if (list == null || list.isEmpty()) { return; } List orderIds = list.stream().map(ChargingOrderListVO::getId).filter(Objects::nonNull).collect(Collectors.toList()); if (orderIds.isEmpty()) { return; } Map> incomeMap = new HashMap<>(); List> rows = this.baseMapper.exchangeCodeBalanceIncomeByOrderIds(orderIds); if (rows != null) { for (Map row : rows) { Long orderId = getSummaryLong(row, "orderId"); if (orderId != null) { incomeMap.put(orderId, row); } } } for (ChargingOrderListVO item : list) { Map row = incomeMap.get(item.getId()); item.setBalanceIncome(getSummaryDecimal(row, "balanceIncome")); item.setBalanceGiftIncome(getSummaryDecimal(row, "balanceGiftIncome")); } } private void fillChargingOrderShareIncome(List list) { if (list == null || list.isEmpty()) { return; } List orderIds = list.stream().map(ChargingOrderVO::getId).filter(Objects::nonNull).collect(Collectors.toList()); if (orderIds.isEmpty()) { return; } Map> incomeMap = new HashMap<>(); List> rows = this.baseMapper.chargingOrderShareIncomeByOrderIds(orderIds); if (rows != null) { for (Map row : rows) { Long orderId = getSummaryLong(row, "orderId"); if (orderId != null) { incomeMap.put(orderId, row); } } } for (ChargingOrderVO item : list) { Map row = incomeMap.get(item.getId()); item.setPlatformIncome(getSummaryDecimal(row, "platformIncome")); item.setPartnerIncome(getSummaryDecimal(row, "partnerIncome")); } } @Override public ChargingOrderListInfoVO chargingInfo(String uid) { TChargingOrder chargingOrder = this.getById(uid); ChargingOrderListInfoVO chargingOrderListInfoVO = new ChargingOrderListInfoVO(); chargingOrderListInfoVO.setStartTime(chargingOrder.getStartTime()); chargingOrderListInfoVO.setEndTime(chargingOrder.getEndTime()); chargingOrderListInfoVO.setStatus(chargingOrder.getStatus()); BigDecimal bigDecimal = new BigDecimal("0.006"); if (chargingOrder.getOrderAmount() != null) { BigDecimal multiply = chargingOrder.getPaymentAmount().multiply(bigDecimal); chargingOrderListInfoVO.setCommissionAmount(multiply.setScale(2, BigDecimal.ROUND_HALF_UP)); } chargingOrderListInfoVO.setElectrovalence(chargingOrder.getElectrovalence()); chargingOrderListInfoVO.setServiceCharge(chargingOrder.getServiceCharge()); if (chargingOrder.getElectrovalence() != null && chargingOrder.getServiceCharge() != null) { chargingOrderListInfoVO.setTotal(chargingOrder.getElectrovalence().add(chargingOrder.getServiceCharge())); } else { chargingOrderListInfoVO.setTotal(new BigDecimal("0")); } if (chargingOrderListInfoVO.getTotal().compareTo(new BigDecimal("0")) > 0) { if (chargingOrder.getCouponDiscountAmount() != null && chargingOrder.getVipDiscountAmount() != null && chargingOrder.getSharingAmount() != null && chargingOrder.getCommissionAmount() != null) { chargingOrderListInfoVO.setRealTotal(chargingOrderListInfoVO.getTotal().subtract(chargingOrder.getCouponDiscountAmount()).subtract(chargingOrder.getVipDiscountAmount()).subtract(chargingOrder.getSharingAmount()).subtract(chargingOrder.getCommissionAmount())); } else { chargingOrderListInfoVO.setRealTotal(chargingOrderListInfoVO.getTotal()); } } else { chargingOrderListInfoVO.setRealTotal(new BigDecimal("0")); } chargingOrderListInfoVO.setCode(chargingOrder.getCode()); List data = siteClient.getSiteByIds(Arrays.asList(chargingOrder.getSiteId())).getData(); if (!data.isEmpty()) { chargingOrderListInfoVO.setSiteName(data.get(0).getName()); chargingOrderListInfoVO.setSiteAddress(data.get(0).getAddress()); } if (chargingOrder.getChargingGunId() != null) { TChargingGun data1 = chargingGunClient.getChargingGunById(chargingOrder.getChargingGunId()).getData(); if (data1 != null) { TChargingPile data2 = chargingPileClient.getChargingPileById(data1.getChargingPileId()).getData(); if (data2 != null) { chargingOrderListInfoVO.setGunCode(data1.getCode()); chargingOrderListInfoVO.setTerminalName(data2.getName() + data1.getName()); } } } chargingOrderListInfoVO.setChargingCapacity(chargingOrder.getChargingCapacity()); chargingOrderListInfoVO.setEndMode(chargingOrder.getEndMode()); // 获取开始SOC 结束soc if (chargingOrder.getCode() != null) { List data6 = uploadRealTimeMonitoringDataService.getDataByOrderCodeAsc(chargingOrder.getCode()); if (data6 != null) { for (UploadRealTimeMonitoringData uploadRealTimeMonitoringData : data6) { if (uploadRealTimeMonitoringData.getOutput_current() != null && uploadRealTimeMonitoringData.getOutput_voltage() != null) { uploadRealTimeMonitoringData.setPower(uploadRealTimeMonitoringData.getOutput_voltage() .multiply(uploadRealTimeMonitoringData.getOutput_current()).divide(new BigDecimal(1000), 2, BigDecimal.ROUND_HALF_UP)); } } if (!data6.isEmpty()) { int min = 100; int max = 0; for (UploadRealTimeMonitoringData uploadRealTimeMonitoringData : data6) { if (uploadRealTimeMonitoringData.getSoc() > max) { max = uploadRealTimeMonitoringData.getSoc(); } if (uploadRealTimeMonitoringData.getSoc() != 0 && uploadRealTimeMonitoringData.getSoc() < min) { min = uploadRealTimeMonitoringData.getSoc(); } } chargingOrderListInfoVO.setEndSoc(max + ""); chargingOrderListInfoVO.setStartSoc(min + ""); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); for (UploadRealTimeMonitoringData uploadRealTimeMonitoringData : data6) { uploadRealTimeMonitoringData.setCreateTime(simpleDateFormat.format(uploadRealTimeMonitoringData.getCreate_time())); } } } chargingOrderListInfoVO.setList(data6); } if (chargingOrderListInfoVO.getStartTime() != null && chargingOrderListInfoVO.getEndTime() != null) { LocalDateTime startTime = chargingOrderListInfoVO.getStartTime(); LocalDateTime endTime = chargingOrderListInfoVO.getEndTime(); // 计算时间差 单位秒 long between = ChronoUnit.SECONDS.between(startTime, endTime); chargingOrderListInfoVO.setChargingSecond(between + ""); } if (chargingOrder.getAppUserCarId() != null) { List data3 = appUserCarClient.getCarByIds(Collections.singletonList(chargingOrder.getAppUserCarId())).getData(); if (data3 != null && !data3.isEmpty()) { chargingOrderListInfoVO.setLicensePlate(data3.get(0).getLicensePlate()); } } else { chargingOrderListInfoVO.setLicensePlate(chargingOrder.getPlateNum()); } chargingOrderListInfoVO.setStartTime(chargingOrder.getStartTime()); chargingOrderListInfoVO.setEndTime(chargingOrder.getEndTime()); chargingOrderListInfoVO.setId(chargingOrder.getId()); chargingOrderListInfoVO.setUid(chargingOrder.getId().toString()); chargingOrderListInfoVO.setCouponDiscountAmount(chargingOrder.getCouponDiscountAmount()); chargingOrderListInfoVO.setVipDiscountAmount(chargingOrder.getVipDiscountAmount()); if (chargingOrder.getOrderSource().equals(2)) { chargingOrderListInfoVO.setSharingAmount(chargingOrder.getServiceCharge().multiply(new BigDecimal("0.2")).setScale(2, BigDecimal.ROUND_HALF_UP)); } else { chargingOrderListInfoVO.setSharingAmount(chargingOrder.getSharingAmount()); } // 查询费用明细列表 List list = chargingOrderAccountingStrategyService.lambdaQuery() .eq(TChargingOrderAccountingStrategy::getChargingOrderId, chargingOrder.getId()) .list(); chargingOrderListInfoVO.setList1(list); return chargingOrderListInfoVO; } /** * 处理充电订单实时监控数据相关的业务逻辑 * * @param query */ @Override public void chargeMonitoring(UploadRealTimeMonitoringDataQuery query) { if (com.ruoyi.common.core.utils.StringUtils.isNotEmpty(query.getTransaction_serial_number())) { //获取当前的计费策略 TChargingOrder chargingOrder1 = this.getOne(new LambdaQueryWrapper().eq(TChargingOrder::getCode, query.getTransaction_serial_number())); BigDecimal residualAmount = chargingOrder1.getRechargeAmount().add(chargingOrder1.getVipDiscountAmount()).subtract(query.getPaid_amount()); TChargingOrder chargingOrder = new TChargingOrder(); chargingOrder.setId(chargingOrder1.getId()); chargingOrder.setAppUserId(chargingOrder1.getAppUserId()); chargingOrder.setResidualAmount(residualAmount); if (query.getOutput_current().compareTo(BigDecimal.ZERO) != 0) { BigDecimal divide = (query.getOutput_current().multiply(query.getOutput_voltage())).divide(new BigDecimal(1000)); chargingOrder.setChargingPower(divide); chargingOrder.setCurrent(query.getOutput_current()); chargingOrder.setVoltage(query.getOutput_voltage()); chargingOrder.setPower(divide); chargingOrder.setNeedElec(query.getOutput_current()); TChargingGun chargingGun = new TChargingGun(); chargingGun.setId(chargingOrder1.getChargingGunId()); chargingGun.setChargingPower(divide); chargingGunClient.updateChargingGunById(chargingGun); } chargingOrder.setChargingCapacity(query.getCharging_degree()); chargingOrder.setElectricity(query.getCharging_degree()); chargingOrder.setTotalElectricity(new BigDecimal(query.getSoc())); this.updateById(chargingOrder); } } /** * 自动结束充电后的处理逻辑 * * @param orderCode */ @Override public void endCharge(String orderCode, Integer endMode) { TChargingOrder order = this.getOne(new LambdaQueryWrapper().eq(TChargingOrder::getCode, orderCode)); TChargingOrder chargingOrder = new TChargingOrder(); chargingOrder.setId(order.getId()); chargingOrder.setAppUserId(order.getAppUserId()); chargingOrder.setEndMode(endMode); this.updateById(chargingOrder); } /** * 异常结束充电处理逻辑 * * @param orderCode */ @Override public void excelEndCharge(String orderCode) { endCharge(orderCode, 0); } /** * 停止充电返回账单后计算费用 * * @param vo */ @Override @Transactional public R endChargeBillingCharge(TransactionRecordMessageVO vo) { RLock lock = redissonClient.getLock("endOrder:" + vo.getTransaction_serial_number()); boolean locked = false; try { 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 (locked) { TChargingOrder chargingOrder = this.getOne(new LambdaQueryWrapper().eq(TChargingOrder::getCode, vo.getTransaction_serial_number())); if (null == chargingOrder) { log.error("订单不存在,订单号:{}", vo.getTransaction_serial_number()); return R.fail("订单不存在,订单号:" + vo.getTransaction_serial_number()); } Integer status = chargingOrder.getStatus(); if (status == 5) { log.info("订单已结算,订单号:{}", vo.getTransaction_serial_number()); return R.fail("订单已结算,订单号:" + vo.getTransaction_serial_number()); } //如果使用优惠券需要判断优惠券是否满足使用条件 //退款金额=优惠券金额+剩余充电金额 BigDecimal periodElectricPrice_total = BigDecimal.ZERO; BigDecimal periodServicePrice_total = BigDecimal.ZERO; BigDecimal vipDiscountAmount_total = BigDecimal.ZERO; BigDecimal serviceCharge_total = BigDecimal.ZERO; BigDecimal total = BigDecimal.ZERO; //获取订单的计费策略 List accountingStrategyDetailOrderList = accountingStrategyDetailOrderClient.getAllAccountingStrategyDetailOrder(chargingOrder.getId()).getData(); if (accountingStrategyDetailOrderList.isEmpty()) { return R.fail("计费策略为空,无法计算费用"); } accountingStrategyDetailOrderList.get(accountingStrategyDetailOrderList.size() - 1).setEndTime("23:59"); //开始处理计费明细数据和优惠数据 chargingOrderAccountingStrategyService.remove(new LambdaQueryWrapper().eq(TChargingOrderAccountingStrategy::getChargingOrderId, chargingOrder.getId())); SimpleDateFormat sdfs = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); List lists = new ArrayList<>(); List dataList = uploadRealTimeMonitoringDataService.getDataByOrderCode(chargingOrder.getCode()); if (!dataList.isEmpty()) { vo.setStart_time(sdfs.format(dataList.get(dataList.size() - 1).getCreate_time()) + ".00"); vo.setEnd_time(sdfs.format(dataList.get(0).getCreate_time()) + ".00"); } //跨天 if (!vo.getStart_time().split(" ")[0].equals(vo.getEnd_time().split(" ")[0])) { //当天的 //开始通过计费策略遍历解析每个时段的费用明细 String start_time = vo.getStart_time(); String end_time = vo.getEnd_time(); long start = sdfs.parse(start_time).getTime(); long end = sdfs.parse(end_time).getTime(); Class clazz = vo.getClass(); /** * "start_time": "2025-01-03 23:06:43.00", * "end_time": "2025-01-04 00:39:39.00", */ for (int i = accountingStrategyDetailOrderList.size(); i > 0; i--) { AccountingStrategyDetailOrder strategyDetail = accountingStrategyDetailOrderList.get(i - 1); long time = sdfs.parse(start_time.split(" ")[0] + " " + strategyDetail.getEndTime() + ":00").getTime(); if (time < start) { break; } //阶段(1=尖阶段,2=峰阶段,3=平阶段,4=谷阶段) Object invoke = null; switch (strategyDetail.getType()) { case 1: //充电度数 invoke = clazz.getMethod("getSpike_charge" + (i)).invoke(vo); break; case 2: //充电度数 invoke = clazz.getMethod("getPeak_charge" + (i)).invoke(vo); break; case 3: //充电度数 invoke = clazz.getMethod("getFlat_charge" + (i)).invoke(vo); break; case 4: //充电度数 invoke = clazz.getMethod("getValley_charge" + (i)).invoke(vo); break; } if (null == invoke || Double.valueOf(invoke.toString()) == 0) { continue; } BigDecimal sharp_peak_charge = new BigDecimal(invoke.toString()); AccountingStrategyDetailOrderVo vo1 = new AccountingStrategyDetailOrderVo(); BeanUtils.copyProperties(strategyDetail, vo1); vo1.setChargingCapacity(sharp_peak_charge); lists.add(0, vo1); } //第二天的 //开始通过计费策略遍历解析每个时段的费用明细 for (int i = 0; i < accountingStrategyDetailOrderList.size(); i++) { AccountingStrategyDetailOrder strategyDetail = accountingStrategyDetailOrderList.get(i); long time = sdfs.parse(end_time.split(" ")[0] + " " + strategyDetail.getStartTime() + ":00").getTime(); if (time > end) { break; } //阶段(1=尖阶段,2=峰阶段,3=平阶段,4=谷阶段) Object invoke = null; switch (strategyDetail.getType()) { case 1: //充电度数 invoke = clazz.getMethod("getSpike_charge" + (i + 1)).invoke(vo); break; case 2: //充电度数 invoke = clazz.getMethod("getPeak_charge" + (i + 1)).invoke(vo); break; case 3: //充电度数 invoke = clazz.getMethod("getFlat_charge" + (i + 1)).invoke(vo); break; case 4: //充电度数 invoke = clazz.getMethod("getValley_charge" + (i + 1)).invoke(vo); break; } if (null == invoke || Double.valueOf(invoke.toString()) == 0) { continue; } BigDecimal sharp_peak_charge = new BigDecimal(invoke.toString()); AccountingStrategyDetailOrderVo vo1 = new AccountingStrategyDetailOrderVo(); BeanUtils.copyProperties(strategyDetail, vo1); vo1.setChargingCapacity(sharp_peak_charge); lists.add(vo1); } } else { for (int i = 0; i < accountingStrategyDetailOrderList.size(); i++) { Class clazz = vo.getClass(); AccountingStrategyDetailOrder strategyDetail = accountingStrategyDetailOrderList.get(i); //阶段(1=尖阶段,2=峰阶段,3=平阶段,4=谷阶段) Object invoke = null; switch (strategyDetail.getType()) { case 1: //充电度数 invoke = clazz.getMethod("getSpike_charge" + (i + 1)).invoke(vo); break; case 2: //充电度数 invoke = clazz.getMethod("getPeak_charge" + (i + 1)).invoke(vo); break; case 3: //充电度数 invoke = clazz.getMethod("getFlat_charge" + (i + 1)).invoke(vo); break; case 4: //充电度数 invoke = clazz.getMethod("getValley_charge" + (i + 1)).invoke(vo); break; } if (null == invoke || Double.valueOf(invoke.toString()) == 0) { continue; } BigDecimal sharp_peak_charge = new BigDecimal(invoke.toString()); AccountingStrategyDetailOrderVo vo1 = new AccountingStrategyDetailOrderVo(); BeanUtils.copyProperties(strategyDetail, vo1); vo1.setChargingCapacity(sharp_peak_charge); lists.add(vo1); } } //开始处理明细 for (int i = 0; i < lists.size(); i++) { AccountingStrategyDetailOrderVo strategyDetail = lists.get(i); BigDecimal sharp_peak_charge = strategyDetail.getChargingCapacity(); TChargingOrderAccountingStrategy chargingOrderAccountingStrategy = new TChargingOrderAccountingStrategy(); chargingOrderAccountingStrategy.setChargingOrderId(chargingOrder.getId()); chargingOrderAccountingStrategy.setAccountingStrategyDetailId(strategyDetail.getId()); chargingOrderAccountingStrategy.setType(strategyDetail.getType()); chargingOrderAccountingStrategy.setElectrovalence(strategyDetail.getElectrovalence()); chargingOrderAccountingStrategy.setServiceCharge(strategyDetail.getServiceCharge()); chargingOrderAccountingStrategy.setCostServiceCharge(strategyDetail.getCostServiceCharge()); if (i == 0) { String time = vo.getStart_time().split(" ")[1]; chargingOrderAccountingStrategy.setStartTime(time.substring(0, time.lastIndexOf(":"))); } else { chargingOrderAccountingStrategy.setStartTime(strategyDetail.getStartTime()); } if (i == lists.size() - 1) { String time = vo.getEnd_time().split(" ")[1]; chargingOrderAccountingStrategy.setEndTime(time.substring(0, time.lastIndexOf(":"))); } else { chargingOrderAccountingStrategy.setEndTime(strategyDetail.getEndTime()); } //已充电总度数 BigDecimal electrovalenc = strategyDetail.getElectrovalence().multiply(sharp_peak_charge); BigDecimal originalServicePrice = strategyDetail.getServiceCharge().multiply(sharp_peak_charge); BigDecimal serviceCharge = originalServicePrice; BigDecimal vipDiscountAmount = BigDecimal.ZERO; //计算优惠金额 if (null != chargingOrder.getVipDiscount()) { //0.58折 vipDiscountAmount = serviceCharge.multiply(new BigDecimal(1).subtract(chargingOrder.getVipDiscount())); serviceCharge = serviceCharge.multiply(chargingOrder.getVipDiscount()); } chargingOrderAccountingStrategy.setChargingCapacity(sharp_peak_charge); 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()); chargingOrderAccountingStrategyService.save(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)); } BigDecimal orderAmount = BigDecimal.valueOf(total.doubleValue()); //原金额 BigDecimal rechargeAmount = chargingOrder.getRechargeAmount(); //支付金额 BigDecimal payAmount = periodElectricPrice_total.add(serviceCharge_total); //退款金额=真实预付金额-实际支付金额;兼容曾在预付阶段错误抵扣优惠券的在途订单。 BigDecimal settlementPrepayment = ChargingCouponSupport.resolveSettlementPrepayment( rechargeAmount, chargingOrder.getBalancePayAmount(), chargingOrder.getThirdPayAmount(), chargingOrder.getCouponDiscountAmount()); BigDecimal refundAmount = settlementPrepayment.subtract(payAmount); UploadRealTimeMonitoringData uploadRealTimeMonitoringData = uploadRealTimeMonitoringDataService.getLastDataById(chargingOrder.getCode()); if (null != uploadRealTimeMonitoringData && null == chargingOrder.getEndMode()) { Integer soc = uploadRealTimeMonitoringData.getSoc(); if (soc >= 98) { chargingOrder.setEndMode(2); } else if (null != chargingOrder.getResidualAmount() && chargingOrder.getResidualAmount().compareTo(new BigDecimal(1)) <= 0) { chargingOrder.setEndMode(3); } else { chargingOrder.setEndMode(1); } } chargingOrder.setResidualAmount(rechargeAmount.subtract(total).setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setStartTime(LocalDateTime.parse(vo.getStart_time(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SS"))); chargingOrder.setEndTime(LocalDateTime.parse(vo.getEnd_time(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SS"))); chargingOrder.setStatus(5); chargingOrder.setOrderAmount(orderAmount.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setElectrovalence(periodElectricPrice_total.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setChargingCapacity(vo.getTotal_electricity()); chargingOrder.setElectricity(vo.getTotal_electricity()); chargingOrder.setVipDiscountAmount(vipDiscountAmount_total); if (vipDiscountAmount_total.compareTo(BigDecimal.ZERO) == 0) { chargingOrder.setVipDiscount(BigDecimal.valueOf(1)); } //计算优惠券 BigDecimal couponDiscount = BigDecimal.ZERO; if (!isWhitelistOrder(chargingOrder) && !isBalancePayMode(chargingOrder.getPayMethod()) && null != chargingOrder.getAppCouponId()) { Long appCouponId = chargingOrder.getAppCouponId(); TAppCoupon appCoupon = appCouponClient.getAppCouponById(chargingOrder.getAppCouponId()).getData(); String couponJson = appCoupon.getCouponJson(); TCoupon tCoupon = JSON.parseObject(couponJson, TCoupon.class); couponDiscount = ChargingCouponSupport.calculateSettlementDiscount( tCoupon, payAmount, serviceCharge_total); if (couponDiscount.compareTo(BigDecimal.ZERO) > 0) { appCoupon.setStatus(2); appCouponClient.updateAppCoupon(appCoupon); } else { appCouponClient.refund(appCouponId.toString()); chargingOrder.setAppCouponId(null); chargingOrder.setCouponDiscountAmount(BigDecimal.ZERO); } } BigDecimal[] whitelistRefundSplit = calculateWhitelistRefundSplit(chargingOrder, periodElectricPrice_total, serviceCharge_total); BigDecimal electricDiscount = whitelistRefundSplit[0]; BigDecimal serviceDiscount = whitelistRefundSplit[1]; BigDecimal whitelistDiscount = electricDiscount.add(serviceDiscount).setScale(4, RoundingMode.HALF_EVEN); 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).max(BigDecimal.ZERO); serviceCharge_total = serviceCharge_total.subtract(couponDiscount).max(BigDecimal.ZERO); chargingOrder.setCouponDiscountAmount(couponDiscount.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setWhitelistDiscountAmount(whitelistDiscount.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setWhitelistElectricRefundAmount(electricDiscount.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setWhitelistServiceRefundAmount(serviceDiscount.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setElectrovalence(periodElectricPrice_total.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setServiceCharge(serviceCharge_total.setScale(2, RoundingMode.HALF_EVEN)); refundAmount = refundAmount.max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP); // 白名单全部免费无需实际渠道退款,但财务展示需要将预估预充金额记录到订单退款金额。 BigDecimal recordedRefundAmount = isWhitelistAllFreeOrder(chargingOrder) ? money(chargingOrder.getRechargeAmount()) : refundAmount; Integer payMethod = chargingOrder.getPayMethod(); BigDecimal thirdRefundAmount = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); BigDecimal balanceRefundAmount = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); BigDecimal refundableThirdPayAmount = getRefundableThirdPayAmount(chargingOrder); BigDecimal refundableAmount = refundableThirdPayAmount.add(money(chargingOrder.getBalancePayAmount())).setScale(2, RoundingMode.HALF_UP); // 全部免费订单启动时未拉起支付,渠道可退金额固定为0,避免误触发微信/支付宝/余额退款。 BigDecimal channelRefundAmount = isWhitelistAllFreeOrder(chargingOrder) ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : refundAmount.min(refundableAmount).setScale(2, RoundingMode.HALF_UP); if (Integer.valueOf(1).equals(payMethod)) { balanceRefundAmount = channelRefundAmount.min(money(chargingOrder.getBalancePayAmount())).setScale(2, RoundingMode.HALF_UP); } else if (Integer.valueOf(2).equals(payMethod)) { thirdRefundAmount = channelRefundAmount.min(refundableThirdPayAmount).setScale(2, RoundingMode.HALF_UP); balanceRefundAmount = channelRefundAmount.subtract(thirdRefundAmount).min(money(chargingOrder.getBalancePayAmount())).setScale(2, RoundingMode.HALF_UP); } else { thirdRefundAmount = channelRefundAmount.min(refundableThirdPayAmount).setScale(2, RoundingMode.HALF_UP); } BigDecimal actualThirdPayAmount = refundableThirdPayAmount.subtract(thirdRefundAmount); if (actualThirdPayAmount.compareTo(BigDecimal.ZERO) < 0) { actualThirdPayAmount = BigDecimal.ZERO; } BigDecimal actualBalancePayAmount = money(chargingOrder.getBalancePayAmount()).subtract(balanceRefundAmount); if (actualBalancePayAmount.compareTo(BigDecimal.ZERO) < 0) { actualBalancePayAmount = BigDecimal.ZERO; } refundAmount = recordedRefundAmount; chargingOrder.setBalanceRefundAmount(balanceRefundAmount.setScale(2, RoundingMode.HALF_UP)); chargingOrder.setThirdRefundAmount(thirdRefundAmount.setScale(2, RoundingMode.HALF_UP)); // paymentAmount用于小程序展示实际消费金额,需包含第三方实扣和余额实扣,不能只取第三方渠道金额。 chargingOrder.setPaymentAmount(actualThirdPayAmount.add(actualBalancePayAmount).setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setRefundAmount(refundAmount.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setRefundStatus(channelRefundAmount.compareTo(BigDecimal.ZERO) > 0 ? 1 : 2); UploadRealTimeMonitoringData asc = uploadRealTimeMonitoringDataService.getDataByOrderCode(chargingOrder.getCode(), "asc"); UploadRealTimeMonitoringData desc = uploadRealTimeMonitoringDataService.getDataByOrderCode(chargingOrder.getCode(), "desc"); if (null != asc) { chargingOrder.setStartSoc(new BigDecimal(asc.getSoc())); } if (null != desc) { chargingOrder.setEndSoc(new BigDecimal(desc.getSoc())); } this.updateById(chargingOrder); chargingOrder = this.getById(chargingOrder.getId()); Site site = siteClient.getSiteByIds(Arrays.asList(chargingOrder.getSiteId())).getData().get(0); //推送三方平台 if (2 == chargingOrder.getOrderSource() && 1 == site.getIsOpenSynchronization()) { TChargingOrder finalChargingOrder = chargingOrder; 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(); } ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); TChargingOrder finalChargingOrder1 = chargingOrder; threadPoolExecutor.execute(() -> { //推送成都监管 NotificationConnectorChargeStatusVo statusVo = new NotificationConnectorChargeStatusVo(); statusVo.setOrderCode(finalChargingOrder1.getCode()); statusVo.setChargingGunId(finalChargingOrder1.getChargingGunId()); chengDuJianGuanClient.notificationConnectorChargeStatus(statusVo); chengDuJianGuanClient.notificationOrderInfo(statusVo); }); //开始将优惠券优惠的金额添加到明细中 BigDecimal couponDiscountAmount = chargingOrder.getCouponDiscountAmount(); if (null != couponDiscountAmount && couponDiscountAmount.compareTo(BigDecimal.ZERO) > 0) { List list = chargingOrderAccountingStrategyService.list(new LambdaQueryWrapper().eq(TChargingOrderAccountingStrategy::getChargingOrderId, chargingOrder.getId())); BigDecimal reduce = list.stream().map(TChargingOrderAccountingStrategy::getPeriodServicePrice).reduce(BigDecimal.ZERO, BigDecimal::add); for (TChargingOrderAccountingStrategy chargingOrderAccountingStrategy : list) { 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(list); } // 将枪状态重置为空闲 TChargingGun chargingGun = new TChargingGun(); chargingGun.setId(chargingOrder.getChargingGunId()); chargingGun.setStatus(2); chargingGun.setChargingPower(BigDecimal.ZERO); chargingGunClient.updateChargingGunById(chargingGun); chargingGun = chargingGunClient.getChargingGunById(chargingOrder.getChargingGunId()).getData(); //推送状态给三方平台 if (2 == chargingOrder.getOrderSource() && 1 == site.getIsOpenSynchronization()) { tcecClient.pushChargingGunStatus(chargingGun.getFullNumber(), 2); } wxSubscribeMessageService.sendChargeEnd(chargingOrder, site, chargingGun); //添加积分 if (1 == chargingOrder.getOrderSource()) { TAppUser appUser = appUserClient.getUserById(chargingOrder.getAppUserId()).getData(); sendVipChargeFinishMessage(appUser, chargingOrder, site); TIntegralRule integralRule = integralRuleClient.getSet().getData(); if (null != integralRule) { Integer num1 = JSON.parseObject(integralRule.getChargeCredit()).getInteger("num1"); Integer integral = chargingOrder.getServiceCharge().intValue() * num1; if (null != appUser.getVipId()) { GetAppUserVipDetail getAppUserVipDetail = new GetAppUserVipDetail(); getAppUserVipDetail.setAppUserId(appUser.getId()); getAppUserVipDetail.setVipId(appUser.getVipId()); TAppUserVipDetail vipDetail = appUserVipDetailClient.getAppUserVipDetail(getAppUserVipDetail).getData(); TVip vip = getVipSnapshot(vipDetail, appUser.getVipId()); Integer doubleIntegration = vip == null ? null : vip.getDoubleIntegration(); //双倍积分 if (Integer.valueOf(1).equals(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(chargingOrder.getCode()); appUserIntegralChange.setExtension(chargingOrder.getId().toString()); appUserClient.updateAppUser(appUser); appUserIntegralChangeClient.addAppUserIntegralChange(appUserIntegralChange); } } //计算用户标签 editUserTag(chargingOrder); //用户推荐奖励 referralReward(chargingOrder); //开始构建退款费用 refundAmount = refundAmount.setScale(2, RoundingMode.HALF_EVEN); Integer rechargePaymentType = chargingOrder.getRechargePaymentType(); if (channelRefundAmount.compareTo(BigDecimal.ZERO) > 0) { //构建退款明细 TChargingOrderRefund chargingOrderRefund = new TChargingOrderRefund(); chargingOrderRefund.setChargingOrderId(chargingOrder.getId()); SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMMddHHmmssSSS"); chargingOrderRefund.setRefundCode("CDF" + sdf1.format(new Date()) + (Double.valueOf(Math.random() * 1000).intValue())); chargingOrderRefund.setRefundAmount(refundAmount); chargingOrderRefund.setBalanceRefundAmount(balanceRefundAmount); chargingOrderRefund.setThirdRefundAmount(thirdRefundAmount); chargingOrderRefund.setPayType(rechargePaymentType); chargingOrderRefund.setRefundStatus(1); chargingOrderRefund.setCode(chargingOrder.getCode()); chargingOrderRefund.setRefundTitle("充电完成退款"); chargingOrderRefund.setRefundContent("充电完成退款"); chargingOrderRefund.setRefundReason("充电完成退款"); chargingOrderRefund.setRefundRemark("实际充电消费金额:" + chargingOrder.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) { changeUserBalance(chargingOrder.getAppUserId(), balanceRefundAmount); recordBalanceRefund(chargingOrder, balanceRefundAmount, chargingOrderRefund.getRefundCode()); } 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()); 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(chargingOrder).multiply(new BigDecimal(100)).intValue()); amount.setCurrency("CNY"); model.setAmount(amount); R> result = wxPaymentClient.refundOrderR(model); if (200 == result.getCode()) { //手续费 Map amount1 = (Map) 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.save(chargingOrderRefund); } } if (thirdRefundAmount.compareTo(BigDecimal.ZERO) > 0 && 2 == rechargePaymentType) { RefundReq dto = new RefundReq(); dto.setOutTradeNo(chargingOrder.getCode()); dto.setOutRequestNo(chargingOrderRefund.getRefundCode()); dto.setRefundAmount(thirdRefundAmount.toString()); dto.setRefundReason("充电完成退款"); RefundResp resp = aliPaymentClient.refund(dto).getData(); if (null != resp) { chargingOrderRefundService.save(chargingOrderRefund); this.chargingOrderStartupFailureWxRefund(chargingOrderRefund.getRefundCode(), resp.getTradeNo(), "SUCCESS", null); } } if (thirdRefundAmount.compareTo(BigDecimal.ZERO) > 0 && 3 == rechargePaymentType) { RefundReq dto = new RefundReq(); dto.setOutTradeNo(chargingOrder.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); this.chargingOrderStartupFailureWxRefund(chargingOrderRefund.getRefundCode(), resp.getTradeNo(), "SUCCESS", null); } } } else if (recordedRefundAmount.compareTo(BigDecimal.ZERO) > 0) { TChargingOrderRefund chargingOrderRefund = new TChargingOrderRefund(); chargingOrderRefund.setChargingOrderId(chargingOrder.getId()); SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMMddHHmmssSSS"); chargingOrderRefund.setRefundCode("CDF" + sdf1.format(new Date()) + (Double.valueOf(Math.random() * 1000).intValue())); chargingOrderRefund.setRefundAmount(recordedRefundAmount); chargingOrderRefund.setBalanceRefundAmount(balanceRefundAmount); chargingOrderRefund.setThirdRefundAmount(thirdRefundAmount); chargingOrderRefund.setRefundStatus(2); chargingOrderRefund.setRefundSerialNumber(isWhitelistAllFreeOrder(chargingOrder) ? "WHITELIST" : "BALANCE"); chargingOrderRefund.setPayType(rechargePaymentType); chargingOrderRefund.setCode(chargingOrder.getCode()); chargingOrderRefund.setRefundTitle("充电完成退款"); chargingOrderRefund.setRefundContent("充电完成退款"); chargingOrderRefund.setRefundReason("充电完成退款"); chargingOrderRefund.setRefundRemark(isWhitelistAllFreeOrder(chargingOrder) ? "白名单全部免费,记录预估退款金额" : "实际充电消费金额:" + chargingOrder.getPaymentAmount()); chargingOrderRefund.setRefundTotalAmount(recordedRefundAmount); chargingOrderRefund.setPayAmount(rechargeAmount); chargingOrderRefund.setRefundTime(LocalDateTime.now()); chargingOrderRefundService.save(chargingOrderRefund); chargingOrder.setRefundStatus(2); chargingOrder.setRefundTime(LocalDateTime.now()); this.updateById(chargingOrder); triggerChargingOrderShare(chargingOrder); } else { // 无可退金额时正常结算,不生成金额为 0 的退款记录。 triggerChargingOrderShare(chargingOrder); } } //添加汇总统计数据 List list4 = chargingOrderAccountingStrategyService.list(new QueryWrapper().eq("charging_order_id", chargingOrder.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 (!chargingOrder.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 (chargingOrder.getStartTime() != null && chargingOrder.getEndTime() != null) { LocalDateTime startTime = chargingOrder.getStartTime(); LocalDateTime endTime = chargingOrder.getEndTime(); // 计算时间差 单位秒 time = ChronoUnit.SECONDS.between(startTime, endTime); } TChargingOrderSummaryData summaryData = new TChargingOrderSummaryData(); summaryData.setChargingOrderId(chargingOrder.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); if (channelRefundAmount.compareTo(BigDecimal.ZERO) == 0 && Integer.valueOf(2).equals(chargingOrder.getRefundStatus())) { triggerChargingOrderShare(chargingOrder, site); } //推送监管平台订单状态和订单详情 ChargingOrderVo chargingOrderVo = new ChargingOrderVo(); BeanUtils.copyProperties(chargingOrder, chargingOrderVo); chargingMessageClient.pushOrderInfo(chargingOrderVo); chargingMessageClient.pushOrderStatus(chargingOrderVo); } } catch (Exception e) { log.error("充电订单结算失败,订单号:{}", vo == null ? null : vo.getTransaction_serial_number(), e); return R.fail("充电订单结算失败:" + firstNotBlank(e.getMessage(), "未知异常")); } finally { if (locked && lock.isHeldByCurrentThread()) { lock.unlock(); } } return R.ok(); } @Override public List> getByDate(LocalDate start, LocalDate end, List siteIds) { LocalDateTime start1 = start.atStartOfDay(); LocalDateTime end1 = end.atTime(LocalTime.MAX); return this.baseMapper.getByDate(start1, end1, siteIds); } @Override public List> queryPower(List siteIds) { return this.baseMapper.queryPower(siteIds); } @Override public Map qureryPowerLevel(List siteIds, ChargingStatisticsQueryDto statisticsQueryDto) { return this.baseMapper.queryPowerLevel(siteIds, statisticsQueryDto); } @Override public List> usersDay(ChargingStatisticsQueryDto statisticsQueryDto) { return this.baseMapper.usersDay(statisticsQueryDto); } @Override public List> usersDay1() { return this.baseMapper.usersDay1(); } @Override public List> usersByQuery(ChargingStatisticsQueryDto statisticsQueryDto) { return this.baseMapper.usersByQuery(statisticsQueryDto); } @Override public List> usersByQuery1(ChargingStatisticsQueryDto statisticsQueryDto) { return this.baseMapper.usersByQuery1(statisticsQueryDto); } @Override public List> getUserTagCount() { return this.baseMapper.getUserTagCount(); } @Override public List> getVipCount() { return this.baseMapper.getVipCount(); } @Override public List> unitConsumption(ChargingStatisticsQueryDto statisticsQueryDto) { return this.baseMapper.unitConsumption(statisticsQueryDto); } @Override public List> carUserMethod() { return this.baseMapper.carUserMethod(); } @Override public List> carUserBrand() { return this.baseMapper.carUserBrand(); } @Override public Map countLocalCar() { return this.baseMapper.countLocalCar(); } @Override public Double getAver(List siteIds) { return this.baseMapper.getAver(siteIds); } @Override public List> getLevelEvaluate(List siteIds) { return this.baseMapper.getLevelEvaluate(siteIds); } @Resource private TShoppingOrderService shoppingOrderService; @Resource private TShoppingOrderRefundService shoppingOrderRefundService; @Resource private TVipOrderService vipOrderService; @Resource private TVipOrderRefundService vipOrderRefundService; @Override public R getSpecialRefundOrder(PayOrderRefundDto payOrderQueryDto) { TChargingOrder order = getSpecialRefundChargingOrder(payOrderQueryDto); if (order == null) { return R.fail("未查询到支付成功的充电订单"); } mergeSuccessfulRefundAmounts(order); PayOrderInfoDto dto = new PayOrderInfoDto(); BigDecimal thirdPayAmount = getRefundableThirdPayAmount(order); BigDecimal thirdRefundAmount = money(order.getThirdRefundAmount()); dto.setOrderId(order.getId().toString()); dto.setCode(order.getCode()); dto.setTradeNo(order.getRechargeSerialNumber()); dto.setPayType(order.getRechargePaymentType()); dto.setPayAmount(thirdPayAmount); dto.setPayTime(order.getPayTime()); dto.setRefundAmount(thirdRefundAmount); dto.setRechargeAmount(money(order.getRechargeAmount())); dto.setFinalAmount(thirdPayAmount.subtract(thirdRefundAmount).max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP)); dto.setPayMethod(order.getPayMethod()); dto.setBalancePayAmount(money(order.getBalancePayAmount())); dto.setThirdPayAmount(thirdPayAmount); dto.setBalanceRefundAmount(money(order.getBalanceRefundAmount())); dto.setThirdRefundAmount(thirdRefundAmount); dto.setRefundableAmount(getSpecialRefundableAmount(order)); return R.ok(dto); } @Override public BigDecimal getManualRefundableAmount(Long chargingOrderId) { if (chargingOrderId == null) { return BigDecimal.ZERO; } return getManualRefundableAmount(this.getById(chargingOrderId)); } private BigDecimal getManualRefundableAmount(TChargingOrder order) { if (order == null || order.getId() == null) { return BigDecimal.ZERO; } List successfulRefunds = chargingOrderRefundService.list( new LambdaQueryWrapper() .eq(TChargingOrderRefund::getChargingOrderId, order.getId()) .eq(TChargingOrderRefund::getRefundStatus, 2)); return calculateManualRefundableAmount(order.getPaymentAmount(), successfulRefunds); } static BigDecimal calculateManualRefundableAmount(BigDecimal paymentAmount, List successfulRefunds) { BigDecimal successfulManualRefundAmount = Optional.ofNullable(successfulRefunds) .orElse(Collections.emptyList()) .stream() .filter(refund -> "后台退款".equals(refund.getRefundTitle()) || SPECIAL_REFUND_TITLE.equals(refund.getRefundTitle())) .map(TChargingOrderRefund::getRefundAmount) .map(amount -> amount == null ? BigDecimal.ZERO : amount) .reduce(BigDecimal.ZERO, BigDecimal::add); return (paymentAmount == null ? BigDecimal.ZERO : paymentAmount) .subtract(successfulManualRefundAmount) .max(BigDecimal.ZERO) .setScale(2, RoundingMode.HALF_UP); } private BigDecimal getSpecialRefundableAmount(TChargingOrder order) { return calculateSpecialRefundableAmount( getManualRefundableAmount(order), getRefundableThirdPayAmount(order), money(order == null ? null : order.getThirdRefundAmount())); } static BigDecimal calculateSpecialRefundableAmount(BigDecimal manualRefundableAmount, BigDecimal thirdPayAmount, BigDecimal thirdRefundAmount) { BigDecimal normalizedManualAmount = manualRefundableAmount == null ? BigDecimal.ZERO : manualRefundableAmount; BigDecimal normalizedThirdPayAmount = thirdPayAmount == null ? BigDecimal.ZERO : thirdPayAmount; BigDecimal normalizedThirdRefundAmount = thirdRefundAmount == null ? BigDecimal.ZERO : thirdRefundAmount; BigDecimal remainingThirdAmount = normalizedThirdPayAmount .subtract(normalizedThirdRefundAmount) .max(BigDecimal.ZERO); return normalizedManualAmount .min(remainingThirdAmount) .setScale(2, RoundingMode.HALF_UP); } @Override @Transactional public R specialRefund(PayOrderRefundDto payOrderQueryDto) { R passwordValid = validateOperationPassword(payOrderQueryDto); if (passwordValid.getCode() != 200) { return passwordValid; } if (payOrderQueryDto.getRefundAmount() == null || payOrderQueryDto.getRefundAmount().compareTo(BigDecimal.ZERO) <= 0) { return R.fail("请输入正确的退款金额"); } TChargingOrder order = getSpecialRefundChargingOrder(payOrderQueryDto); if (order == null) { return R.fail("未查询到支付成功的充电订单"); } RLock lock = redissonClient.getLock("SPECIAL_REFUND_ORDER_LOCK:" + order.getId()); if (!lock.tryLock()) { return R.fail("该订单正在退款处理中,请勿重复提交"); } try { order = this.getById(order.getId()); if (order == null) { return R.fail("充电订单不存在"); } mergeSuccessfulRefundAmounts(order); BigDecimal refundAmount = money(payOrderQueryDto.getRefundAmount()); BigDecimal refundableAmount = getSpecialRefundableAmount(order); if (refundAmount.compareTo(refundableAmount) > 0) { return R.fail("退款金额不能超过剩余可退金额"); } BigDecimal refundedThirdAmount = money(order.getThirdRefundAmount()); BigDecimal refundableThirdAmount = getRefundableThirdPayAmount(order).subtract(refundedThirdAmount); if (refundableThirdAmount.compareTo(BigDecimal.ZERO) < 0) { refundableThirdAmount = BigDecimal.ZERO; } BigDecimal thirdRefundAmount = isBalancePayMode(order.getPayMethod()) ? refundAmount.min(refundableThirdAmount).setScale(2, RoundingMode.HALF_UP) : refundAmount; BigDecimal balanceRefundAmount = refundAmount.subtract(thirdRefundAmount).setScale(2, RoundingMode.HALF_UP); if (balanceRefundAmount.compareTo(BigDecimal.ZERO) < 0) { balanceRefundAmount = BigDecimal.ZERO; } TChargingOrderRefund refund = buildSpecialChargingRefund(order, payOrderQueryDto, refundAmount, balanceRefundAmount, thirdRefundAmount); if (thirdRefundAmount.compareTo(BigDecimal.ZERO) == 0) { refund.setRefundSerialNumber("BALANCE"); finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount); return R.ok(); } Integer payType = order.getRechargePaymentType(); boolean success = false; if (Integer.valueOf(1).equals(payType)) { R shareRefund = refundIcbcShareBeforeMainRefund(order, refund, thirdRefundAmount); if (shareRefund.getCode() != 200) { return shareRefund; } success = refundByIcbcAndFinish(order, refund, thirdRefundAmount, SPECIAL_REFUND_TITLE, true, refundAmount, balanceRefundAmount); } else if (Integer.valueOf(2).equals(payType)) { RefundReq dto = new RefundReq(); dto.setOutTradeNo(order.getCode()); dto.setOutRequestNo(refund.getRefundCode()); dto.setRefundAmount(thirdRefundAmount.toString()); dto.setRefundReason(SPECIAL_REFUND_TITLE); RefundResp resp = aliPaymentClient.refund(dto).getData(); if (resp != null) { refund.setRefundSerialNumber(resp.getTradeNo()); refund.setRefundTime(LocalDateTime.now()); finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount); success = true; } } else if (Integer.valueOf(3).equals(payType)) { RefundReq dto = new RefundReq(); dto.setOutTradeNo(order.getRechargeSerialNumber()); dto.setOutRequestNo(refund.getRefundCode()); dto.setRefundAmount(thirdRefundAmount.toString()); dto.setRefundReason(SPECIAL_REFUND_TITLE); RefundResp resp = h5AliPaymentClient.refund(dto).getData(); if (resp != null && "10000".equals(resp.getCode())) { refund.setRefundSerialNumber(resp.getTradeNo()); refund.setRefundTime(LocalDateTime.now()); finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount); success = true; } } return success ? R.ok() : R.fail("退款失败,请检查支付渠道或稍后重试"); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } private void mergeSuccessfulRefundAmounts(TChargingOrder order) { if (order == null || order.getId() == null) { return; } List refunds = chargingOrderRefundService.list( new LambdaQueryWrapper() .eq(TChargingOrderRefund::getChargingOrderId, order.getId()) .eq(TChargingOrderRefund::getRefundStatus, 2)); if (refunds == null || refunds.isEmpty()) { return; } BigDecimal successfulAmount = BigDecimal.ZERO; BigDecimal explicitBalanceAmount = BigDecimal.ZERO; BigDecimal explicitThirdAmount = BigDecimal.ZERO; BigDecimal unspecifiedAmount = BigDecimal.ZERO; for (TChargingOrderRefund refund : refunds) { BigDecimal amount = money(refund.getRefundAmount()); BigDecimal balanceAmount = money(refund.getBalanceRefundAmount()); BigDecimal thirdAmount = money(refund.getThirdRefundAmount()); successfulAmount = successfulAmount.add(amount); explicitBalanceAmount = explicitBalanceAmount.add(balanceAmount); explicitThirdAmount = explicitThirdAmount.add(thirdAmount); if (balanceAmount.add(thirdAmount).compareTo(BigDecimal.ZERO) == 0) { unspecifiedAmount = unspecifiedAmount.add(amount); } } BigDecimal inferredThirdAmount = unspecifiedAmount.min( getRefundableThirdPayAmount(order).subtract(explicitThirdAmount).max(BigDecimal.ZERO)); BigDecimal inferredBalanceAmount = unspecifiedAmount.subtract(inferredThirdAmount).max(BigDecimal.ZERO); order.setRefundAmount(money(order.getRefundAmount()).max(money(successfulAmount))); order.setThirdRefundAmount(money(order.getThirdRefundAmount()).max(money(explicitThirdAmount.add(inferredThirdAmount)))); order.setBalanceRefundAmount(money(order.getBalanceRefundAmount()).max(money(explicitBalanceAmount.add(inferredBalanceAmount)))); } private R refundIcbcShareBeforeMainRefund(TChargingOrder order, TChargingOrderRefund refund, BigDecimal thirdRefundAmount) { if (order == null || refund == null || thirdRefundAmount == null || thirdRefundAmount.compareTo(BigDecimal.ZERO) <= 0) { return R.ok(); } if (!"ICBC".equals(order.getSourceName())) { return R.ok(); } List shareRecords = chargingOrderShareRecordMapper.selectList( new LambdaQueryWrapper() .eq(TChargingOrderShareRecord::getChargingOrderId, order.getId()) .eq(TChargingOrderShareRecord::getPayChannel, "ICBC") .eq(TChargingOrderShareRecord::getNeedOnlineShare, true) .eq(TChargingOrderShareRecord::getOnlineShareStatus, "SUCCESS") .eq(TChargingOrderShareRecord::getDelFlag, false) .orderByAsc(TChargingOrderShareRecord::getId)); if (shareRecords == null || shareRecords.isEmpty()) { return R.ok(); } BigDecimal thirdPayAmount = getRefundableThirdPayAmount(order); if (thirdPayAmount.compareTo(BigDecimal.ZERO) <= 0) { return R.fail("订单已分账但第三方支付金额异常,禁止退款"); } BigDecimal cumulativeThirdRefund = money(order.getThirdRefundAmount()).add(thirdRefundAmount).min(thirdPayAmount).setScale(2, RoundingMode.HALF_UP); for (TChargingOrderShareRecord shareRecord : shareRecords) { BigDecimal shareAmount = money(shareRecord.getShareAmount()); if (shareAmount.compareTo(BigDecimal.ZERO) <= 0) { continue; } BigDecimal targetRefundAmount = shareAmount.multiply(cumulativeThirdRefund).divide(thirdPayAmount, 2, RoundingMode.HALF_UP).min(shareAmount).setScale(2, RoundingMode.HALF_UP); BigDecimal refundedShareAmount; try { refundedShareAmount = queryIcbcShareRefundAmount(order, shareRecord); } catch (ServiceException e) { return R.fail(e.getMessage()); } BigDecimal shareRefundAmount = targetRefundAmount.subtract(refundedShareAmount).setScale(2, RoundingMode.HALF_UP); if (shareRefundAmount.compareTo(BigDecimal.ZERO) <= 0) { continue; } R result = refundIcbcShareRecord(order, shareRecord, refund, shareRefundAmount); if (result.getCode() != 200) { return result; } } return R.ok(); } private BigDecimal queryIcbcShareRefundAmount(TChargingOrder order, TChargingOrderShareRecord shareRecord) { try { IcbcSubOrderQueryReq queryReq = IcbcAdminFundOperationSupport.buildSubOrderQueryRequest(order, shareRecord); R queryResult = icbcPaymentClient.querySubOrder(queryReq); IcbcSubOrderQueryResp queryResp = queryResult == null ? null : queryResult.getData(); if (queryResult == null || queryResult.getCode() != 200 || queryResp == null || !"SUCCESS".equals(queryResp.getStatus())) { throw new ServiceException(queryResult == null ? "工行子订单查询无响应" : firstNotBlank(queryResult.getMsg(), "工行子订单查询失败")); } return parseIcbcAmount(queryResp.getOrderRefundAmnt()); } catch (IllegalArgumentException e) { throw new ServiceException(e.getMessage()); } catch (ServiceException e) { throw e; } catch (Exception e) { throw new ServiceException("工行子订单查询异常:" + firstNotBlank(e.getMessage(), "未知异常")); } } private R refundIcbcShareRecord(TChargingOrder order, TChargingOrderShareRecord shareRecord, TChargingOrderRefund refund, BigDecimal shareRefundAmount) { String suffix = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")); String retSubOrderId = ("SPSUB" + refund.getRefundCode() + shareRecord.getId()).substring(0, Math.min(50, ("SPSUB" + refund.getRefundCode() + shareRecord.getId()).length())); String outtrxSerialNo = ("SPSUBREF" + suffix + shareRecord.getId()).substring(0, Math.min(50, ("SPSUBREF" + suffix + shareRecord.getId()).length())); try { IcbcSubOrderRefundReq refundReq = IcbcAdminFundOperationSupport.buildSubOrderRefundRequest( order, shareRecord, shareRefundAmount, retSubOrderId, outtrxSerialNo); refundReq.setMerSpInfor("特殊退款分账退回:" + order.getCode()); R refundResult = icbcPaymentClient.refundSubOrder(refundReq); IcbcSubOrderRefundResp refundResp = refundResult == null ? null : refundResult.getData(); if (refundResult == null || refundResult.getCode() != 200 || refundResp == null || !"SUCCESS".equals(refundResp.getStatus())) { String message = refundResult == null ? "工行分账退款无响应" : firstNotBlank(refundResult.getMsg(), refundResp == null ? null : refundResp.getRawResponse(), "工行分账退款失败"); log.error("特殊退款分账退回失败,订单号:{},退款单号:{},分账记录:{},金额:{},原因:{}", order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount, message); return R.fail("分账退回失败:" + message); } log.info("特殊退款分账退回成功,订单号:{},退款单号:{},分账记录:{},金额:{}", order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount); return R.ok(); } catch (IllegalArgumentException | ServiceException e) { log.error("特殊退款分账退回失败,订单号:{},退款单号:{},分账记录:{},金额:{}", order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount, e); return R.fail("分账退回失败:" + e.getMessage()); } catch (Exception e) { log.error("特殊退款分账退回异常,订单号:{},退款单号:{},分账记录:{},金额:{}", order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount, e); return R.fail("分账退回异常:" + firstNotBlank(e.getMessage(), "未知异常")); } } private BigDecimal parseIcbcAmount(String amount) { if (!StringUtils.hasLength(amount)) { return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); } String value = amount.trim(); try { if (value.contains(".")) { return money(new BigDecimal(value)); } return new BigDecimal(value).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP); } catch (NumberFormatException e) { throw new ServiceException("工行金额格式异常:" + amount); } } private R validateOperationPassword(PayOrderRefundDto payOrderQueryDto) { if (payOrderQueryDto == null || StringUtils.isEmpty(payOrderQueryDto.getOperationPassword())) { return R.fail("请输入操作密码"); } SysUser sysUser = sysUserClient.getSysUser(tokenService.getLoginUser().getUserid()).getData(); if (sysUser == null || StringUtils.isEmpty(sysUser.getOperationPassword())) { return R.fail("当前账号未配置操作密码"); } if (!SecurityUtils.matchesPassword(payOrderQueryDto.getOperationPassword(), sysUser.getOperationPassword())) { return R.fail("操作密码错误"); } return R.ok(); } private TChargingOrder getSpecialRefundChargingOrder(PayOrderRefundDto payOrderQueryDto) { if (payOrderQueryDto == null) { return null; } LambdaQueryWrapper wrapper = new LambdaQueryWrapper() .eq(TChargingOrder::getRechargePaymentStatus, 2) .eq(TChargingOrder::getDelFlag, false); if (payOrderQueryDto.getOrderId() != null) { wrapper.eq(TChargingOrder::getId, payOrderQueryDto.getOrderId()); } else if (StringUtils.hasLength(payOrderQueryDto.getCode())) { wrapper.eq(TChargingOrder::getCode, payOrderQueryDto.getCode()); } else if (StringUtils.hasLength(payOrderQueryDto.getSerialNumber())) { wrapper.eq(TChargingOrder::getRechargeSerialNumber, payOrderQueryDto.getSerialNumber()); } else { return null; } return this.getOne(wrapper.last("limit 1")); } private TChargingOrderRefund buildSpecialChargingRefund(TChargingOrder order, PayOrderRefundDto dto, BigDecimal refundAmount, BigDecimal balanceRefundAmount, BigDecimal thirdRefundAmount) { TChargingOrderRefund refund = new TChargingOrderRefund(); refund.setChargingOrderId(order.getId()); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); refund.setRefundCode("TSF" + sdf.format(new Date()) + (Double.valueOf(Math.random() * 1000).intValue())); refund.setRefundAmount(refundAmount); refund.setBalanceRefundAmount(balanceRefundAmount); refund.setThirdRefundAmount(thirdRefundAmount); refund.setRefundStatus(1); refund.setPayType(order.getRechargePaymentType()); refund.setRefundTime(LocalDateTime.now()); refund.setCode(order.getCode()); refund.setRefundTitle(SPECIAL_REFUND_TITLE); refund.setRefundContent(SPECIAL_REFUND_TITLE); refund.setRefundReason(StringUtils.hasLength(dto.getReason()) ? dto.getReason() : SPECIAL_REFUND_TITLE); refund.setRefundRemark(dto.getRemark()); refund.setRefundTotalAmount(money(order.getRefundAmount()).add(refundAmount)); refund.setPayAmount(money(order.getPaymentAmount())); return refund; } @Override public R payRefund(PayOrderRefundDto payOrderQueryDto) { R passwordValid = validateOperationPassword(payOrderQueryDto); if (passwordValid.getCode() != 200) { return passwordValid; } if (payOrderQueryDto.getRefundAmount() == null || payOrderQueryDto.getRefundAmount().compareTo(BigDecimal.ZERO) <= 0) { return R.fail("退款金额必须大于0"); } if (payOrderQueryDto.getType() == 1) { TChargingOrder tChargingOrder = this.baseMapper.selectById(payOrderQueryDto.getOrderId()); if (tChargingOrder == null) { return R.fail("充电订单不存在"); } BigDecimal manualRefundAmount = money(payOrderQueryDto.getRefundAmount()); BigDecimal remainingRefundAmount = getManualRefundableAmount(tChargingOrder); if (manualRefundAmount.compareTo(remainingRefundAmount) > 0) { return R.fail("退款金额不能超过剩余可退金额" + remainingRefundAmount.toPlainString() + "元"); } if (manualRefundAmount.compareTo(remainingRefundAmount) == 0) { tChargingOrder.setStatus(5); } TChargingOrderRefund chargingOrderRefund = new TChargingOrderRefund(); chargingOrderRefund.setChargingOrderId(tChargingOrder.getId()); chargingOrderRefund.setRefundAmount(manualRefundAmount); chargingOrderRefund.setRefundStatus(1); chargingOrderRefund.setPayType(tChargingOrder.getRechargePaymentType()); chargingOrderRefund.setRefundTime(LocalDateTime.now()); chargingOrderRefund.setCode(tChargingOrder.getCode()); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); chargingOrderRefund.setRefundCode("CDF" + sdf.format(new Date()) + (Double.valueOf(Math.random() * 1000).intValue())); chargingOrderRefund.setRefundTitle("后台退款"); chargingOrderRefund.setRefundContent("后台退款"); chargingOrderRefund.setRefundReason("后台退款"); chargingOrderRefund.setRefundRemark(payOrderQueryDto.getRemark()); chargingOrderRefund.setRefundTotalAmount(money(tChargingOrder.getRefundAmount()).add(manualRefundAmount)); chargingOrderRefund.setPayAmount(money(tChargingOrder.getPaymentAmount())); BigDecimal refundedThirdAmount = money(tChargingOrder.getThirdRefundAmount()); BigDecimal refundableThirdAmount = getRefundableThirdPayAmount(tChargingOrder).subtract(refundedThirdAmount); if (refundableThirdAmount.compareTo(BigDecimal.ZERO) < 0) { refundableThirdAmount = BigDecimal.ZERO; } BigDecimal thirdRefundAmount = isBalancePayMode(tChargingOrder.getPayMethod()) ? manualRefundAmount.min(refundableThirdAmount).setScale(2, RoundingMode.HALF_UP) : manualRefundAmount; BigDecimal balanceRefundAmount = manualRefundAmount.subtract(thirdRefundAmount).setScale(2, RoundingMode.HALF_UP); if (balanceRefundAmount.compareTo(BigDecimal.ZERO) < 0) { balanceRefundAmount = BigDecimal.ZERO; } chargingOrderRefund.setBalanceRefundAmount(balanceRefundAmount); chargingOrderRefund.setThirdRefundAmount(thirdRefundAmount); if (thirdRefundAmount.compareTo(BigDecimal.ZERO) == 0) { chargingOrderRefund.setRefundSerialNumber("BALANCE"); finishManualChargingRefund(tChargingOrder, chargingOrderRefund, manualRefundAmount, balanceRefundAmount, thirdRefundAmount); } else if (isIcbcOrder(tChargingOrder)) { R shareRefund = refundIcbcShareBeforeMainRefund(tChargingOrder, chargingOrderRefund, thirdRefundAmount); if (shareRefund.getCode() != 200) { return shareRefund; } refundByIcbcAndFinish(tChargingOrder, chargingOrderRefund, thirdRefundAmount, "取消订单", true, manualRefundAmount, balanceRefundAmount); } else if (1 == tChargingOrder.getRechargePaymentType()) { WxPaymentRefundModel model = new WxPaymentRefundModel(); model.setOut_trade_no(tChargingOrder.getCode()); model.setOut_refund_no(chargingOrderRefund.getRefundCode()); model.setTransaction_id(tChargingOrder.getRechargeSerialNumber()); 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(tChargingOrder).multiply(new BigDecimal(100)).intValue()); amount.setCurrency("CNY"); model.setAmount(amount); R> orderR = wxPaymentClient.refundOrderR(model); if (orderR == null || orderR.getCode() != 200) { return R.fail(orderR == null ? "微信退款服务无响应" : firstNotBlank(orderR.getMsg(), "微信退款失败")); } finishManualChargingRefund(tChargingOrder, chargingOrderRefund, manualRefundAmount, balanceRefundAmount, thirdRefundAmount); //手续费 if (orderR.getData() != null && orderR.getData().get("amount") instanceof Map) { Map amount1 = (Map) orderR.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 == tChargingOrder.getRechargePaymentType()) { RefundReq dto = new RefundReq(); dto.setOutTradeNo(tChargingOrder.getCode()); dto.setOutRequestNo(tChargingOrder.getCode()); dto.setRefundAmount(thirdRefundAmount.toString()); dto.setRefundReason("取消订单"); R refundResult = aliPaymentClient.refund(dto); RefundResp resp = refundResult == null ? null : refundResult.getData(); if (refundResult == null || refundResult.getCode() != 200 || resp == null || !"10000".equals(resp.getCode())) { return R.fail(refundResult == null ? "支付宝退款服务无响应" : firstNotBlank(resp == null ? null : resp.getMsg(), refundResult.getMsg(), "支付宝退款失败")); } finishManualChargingRefund(tChargingOrder, chargingOrderRefund, manualRefundAmount, balanceRefundAmount, thirdRefundAmount); } else if (3 == tChargingOrder.getRechargePaymentType()) { RefundReq dto = new RefundReq(); dto.setOutTradeNo(tChargingOrder.getRechargeSerialNumber()); dto.setOutRequestNo(tChargingOrder.getCode()); dto.setRefundAmount(thirdRefundAmount.toString()); dto.setRefundReason("取消订单"); R refundResult = h5AliPaymentClient.refund(dto); RefundResp resp = refundResult == null ? null : refundResult.getData(); if (refundResult == null || refundResult.getCode() != 200 || resp == null || !"10000".equals(resp.getCode())) { return R.fail(refundResult == null ? "支付宝H5退款服务无响应" : firstNotBlank(refundResult.getMsg(), "支付宝H5退款失败")); } finishManualChargingRefund(tChargingOrder, chargingOrderRefund, manualRefundAmount, balanceRefundAmount, thirdRefundAmount); } else { return R.fail("不支持的退款支付方式"); } if (5 == tChargingOrder.getStatus()) { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); threadPoolExecutor.execute(() -> { //推送成都监管 NotificationConnectorChargeStatusVo statusVo = new NotificationConnectorChargeStatusVo(); statusVo.setOrderCode(tChargingOrder.getCode()); statusVo.setChargingGunId(tChargingOrder.getChargingGunId()); chengDuJianGuanClient.notificationConnectorChargeStatus(statusVo); chengDuJianGuanClient.notificationOrderInfo(statusVo); }); } return R.ok(); } if (payOrderQueryDto.getType() == 2) { TShoppingOrder shoppingOrder = shoppingOrderService.getById(payOrderQueryDto.getOrderId()); if (shoppingOrder.getPaymentAmount().compareTo(payOrderQueryDto.getRefundAmount()) == -1) { return R.fail("退款金额需小于支付金额"); } if ((shoppingOrder.getPaymentAmount().subtract(shoppingOrder.getRefundAmount()).compareTo(payOrderQueryDto.getRefundAmount())) == 0) { shoppingOrder.setStatus(5); } TShoppingOrderRefund shoppingOrderRefund = new TShoppingOrderRefund(); shoppingOrderRefund.setShoppingOrderId(shoppingOrder.getId()); shoppingOrderRefund.setRefundAmount(payOrderQueryDto.getRefundAmount()); shoppingOrderRefund.setRefundStatus(1); shoppingOrderRefund.setPayType(shoppingOrder.getPaymentType()); shoppingOrderRefund.setRefundTime(LocalDateTime.now()); shoppingOrderRefund.setCode(shoppingOrder.getCode()); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); shoppingOrderRefund.setRefundCode("GWF" + sdf.format(new Date()) + (Double.valueOf(Math.random() * 1000).intValue())); shoppingOrderRefund.setRefundTitle("后台退款"); shoppingOrderRefund.setRefundContent("后台退款"); shoppingOrderRefund.setRefundReason("后台退款"); shoppingOrderRefund.setRefundRemark(payOrderQueryDto.getRemark()); shoppingOrderRefund.setRefundTotalAmount(money(shoppingOrder.getRefundAmount()).add(payOrderQueryDto.getRefundAmount())); shoppingOrderRefund.setPayAmount(shoppingOrder.getPaymentAmount()); if (1 == shoppingOrder.getPaymentType()) { WxPaymentRefundModel model = new WxPaymentRefundModel(); model.setOut_trade_no(shoppingOrder.getCode()); model.setOut_refund_no(shoppingOrderRefund.getRefundCode()); model.setTransaction_id(shoppingOrder.getSerialNumber()); model.setReason("取消订单"); model.setNotify_url("/payment/wx/refund/notify"); WxPaymentRefundModel.RefundAmount amount = new WxPaymentRefundModel.RefundAmount(); amount.setRefund(payOrderQueryDto.getRefundAmount().multiply(new BigDecimal(100)).intValue()); amount.setTotal(shoppingOrder.getPaymentAmount().multiply(new BigDecimal(100)).intValue()); amount.setCurrency("CNY"); model.setAmount(amount); R> orderR = wxPaymentClient.refundOrderR(model); if (200 == orderR.getCode()) { shoppingOrder.setRefundStatus(2); shoppingOrder.setRefundAmount((shoppingOrder.getRefundAmount() == null ? BigDecimal.valueOf(0) : shoppingOrder.getRefundAmount()).add(payOrderQueryDto.getRefundAmount())); shoppingOrderService.updateById(shoppingOrder); shoppingOrderRefundService.save(shoppingOrderRefund); //手续费 Map amount1 = (Map) orderR.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)); shoppingOrderRefund.setRefundFee(refund_fee); shoppingOrderRefundService.updateById(shoppingOrderRefund); } } if (2 == shoppingOrder.getPaymentType()) { RefundReq dto = new RefundReq(); dto.setOutTradeNo(shoppingOrder.getCode()); dto.setOutRequestNo(shoppingOrder.getCode()); dto.setRefundAmount(payOrderQueryDto.getRefundAmount().toString()); dto.setRefundReason("取消订单"); RefundResp resp = aliPaymentClient.refund(dto).getData(); if (null != resp) { shoppingOrder.setRefundStatus(2); shoppingOrder.setRefundAmount((shoppingOrder.getRefundAmount() == null ? BigDecimal.valueOf(0) : shoppingOrder.getRefundAmount()).add(payOrderQueryDto.getRefundAmount())); shoppingOrderService.updateById(shoppingOrder); shoppingOrderRefundService.save(shoppingOrderRefund); } } if (3 == shoppingOrder.getPaymentType()) { IcbcRefundReq refundReq = new IcbcRefundReq(); refundReq.setOutTradeNo(shoppingOrder.getCode()); refundReq.setOrderId(shoppingOrder.getSerialNumber()); refundReq.setOutRefundNo(shoppingOrderRefund.getRefundCode()); refundReq.setRefundAmount(payOrderQueryDto.getRefundAmount()); refundReq.setTotalAmount(shoppingOrder.getPaymentAmount()); refundReq.setReason("后台商城订单退款"); R refundResult = icbcPaymentClient.refund(refundReq); IcbcRefundResp refundResp = refundResult == null ? null : refundResult.getData(); if (refundResult == null || refundResult.getCode() != 200 || refundResp == null || !"SUCCESS".equals(refundResp.getStatus())) { return R.fail(refundResult == null ? "工行退款服务无响应" : firstNotBlank(refundResp == null ? null : refundResp.getRawResponse(), refundResult.getMsg(), "工行退款失败")); } shoppingOrder.setRefundStatus(2); shoppingOrder.setRefundAmount(money(shoppingOrder.getRefundAmount()).add(payOrderQueryDto.getRefundAmount())); shoppingOrder.setRefundSerialNumber(refundResp.getRefundId()); shoppingOrder.setRefundTime(LocalDateTime.now()); shoppingOrderService.updateById(shoppingOrder); shoppingOrderRefund.setRefundStatus(2); shoppingOrderRefund.setRefundSerialNumber(refundResp.getRefundId()); shoppingOrderRefundService.save(shoppingOrderRefund); } // if(3 == tChargingOrder.getPaymentType()){ // RefundReq dto = new RefundReq(); // dto.setOutTradeNo(tChargingOrder.getCode()); // dto.setOutRequestNo(tChargingOrder.getCode()); // dto.setRefundAmount(payOrderQueryDto.getRefundAmount().toString()); // dto.setRefundReason("取消订单"); // RefundResp resp = h5AliPaymentClient.refund(dto).getData(); // if(null != resp){ // tChargingOrder.setRefundStatus(2); // tChargingOrder.setRefundAmount((tChargingOrder.getRefundAmount()==null? BigDecimal.valueOf(0) :tChargingOrder.getRefundAmount()).add(payOrderQueryDto.getRefundAmount())); // shoppingOrderService.updateById(tChargingOrder); // shoppingOrderRefundService.save(chargingOrderRefund); // } // } } if (payOrderQueryDto.getType() == 3) { TVipOrder vipOrder = vipOrderService.getById(payOrderQueryDto.getOrderId()); if (vipOrder.getPaymentAmount().compareTo(payOrderQueryDto.getRefundAmount()) == -1) { return R.fail("退款金额需小于支付金额"); } TVipOrderRefund vipOrderRefund = new TVipOrderRefund(); vipOrderRefund.setVipOrderId(vipOrder.getId()); vipOrderRefund.setRefundAmount(payOrderQueryDto.getRefundAmount()); vipOrderRefund.setRefundStatus(1); vipOrderRefund.setPayTime(vipOrder.getPayTime()); vipOrderRefund.setPayCode(vipOrder.getCode()); vipOrderRefund.setPayType(vipOrder.getPaymentType()); vipOrderRefund.setRefundTime(LocalDateTime.now()); vipOrderRefund.setCode(vipOrder.getCode()); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); vipOrderRefund.setRefundCode("HYF" + sdf.format(new Date()) + (Double.valueOf(Math.random() * 1000).intValue())); vipOrderRefund.setRefundTitle("后台退款"); vipOrderRefund.setRefundContent("后台退款"); vipOrderRefund.setRefundReason("后台退款"); vipOrderRefund.setRefundRemark(payOrderQueryDto.getRemark()); vipOrderRefund.setRefundTotalAmount(money(vipOrder.getRefundAmount()).add(payOrderQueryDto.getRefundAmount())); vipOrderRefund.setPayAmount(vipOrder.getPaymentAmount()); if (1 == vipOrder.getPaymentType()) { WxPaymentRefundModel model = new WxPaymentRefundModel(); model.setOut_trade_no(vipOrder.getCode()); model.setOut_refund_no(vipOrderRefund.getRefundCode()); model.setTransaction_id(vipOrder.getSerialNumber()); model.setReason("取消订单"); model.setNotify_url("/payment/wx/refund/notify"); WxPaymentRefundModel.RefundAmount amount = new WxPaymentRefundModel.RefundAmount(); amount.setRefund(payOrderQueryDto.getRefundAmount().multiply(new BigDecimal(100)).intValue()); amount.setTotal(vipOrder.getPaymentAmount().multiply(new BigDecimal(100)).intValue()); amount.setCurrency("CNY"); model.setAmount(amount); R> orderR = wxPaymentClient.refundOrderR(model); if (200 == orderR.getCode()) { vipOrder.setRefundStatus(2); vipOrder.setRefundAmount((vipOrder.getRefundAmount() == null ? BigDecimal.valueOf(0) : vipOrder.getRefundAmount()).add(payOrderQueryDto.getRefundAmount())); vipOrderService.updateById(vipOrder); vipOrderRefundService.save(vipOrderRefund); //手续费 Map amount1 = (Map) orderR.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)); vipOrderRefund.setRefundFee(refund_fee); vipOrderRefundService.updateById(vipOrderRefund); } } if (2 == vipOrder.getPaymentType()) { RefundReq dto = new RefundReq(); dto.setOutTradeNo(vipOrder.getCode()); dto.setOutRequestNo(vipOrder.getCode()); dto.setRefundAmount(payOrderQueryDto.getRefundAmount().toString()); dto.setRefundReason("取消订单"); RefundResp resp = aliPaymentClient.refund(dto).getData(); if (null != resp) { vipOrder.setRefundStatus(2); vipOrder.setRefundAmount((vipOrder.getRefundAmount() == null ? BigDecimal.valueOf(0) : vipOrder.getRefundAmount()).add(payOrderQueryDto.getRefundAmount())); vipOrderService.updateById(vipOrder); vipOrderRefundService.save(vipOrderRefund); } } if (3 == vipOrder.getPaymentType()) { IcbcRefundReq refundReq = new IcbcRefundReq(); refundReq.setOutTradeNo(vipOrder.getCode()); refundReq.setOrderId(vipOrder.getSerialNumber()); refundReq.setOutRefundNo(vipOrderRefund.getRefundCode()); refundReq.setRefundAmount(payOrderQueryDto.getRefundAmount()); refundReq.setTotalAmount(vipOrder.getPaymentAmount()); refundReq.setReason("后台会员订单退款"); R refundResult = icbcPaymentClient.refund(refundReq); IcbcRefundResp refundResp = refundResult == null ? null : refundResult.getData(); if (refundResult == null || refundResult.getCode() != 200 || refundResp == null || !"SUCCESS".equals(refundResp.getStatus())) { return R.fail(refundResult == null ? "工行退款服务无响应" : firstNotBlank(refundResp == null ? null : refundResp.getRawResponse(), refundResult.getMsg(), "工行退款失败")); } vipOrder.setRefundStatus(2); vipOrder.setRefundAmount(money(vipOrder.getRefundAmount()).add(payOrderQueryDto.getRefundAmount())); vipOrderService.updateById(vipOrder); vipOrderRefund.setRefundStatus(2); vipOrderRefund.setRefundSerialNumber(refundResp.getRefundId()); vipOrderRefundService.save(vipOrderRefund); } // if(3 == tChargingOrder.getPaymentType()){ // RefundReq dto = new RefundReq(); // dto.setOutTradeNo(tChargingOrder.getCode()); // dto.setOutRequestNo(tChargingOrder.getCode()); // dto.setRefundAmount(payOrderQueryDto.getRefundAmount().toString()); // dto.setRefundReason("取消订单"); // RefundResp resp = h5AliPaymentClient.refund(dto).getData(); // if(null != resp){ // tChargingOrder.setRefundStatus(2); // tChargingOrder.setRefundAmount((tChargingOrder.getRefundAmount()==null? BigDecimal.valueOf(0) :tChargingOrder.getRefundAmount()).add(payOrderQueryDto.getRefundAmount())); // vipOrderService.updateById(tChargingOrder); // vipOrderRefundService.save(chargingOrderRefund); // } // } } return R.ok(); } /** * 根据车牌号和开始时间查询充电数据 * * @param query * @return */ @Override public TChargingOrder getChargingOrderByLicensePlate(GetChargingOrderByLicensePlate query) { TChargingOrder one = this.getOne(new LambdaQueryWrapper().eq(TChargingOrder::getPlateNum, query.getLicensePlate()) .eq(TChargingOrder::getDelFlag, 0).between(TChargingOrder::getStartTime, query.getStartTime(), LocalDateTime.now()) .eq(TChargingOrder::getRechargePaymentStatus, 2).in(TChargingOrder::getStatus, Arrays.asList(3, 4, 5)) .last(" order by start_time desc limit 0, 1")); return one; } @Override public TSettlementConfirm settlementAdd(SettlementConfirmAdd dto) { List data = siteClient.getSiteByIds(Arrays.asList(dto.getSiteId())).getData(); List list3 = chargingOrderAccountingStrategyService.list(); TSettlementConfirm tSettlementConfirm = new TSettlementConfirm(); if (!data.isEmpty()) { tSettlementConfirm.setSiteName(data.get(0).getName()); tSettlementConfirm.setPartnerId(data.get(0).getPartnerId()); if (data.get(0).getPartnerId() != null) { Partner data1 = siteClient.getPartnerR(data.get(0).getPartnerId()).getData(); if (data1 != null) { tSettlementConfirm.setPartnerName(data1.getName()); tSettlementConfirm.setPartnerId(data1.getId()); } } } String startTime = dto.getStartTime(); String endTime = dto.getEndTime(); // 将这两个时间转化为localDateTime // 创建 DateTimeFormatter 对象,指定格式 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"); // 将字符串解析为 LocalDateTime 对象 LocalDateTime localDateTime = LocalDateTime.parse(startTime, formatter); LocalDateTime localDateTime1 = LocalDateTime.parse(endTime, formatter); tSettlementConfirm.setEndTime(localDateTime1); tSettlementConfirm.setStartTime(localDateTime); // 根据站点id和开始时间和结束时间和归属日期 查询充电订单列表 QueryWrapper eq = new QueryWrapper() .eq("site_id", dto.getSiteId()); switch (dto.getType()) { case 1: eq.between("pay_time", localDateTime, localDateTime1); break; case 2: eq.between("end_time", localDateTime, localDateTime1); break; } List tChargingOrders = this.baseMapper.selectList(eq); // 充电电量 度 BigDecimal chargingElectronic = new BigDecimal("0"); // 三方收费 分佣 BigDecimal sharingAmount = new BigDecimal("0"); // 三方交易手续费 三方收费*0.6% BigDecimal commissionAmount = new BigDecimal("0"); // 累计电费 BigDecimal electrovalence = new BigDecimal("0"); // 累计服务费 BigDecimal serviceCharge = new BigDecimal("0"); // 订单手续费 订单支付金额*0.6% BigDecimal orderCommission = new BigDecimal("0"); // 会员抵扣 BigDecimal vipDiscount = new BigDecimal("0"); // 优惠券抵扣 BigDecimal couponDiscount = new BigDecimal("0"); // 支付订单金额 需要减去退款金额 BigDecimal paymentAmount = new BigDecimal("0"); for (TChargingOrder tChargingOrder : tChargingOrders) { if (null == tChargingOrder.getStatus() || 5 != tChargingOrder.getStatus()) { continue; } List list4 = list3.stream().filter(e -> e.getChargingOrderId().equals(tChargingOrder.getId())).collect(Collectors.toList()); 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); // 累加充电总度数 chargingElectronic = chargingElectronic.add(chargingCapacity); // 累加分佣 if (tChargingOrder.getOrderSource() == 2) { sharingAmount = sharingAmount.add(service_reduce.multiply(new BigDecimal("0.2")).setScale(2, BigDecimal.ROUND_DOWN)); } // 累加电费 electrovalence = electrovalence.add(electronic_reduce); if (tChargingOrder.getPaymentAmount() != null && tChargingOrder.getOrderSource() != 2) { paymentAmount = paymentAmount.add(tChargingOrder.getPaymentAmount()); } if (tChargingOrder.getRechargeAmount() != null) { orderCommission = orderCommission.add(tChargingOrder.getRechargeAmount().multiply(new BigDecimal("0.006")) .setScale(2, BigDecimal.ROUND_DOWN)); } // 累加服务费 if (tChargingOrder.getServiceCharge() != null) { if (tChargingOrder.getOrderSource() == 2) { serviceCharge = serviceCharge.add(service_reduce.multiply(new BigDecimal("0.8")).setScale(2, BigDecimal.ROUND_DOWN)); paymentAmount = paymentAmount.add(electronic_reduce).add(service_reduce.multiply(new BigDecimal("0.8"))); } else { serviceCharge = serviceCharge.add(service_reduce); } } // 累加会员折扣 if (tChargingOrder.getVipDiscountAmount() != null) { vipDiscount = vipDiscount.add(tChargingOrder.getVipDiscountAmount()); } // 累加优惠券折扣 if (tChargingOrder.getCouponDiscountAmount() != null) { couponDiscount = couponDiscount.add(tChargingOrder.getCouponDiscountAmount()); } } // 三方交易手续费 三方收费*0.6% commissionAmount = sharingAmount.multiply(new BigDecimal("0.006")); tSettlementConfirm.setSharingAmount(sharingAmount.setScale(2, RoundingMode.HALF_DOWN)); tSettlementConfirm.setCommissionAmount(commissionAmount.setScale(2, RoundingMode.HALF_DOWN)); tSettlementConfirm.setElectrovalence(electrovalence.setScale(2, RoundingMode.HALF_DOWN)); tSettlementConfirm.setServiceCharge(serviceCharge.subtract(orderCommission).setScale(2, RoundingMode.HALF_DOWN)); tSettlementConfirm.setOrderCommission(orderCommission); tSettlementConfirm.setVipDiscount(vipDiscount); tSettlementConfirm.setCouponDiscount(couponDiscount); tSettlementConfirm.setSiteId(dto.getSiteId()); tSettlementConfirm.setChargingElectronic(chargingElectronic); tSettlementConfirm.setIncome(chargingElectronic.add(serviceCharge)); tSettlementConfirm.setVipDiscount(vipDiscount); tSettlementConfirm.setCouponDiscount(couponDiscount); tSettlementConfirm.setType(dto.getType()); if (dto.getState() == 2) { tSettlementConfirm.setServiceMoney(dto.getServiceCharge().subtract(dto.getVenue()).subtract(dto.getClean()) .subtract(dto.getMaintain()).multiply(dto.getServicePartner())); tSettlementConfirm.setSupplyElectronic(dto.getSupplyElectronic()); tSettlementConfirm.setVenue(dto.getVenue()); tSettlementConfirm.setMetering(dto.getMetering()); tSettlementConfirm.setClean(dto.getClean()); tSettlementConfirm.setLossElectronic(dto.getMeteringElectronic().subtract(dto.getChargingElectronic())); tSettlementConfirm.setMaintain(dto.getMaintain()); tSettlementConfirm.setMeteringElectronic(dto.getMeteringElectronic()); tSettlementConfirm.setProportionPartner(dto.getProportionPartner()); tSettlementConfirm.setProportionMoney(dto.getProportionMoney()); tSettlementConfirm.setTotalElectronic(dto.getTotalElectronic()); tSettlementConfirm.setTotalService(dto.getTotalService()); tSettlementConfirm.setRemark(dto.getRemark()); tSettlementConfirm.setServicePartner(dto.getServicePartner()); BigDecimal subtract = new BigDecimal("1").subtract(dto.getProportionPartner()); BigDecimal subtract9 = new BigDecimal("1").subtract(dto.getServicePartner()); // 总电损费用 BigDecimal divide = tSettlementConfirm.getProportionMoney().divide(subtract, 2, BigDecimal.ROUND_DOWN); // 平台承担电损 BigDecimal subtract1 = divide.subtract(tSettlementConfirm.getProportionMoney()); BigDecimal multiply = dto.getServiceMoney().multiply(subtract); BigDecimal subtract2 = multiply.subtract(subtract1); tSettlementConfirm.setNewMoney(dto.getServiceCharge().subtract(dto.getVenue()).subtract(dto.getClean()) .subtract(dto.getMaintain()).multiply(subtract9).subtract(subtract1)); tSettlementConfirm.setTotalService(dto.getTotalService()); tSettlementConfirm.setServiceRemark(dto.getServiceRemark()); tSettlementConfirm.setDistribution(dto.getDistribution()); tSettlementConfirm.setIncome(dto.getElectrovalence().add(dto.getServiceCharge())); tSettlementConfirm.setCost(dto.getVenue().add(dto.getClean()).add(dto.getMaintain()).add(dto.getSupplyElectronic())); tSettlementConfirm.setProfitMoney(tSettlementConfirm.getIncome().subtract(tSettlementConfirm.getCost())); tSettlementConfirm.setNewSettlement(tSettlementConfirm.getProfitMoney().add(tSettlementConfirm.getCost())); tSettlementConfirm.setServiceMoney(dto.getServiceCharge().subtract(dto.getVenue()).subtract(dto.getClean()) .subtract(dto.getMaintain()).multiply(dto.getServicePartner())); tSettlementConfirmMapper.insert(tSettlementConfirm); } return tSettlementConfirm; } @Override public PageInfo settlementList(SettlementListQuery dto) { String startTime = null; String endTime = null; if (StringUtils.hasLength(dto.getStartTime())) { dto.setType(1); String[] split = dto.getStartTime().split(" - "); startTime = split[0]; endTime = split[1]; } if (StringUtils.hasLength(dto.getEndTime())) { dto.setType(2); String[] split = dto.getEndTime().split(" - "); startTime = split[0]; endTime = split[1]; } dto.setStartTime(startTime); dto.setEndTime(endTime); Long userId = tokenService.getLoginUser().getUserid(); // 查询当前登陆人按钮权限 SysUser sysUser = sysUserClient.getSysUser(tokenService.getLoginUser().getUserid()).getData(); Integer roleType = sysUser.getRoleType(); //如果没传siteId,获取当前登陆人所有的siteIds List siteIds = new ArrayList<>(); if (dto.getSiteIds() == null) { if (userId != null) { List data = siteClient.getSiteListByUserId(userId).getData(); for (GetSiteListDTO datum : data) { siteIds.add(datum.getId()); } } } else { siteIds.add(dto.getSiteIds()); } if (siteIds.isEmpty()) { siteIds.add(-1); } else { if (roleType == 2) { List integers = new ArrayList<>(); for (Integer siteId : siteIds) { // 校验有没有这个站点的权限 List t1 = partnerClient.getSettlementListMenu(sysUser.getObjectId(), siteId).getData(); Boolean b = t1.get(2); if (b) { integers.add(siteId); } } siteIds = integers; } } dto.setSiteIdss(siteIds); PageInfo pageInfo = new PageInfo<>(dto.getPageCurr(), dto.getPageSize()); List list = tSettlementConfirmMapper.settlementList(pageInfo, dto); for (TSettlementConfirm tSettlementConfirm : list) { if (roleType == 2) { List data = partnerClient.getSettlementListMenu(sysUser.getObjectId(), tSettlementConfirm.getSiteId()).getData(); tSettlementConfirm.setAuthDownLoad(data.get(0)); tSettlementConfirm.setAuthDelete(data.get(1)); } tSettlementConfirm.setUid(tSettlementConfirm.getId().toString()); List data1 = siteClient.getSiteByIds(Arrays.asList(tSettlementConfirm.getSiteId())).getData(); if (!data1.isEmpty()) { tSettlementConfirm.setSiteName(data1.get(0).getName()); Partner data = siteClient.getPartnerR(data1.get(0).getPartnerId()).getData(); if (data != null) { tSettlementConfirm.setPartnerName(data.getName()); } } String format = tSettlementConfirm.getStartTime().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日HH:mm:ss")); String format1 = tSettlementConfirm.getEndTime().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日HH:mm:ss")); tSettlementConfirm.setTime(format + "至" + format1); } pageInfo.setRecords(list); return pageInfo; } @Autowired private TChargingOrderService chargingOrderService; @Override public SettlementTotalVO settlementTotal(String time) { Long userId = tokenService.getLoginUser().getUserid(); //如果没传siteId,获取当前登陆人所有的siteIds List siteIds = new ArrayList<>(); if (userId != null) { List data = siteClient.getSiteListByUserId(userId).getData(); for (GetSiteListDTO datum : data) { siteIds.add(datum.getId()); } } if (siteIds.isEmpty()) { siteIds.add(-1); } List siteList = siteClient.getSiteAll().getData(); List pileList = chargingGunClient.getAllPile().getData(); List totalOrder = chargingOrderService.list(new LambdaQueryWrapper<>(TChargingOrder.class) .eq(TChargingOrder::getRechargePaymentStatus, 2).eq(TChargingOrder::getStatus, 5)); SettlementTotalVO res = new SettlementTotalVO(); List list1 = tSettlementConfirmMapper.settlementTotal(time, siteIds); LocalDateTime parse = LocalDateTime.parse(time, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // 减少一个月 LocalDateTime minus = parse.minusMonths(1); List list2 = tSettlementConfirmMapper.settlementTotal(time, siteIds); // 合计 List total = new ArrayList<>(); TSettlementConfirm tSettlementConfirm1 = new TSettlementConfirm(); BigDecimal meteringElectronic = new BigDecimal("0"); BigDecimal chargingElectronic = new BigDecimal("0"); BigDecimal lossElectronic = new BigDecimal("0"); BigDecimal income = new BigDecimal("0"); BigDecimal venue = new BigDecimal("0"); BigDecimal metering = new BigDecimal("0"); BigDecimal clean = new BigDecimal("0"); BigDecimal maintain = new BigDecimal("0"); BigDecimal cost = new BigDecimal("0"); BigDecimal profitMoney = new BigDecimal("0"); BigDecimal newMoney = new BigDecimal("0"); BigDecimal newSettlement = new BigDecimal("0"); BigDecimal supplyElectronic = new BigDecimal("0"); BigDecimal proportionPartner = new BigDecimal("0"); BigDecimal proportionMoney = new BigDecimal("0"); BigDecimal totalElectronic = new BigDecimal("0"); BigDecimal totalService = new BigDecimal("0"); BigDecimal servicePartner = new BigDecimal("0"); BigDecimal serviceMoney = new BigDecimal("0"); BigDecimal sharingAmount = new BigDecimal("0"); BigDecimal commissionAmount = new BigDecimal("0"); BigDecimal electrovalence = new BigDecimal("0"); BigDecimal serviceCharge = new BigDecimal("0"); BigDecimal orderCommission = new BigDecimal("0"); BigDecimal vipDiscount = new BigDecimal("0"); BigDecimal couponDiscount = new BigDecimal("0"); tSettlementConfirm1.setElectronicRefund(0); // 上月成本合计 BigDecimal beforeCost = new BigDecimal("0"); // 上月利润合计 BigDecimal beforeIncome = new BigDecimal("0"); int i = 1; for (TSettlementConfirm tSettlementConfirm : list1) { tSettlementConfirm.setXuhao(i); tSettlementConfirm.setMetering(tSettlementConfirm.getSupplyElectronic()); Site site = siteList.stream().filter(e -> e.getId().equals(tSettlementConfirm.getSiteId())).findFirst().orElse(null); if (site != null) { tSettlementConfirm.setSiteName(site.getName()); } if (tSettlementConfirm.getMeteringElectronic() != null) { meteringElectronic = meteringElectronic.add(tSettlementConfirm.getMeteringElectronic()); chargingElectronic = chargingElectronic.add(tSettlementConfirm.getChargingElectronic()); } if (tSettlementConfirm.getLossElectronic() != null) { lossElectronic = lossElectronic.add(tSettlementConfirm.getLossElectronic()); } income = income.add(tSettlementConfirm.getIncome()); venue = venue.add(tSettlementConfirm.getVenue()); metering = metering.add(tSettlementConfirm.getMetering()); clean = clean.add(tSettlementConfirm.getClean()); maintain = maintain.add(tSettlementConfirm.getMaintain()); cost = cost.add(tSettlementConfirm.getCost()); profitMoney = profitMoney.add(tSettlementConfirm.getProfitMoney()); newMoney = newMoney.add(tSettlementConfirm.getNewMoney()); newSettlement = newSettlement.add(tSettlementConfirm.getNewSettlement()); supplyElectronic = supplyElectronic.add(tSettlementConfirm.getSupplyElectronic()); proportionPartner = proportionPartner.add(tSettlementConfirm.getProportionPartner()); proportionMoney = proportionMoney.add(tSettlementConfirm.getProportionMoney()); totalElectronic = totalElectronic.add(tSettlementConfirm.getTotalElectronic()); totalService = totalService.add(tSettlementConfirm.getTotalService()); servicePartner = servicePartner.add(tSettlementConfirm.getServicePartner()); serviceMoney = serviceMoney.add(tSettlementConfirm.getServiceMoney()); sharingAmount = sharingAmount.add(tSettlementConfirm.getSharingAmount()); commissionAmount = commissionAmount.add(tSettlementConfirm.getCommissionAmount()); electrovalence = electrovalence.add(tSettlementConfirm.getElectrovalence()); serviceCharge = serviceCharge.add(tSettlementConfirm.getServiceCharge()); orderCommission = orderCommission.add(tSettlementConfirm.getOrderCommission()); vipDiscount = vipDiscount.add(tSettlementConfirm.getVipDiscount()); couponDiscount = couponDiscount.add(tSettlementConfirm.getCouponDiscount()); // 查询结算确认单开始时间和结束时间的单子 QueryWrapper eq = new QueryWrapper() .eq("site_id", tSettlementConfirm.getSiteId()) .eq("recharge_payment_status", 2); switch (tSettlementConfirm.getType()) { case 1: eq.between("start_time", tSettlementConfirm.getStartTime(), tSettlementConfirm.getEndTime()); break; case 2: eq.between("end_time", tSettlementConfirm.getStartTime(), tSettlementConfirm.getEndTime()); break; } List tChargingOrders = totalOrder.stream().filter(e -> e.getPayTime() .isBefore(tSettlementConfirm.getEndTime()) && e.getPayTime().isAfter(tSettlementConfirm.getStartTime()) && e.getSiteId().equals(tSettlementConfirm.getSiteId())).collect(Collectors.toList()); // 充电总时长 Long temp = 0L; for (TChargingOrder tChargingOrder : tChargingOrders) { LocalDateTime startTime = tChargingOrder.getStartTime(); LocalDateTime endTime = tChargingOrder.getEndTime(); // 计算时间差 单位秒 long between = ChronoUnit.SECONDS.between(startTime, endTime); temp += between; } List data = siteClient.getPileListBySiteId(tSettlementConfirm.getSiteId()).getData(); // 计算充电桩的功率平均值 BigDecimal bigDecimal = new BigDecimal("0"); for (TChargingPile datum : data) { bigDecimal = bigDecimal.add(datum.getRatedPower()); } tSettlementConfirm.setIncome(tSettlementConfirm.getElectrovalence().add(tSettlementConfirm.getServiceCharge())); //利用率=充电量/(桩数量*功率*时间) List data2 = pileList.stream().filter(e -> e.getSiteId().equals(tSettlementConfirm.getSiteId())).collect(Collectors.toList()); BigDecimal bigDecimal2 = new BigDecimal("0"); if (data2 != null && (!data2.isEmpty())) { for (TChargingPile tChargingPile : data2) { bigDecimal2 = bigDecimal2.add(tChargingPile.getRatedPower()); } BigDecimal bigDecimal1 = new BigDecimal(data2.size() + ""); // 平均功率 BigDecimal divide = bigDecimal2.divide(bigDecimal1, 2, RoundingMode.HALF_DOWN); // 充电量 BigDecimal chargingElectronic1 = tSettlementConfirm.getChargingElectronic(); // 桩数量 BigDecimal bigDecimal3 = new BigDecimal(data2.size()); BigDecimal multiply = chargingElectronic1.multiply(new BigDecimal("24")); BigDecimal multiply1 = bigDecimal3.multiply(divide).multiply(new BigDecimal(temp)); tSettlementConfirm.setRate(chargingElectronic1.divide(multiply1, 2, RoundingMode.HALF_DOWN) + ""); } else { tSettlementConfirm.setRate(""); } for (TSettlementConfirm settlementConfirm : list2) { int value = parse.getMonth().getValue(); settlementConfirm.setMonth(value); settlementConfirm.setIncome(settlementConfirm.getElectrovalence().add(settlementConfirm.getServiceCharge())); tSettlementConfirm.setMetering(tSettlementConfirm.getSupplyElectronic()); if (tSettlementConfirm.getSiteId().equals(settlementConfirm.getSiteId())) { // 电站相同比较收入涨幅跌幅 if (tSettlementConfirm.getIncome().compareTo(BigDecimal.ZERO) > 0) { BigDecimal subtract = tSettlementConfirm.getIncome().subtract(settlementConfirm.getIncome()).divide(tSettlementConfirm.getIncome(), 2, RoundingMode.HALF_DOWN).multiply(new BigDecimal("100")); tSettlementConfirm.setIncomePercentage(subtract + "%"); } else { tSettlementConfirm.setIncomePercentage("100" + "%"); } // 比较总利润 收入合计-成本合计 BigDecimal subtract1 = tSettlementConfirm.getIncome().subtract(tSettlementConfirm.getCost()); BigDecimal subtract2 = settlementConfirm.getIncome().subtract(settlementConfirm.getCost()); BigDecimal bigDecimal1 = subtract1.subtract(subtract2) .divide(subtract2, 2, RoundingMode.HALF_DOWN) .multiply(new BigDecimal("100")) .setScale(2, RoundingMode.HALF_DOWN); tSettlementConfirm.setTotalPercentage(bigDecimal1 + "%"); } beforeCost = beforeCost.add(settlementConfirm.getCost()); beforeIncome = beforeIncome.add(settlementConfirm.getIncome()); } i++; } TSettlementConfirm tSettlementConfirm = new TSettlementConfirm(); tSettlementConfirm.setMonth(parse.getMonthValue()); tSettlementConfirm.setMeteringElectronic(meteringElectronic); tSettlementConfirm.setChargingElectronic(chargingElectronic); tSettlementConfirm.setLossElectronic(lossElectronic); tSettlementConfirm.setIncome(income); tSettlementConfirm.setVenue(venue); tSettlementConfirm.setMetering(metering); tSettlementConfirm.setClean(clean); tSettlementConfirm.setMaintain(maintain); tSettlementConfirm.setCost(cost); tSettlementConfirm.setProfitMoney(profitMoney); tSettlementConfirm.setNewMoney(newMoney); tSettlementConfirm.setNewSettlement(newSettlement); tSettlementConfirm.setSupplyElectronic(supplyElectronic); tSettlementConfirm.setProportionPartner(proportionPartner); tSettlementConfirm.setProportionMoney(proportionMoney); tSettlementConfirm.setTotalElectronic(totalElectronic); tSettlementConfirm.setTotalService(totalService); tSettlementConfirm.setServicePartner(servicePartner); tSettlementConfirm.setServiceMoney(serviceMoney); tSettlementConfirm.setSharingAmount(sharingAmount); tSettlementConfirm.setCommissionAmount(commissionAmount); tSettlementConfirm.setElectrovalence(electrovalence); tSettlementConfirm.setServiceCharge(serviceCharge); tSettlementConfirm.setOrderCommission(orderCommission); tSettlementConfirm.setVipDiscount(vipDiscount); tSettlementConfirm.setCouponDiscount(couponDiscount); // 本月 if (beforeIncome.compareTo(new BigDecimal("0")) == 0) { tSettlementConfirm.setIncomePercentage(0 + "%"); tSettlementConfirm.setIncomePercentage(0 + "%"); } else { BigDecimal subtract = income.subtract(beforeIncome).divide(beforeIncome, 2, RoundingMode.HALF_DOWN).multiply(new BigDecimal("100")); tSettlementConfirm.setIncomePercentage(subtract + "%"); tSettlementConfirm.setIncomePercentage(subtract + "%"); } // 比较总利润 收入合计-成本合计 BigDecimal subtract1 = income.subtract(cost); BigDecimal subtract2 = beforeIncome.subtract(beforeCost); if (subtract2.compareTo(new BigDecimal("0")) == 0) { tSettlementConfirm.setTotalPercentage(0 + "%"); } else { tSettlementConfirm.setTotalPercentage(subtract1.subtract(subtract2).divide(subtract2, 2, RoundingMode.HALF_DOWN).setScale(2, RoundingMode.HALF_DOWN).multiply(new BigDecimal("100")) + "%"); } tSettlementConfirm.setElectronicRefund(0); // 查询上次汇报数据 进行比对涨幅跌幅 total.add(tSettlementConfirm); res.setList1(list1); res.setList2(total); return res; } @Override public SettlementTotalVO settlementTotalR(String time, Long userId) { //如果没传siteId,获取当前登陆人所有的siteIds List siteIds = new ArrayList<>(); if (userId != null) { List data = siteClient.getSiteListByUserId(userId).getData(); for (GetSiteListDTO datum : data) { siteIds.add(datum.getId()); } } if (siteIds.isEmpty()) { siteIds.add(-1); } List siteList = siteClient.getSiteAll().getData(); List gunList = chargingGunClient.getAllGun().getData(); List pileList = chargingGunClient.getAllPile().getData(); List userList = appUserClient.getAllUser().getData(); List carList = appUserCarClient.getAllCar().getData(); List totalOrder = chargingOrderService.list(new LambdaQueryWrapper<>(TChargingOrder.class) .eq(TChargingOrder::getRechargePaymentStatus, 2)); SettlementTotalVO res = new SettlementTotalVO(); List list1 = tSettlementConfirmMapper.settlementTotal(time, siteIds); LocalDateTime parse = LocalDateTime.parse(time, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); List list2 = tSettlementConfirmMapper.settlementTotal(time, siteIds); // 合计 List total = new ArrayList<>(); TSettlementConfirm tSettlementConfirm1 = new TSettlementConfirm(); BigDecimal meteringElectronic = new BigDecimal("0"); BigDecimal chargingElectronic = new BigDecimal("0"); BigDecimal lossElectronic = new BigDecimal("0"); BigDecimal income = new BigDecimal("0"); BigDecimal venue = new BigDecimal("0"); BigDecimal metering = new BigDecimal("0"); BigDecimal clean = new BigDecimal("0"); BigDecimal maintain = new BigDecimal("0"); BigDecimal cost = new BigDecimal("0"); BigDecimal profitMoney = new BigDecimal("0"); BigDecimal newMoney = new BigDecimal("0"); BigDecimal newSettlement = new BigDecimal("0"); BigDecimal supplyElectronic = new BigDecimal("0"); BigDecimal proportionPartner = new BigDecimal("0"); BigDecimal proportionMoney = new BigDecimal("0"); BigDecimal totalElectronic = new BigDecimal("0"); BigDecimal totalService = new BigDecimal("0"); BigDecimal servicePartner = new BigDecimal("0"); BigDecimal serviceMoney = new BigDecimal("0"); BigDecimal sharingAmount = new BigDecimal("0"); BigDecimal commissionAmount = new BigDecimal("0"); BigDecimal electrovalence = new BigDecimal("0"); BigDecimal serviceCharge = new BigDecimal("0"); BigDecimal orderCommission = new BigDecimal("0"); BigDecimal vipDiscount = new BigDecimal("0"); BigDecimal couponDiscount = new BigDecimal("0"); // tSettlementConfirm1.setIncomePercentage(); // tSettlementConfirm1.setTotalPercentage(); tSettlementConfirm1.setElectronicRefund(0); // 上月成本合计 BigDecimal beforeCost = new BigDecimal("0"); // 上月利润合计 BigDecimal beforeIncome = new BigDecimal("0"); int i = 1; for (TSettlementConfirm tSettlementConfirm : list1) { tSettlementConfirm.setXuhao(i); tSettlementConfirm.setMetering(tSettlementConfirm.getSupplyElectronic()); List data1 = siteList.stream().filter(e -> e.getId().equals(tSettlementConfirm.getSiteId())).collect(Collectors.toList()); if (!data1.isEmpty()) { tSettlementConfirm.setSiteName(data1.get(0).getName()); } if (tSettlementConfirm.getMeteringElectronic() != null) { meteringElectronic = meteringElectronic.add(tSettlementConfirm.getMeteringElectronic()); chargingElectronic = chargingElectronic.add(tSettlementConfirm.getChargingElectronic()); } if (tSettlementConfirm.getLossElectronic() != null) { lossElectronic = lossElectronic.add(tSettlementConfirm.getLossElectronic()); } income = income.add(tSettlementConfirm.getIncome()); venue = venue.add(tSettlementConfirm.getVenue()); metering = metering.add(tSettlementConfirm.getMetering()); clean = clean.add(tSettlementConfirm.getClean()); maintain = maintain.add(tSettlementConfirm.getMaintain()); cost = cost.add(tSettlementConfirm.getCost()); profitMoney = profitMoney.add(tSettlementConfirm.getProfitMoney()); newMoney = newMoney.add(tSettlementConfirm.getNewMoney()); newSettlement = newSettlement.add(tSettlementConfirm.getNewSettlement()); supplyElectronic = supplyElectronic.add(tSettlementConfirm.getSupplyElectronic()); proportionPartner = proportionPartner.add(tSettlementConfirm.getProportionPartner()); proportionMoney = proportionMoney.add(tSettlementConfirm.getProportionMoney()); totalElectronic = totalElectronic.add(tSettlementConfirm.getTotalElectronic()); totalService = totalService.add(tSettlementConfirm.getTotalService()); servicePartner = servicePartner.add(tSettlementConfirm.getServicePartner()); serviceMoney = serviceMoney.add(tSettlementConfirm.getServiceMoney()); sharingAmount = sharingAmount.add(tSettlementConfirm.getSharingAmount()); commissionAmount = commissionAmount.add(tSettlementConfirm.getCommissionAmount()); electrovalence = electrovalence.add(tSettlementConfirm.getElectrovalence()); serviceCharge = serviceCharge.add(tSettlementConfirm.getServiceCharge()); orderCommission = orderCommission.add(tSettlementConfirm.getOrderCommission()); vipDiscount = vipDiscount.add(tSettlementConfirm.getVipDiscount()); couponDiscount = couponDiscount.add(tSettlementConfirm.getCouponDiscount()); // 查询结算确认单开始时间和结束时间的单子 QueryWrapper eq = new QueryWrapper() .eq("site_id", tSettlementConfirm.getSiteId()) .eq("recharge_payment_status", 2); switch (tSettlementConfirm.getType()) { case 1: eq.between("start_time", tSettlementConfirm.getStartTime(), tSettlementConfirm.getEndTime()); break; case 2: eq.between("end_time", tSettlementConfirm.getStartTime(), tSettlementConfirm.getEndTime()); break; } List tChargingOrders = totalOrder.stream().filter(e -> e.getPayTime() .isBefore(tSettlementConfirm.getEndTime()) && e.getPayTime().isAfter(tSettlementConfirm.getStartTime()) && e.getSiteId().equals(tSettlementConfirm.getSiteId())).collect(Collectors.toList()); // 充电总时长 Long temp = 0L; for (TChargingOrder tChargingOrder : tChargingOrders) { LocalDateTime startTime = tChargingOrder.getStartTime(); LocalDateTime endTime = tChargingOrder.getEndTime(); // 计算时间差 单位秒 long between = ChronoUnit.SECONDS.between(startTime, endTime); temp += between; } List data = siteClient.getPileListBySiteId(tSettlementConfirm.getSiteId()).getData(); // 计算充电桩的功率平均值 BigDecimal bigDecimal = new BigDecimal("0"); for (TChargingPile datum : data) { bigDecimal = bigDecimal.add(datum.getRatedPower()); } tSettlementConfirm.setIncome(tSettlementConfirm.getElectrovalence().add(tSettlementConfirm.getServiceCharge())); //利用率=充电量/(桩数量*功率*时间) List data2 = chargingPileClient.getChargingPileBySiteId(tSettlementConfirm.getSiteId()).getData(); BigDecimal bigDecimal2 = new BigDecimal("0"); if (data2 != null && (!data2.isEmpty())) { for (TChargingPile tChargingPile : data2) { bigDecimal2 = bigDecimal2.add(tChargingPile.getRatedPower()); } BigDecimal bigDecimal1 = new BigDecimal(data2.size() + ""); // 平均功率 BigDecimal divide = bigDecimal2.divide(bigDecimal1, 2, RoundingMode.HALF_DOWN); // 充电量 BigDecimal chargingElectronic1 = tSettlementConfirm.getChargingElectronic(); // 桩数量 BigDecimal bigDecimal3 = new BigDecimal(data2.size()); BigDecimal multiply = chargingElectronic1.multiply(new BigDecimal("24")); BigDecimal multiply1 = bigDecimal3.multiply(divide).multiply(new BigDecimal(temp)); tSettlementConfirm.setRate(chargingElectronic1.divide(multiply1, 2, RoundingMode.HALF_DOWN) + ""); } else { tSettlementConfirm.setRate(""); } for (TSettlementConfirm settlementConfirm : list2) { int value = parse.getMonth().getValue(); settlementConfirm.setMonth(value); settlementConfirm.setIncome(settlementConfirm.getElectrovalence().add(settlementConfirm.getServiceCharge())); tSettlementConfirm.setMetering(tSettlementConfirm.getSupplyElectronic()); if (tSettlementConfirm.getSiteId().equals(settlementConfirm.getSiteId())) { // 电站相同比较收入涨幅跌幅 if (tSettlementConfirm.getIncome().compareTo(BigDecimal.ZERO) > 0) { BigDecimal subtract = tSettlementConfirm.getIncome().subtract(settlementConfirm.getIncome()).divide(tSettlementConfirm.getIncome(), 2, RoundingMode.HALF_DOWN).multiply(new BigDecimal("100")); tSettlementConfirm.setIncomePercentage(subtract + "%"); } else { tSettlementConfirm.setIncomePercentage("100" + "%"); } // 比较总利润 收入合计-成本合计 BigDecimal subtract1 = tSettlementConfirm.getIncome().subtract(tSettlementConfirm.getCost()); BigDecimal subtract2 = settlementConfirm.getIncome().subtract(settlementConfirm.getCost()); BigDecimal bigDecimal1 = subtract1.subtract(subtract2) .divide(subtract2, 2, RoundingMode.HALF_DOWN) .multiply(new BigDecimal("100")) .setScale(2, RoundingMode.HALF_DOWN); tSettlementConfirm.setTotalPercentage(bigDecimal1 + "%"); } beforeCost = beforeCost.add(settlementConfirm.getCost()); beforeIncome = beforeIncome.add(settlementConfirm.getIncome()); } i++; } TSettlementConfirm tSettlementConfirm = new TSettlementConfirm(); tSettlementConfirm.setMonth(parse.getMonthValue()); tSettlementConfirm.setMeteringElectronic(meteringElectronic); tSettlementConfirm.setChargingElectronic(chargingElectronic); tSettlementConfirm.setLossElectronic(lossElectronic); tSettlementConfirm.setIncome(income); tSettlementConfirm.setVenue(venue); tSettlementConfirm.setMetering(metering); tSettlementConfirm.setClean(clean); tSettlementConfirm.setMaintain(maintain); tSettlementConfirm.setCost(cost); tSettlementConfirm.setProfitMoney(profitMoney); tSettlementConfirm.setNewMoney(newMoney); tSettlementConfirm.setNewSettlement(newSettlement); tSettlementConfirm.setSupplyElectronic(supplyElectronic); tSettlementConfirm.setProportionPartner(proportionPartner); tSettlementConfirm.setProportionMoney(proportionMoney); tSettlementConfirm.setTotalElectronic(totalElectronic); tSettlementConfirm.setTotalService(totalService); tSettlementConfirm.setServicePartner(servicePartner); tSettlementConfirm.setServiceMoney(serviceMoney); tSettlementConfirm.setSharingAmount(sharingAmount); tSettlementConfirm.setCommissionAmount(commissionAmount); tSettlementConfirm.setElectrovalence(electrovalence); tSettlementConfirm.setServiceCharge(serviceCharge); tSettlementConfirm.setOrderCommission(orderCommission); tSettlementConfirm.setVipDiscount(vipDiscount); tSettlementConfirm.setCouponDiscount(couponDiscount); // 本月 if (beforeIncome.compareTo(new BigDecimal("0")) == 0) { tSettlementConfirm.setIncomePercentage(0 + "%"); tSettlementConfirm.setIncomePercentage(0 + "%"); } else { BigDecimal subtract = income.subtract(beforeIncome).divide(beforeIncome, 2, RoundingMode.HALF_DOWN).multiply(new BigDecimal("100")); tSettlementConfirm.setIncomePercentage(subtract + "%"); tSettlementConfirm.setIncomePercentage(subtract + "%"); } // 比较总利润 收入合计-成本合计 BigDecimal subtract1 = income.subtract(cost); BigDecimal subtract2 = beforeIncome.subtract(beforeCost); if (subtract2.compareTo(new BigDecimal("0")) == 0) { tSettlementConfirm.setTotalPercentage(0 + "%"); } else { tSettlementConfirm.setTotalPercentage(subtract1.subtract(subtract2).divide(subtract2, 2, RoundingMode.HALF_DOWN).setScale(2, RoundingMode.HALF_DOWN).multiply(new BigDecimal("100")) + "%"); } tSettlementConfirm.setElectronicRefund(0); // 查询上次汇报数据 进行比对涨幅跌幅 total.add(tSettlementConfirm); res.setList1(list1); res.setList2(total); return res; } @Override public List> countBySource(List siteIds, ChargingStatisticsQueryDto statisticsQueryDto) { return this.baseMapper.countBySource(siteIds, statisticsQueryDto); } @Override public List> equipmentUserType1(List siteIds, ChargingStatisticsQueryDto statisticsQueryDto) { return this.baseMapper.equipmentUserType1(siteIds, statisticsQueryDto); } @Override public List> equipmentUserType2(List siteIds, ChargingStatisticsQueryDto statisticsQueryDto) { return this.baseMapper.equipmentUserType2(siteIds, statisticsQueryDto); } @Override public List> equipmentMapbroke1(List siteIds, ChargingStatisticsQueryDto statisticsQueryDto) { return this.baseMapper.equipmentMapbroke1(siteIds, statisticsQueryDto); } @Override public List> equipmentMapbroke2(List siteIds, ChargingStatisticsQueryDto statisticsQueryDto) { return this.baseMapper.equipmentMapbroke2(siteIds, statisticsQueryDto); } @Override public List> equipmentMapOut1(List siteIds, ChargingStatisticsQueryDto statisticsQueryDto) { return this.baseMapper.equipmentMapOut1(siteIds, statisticsQueryDto); } @Override public List> equipmentMapOut2(List siteIds, ChargingStatisticsQueryDto statisticsQueryDto) { return this.baseMapper.equipmentMapOut2(siteIds, statisticsQueryDto); } @Override public List> needElec(List siteIds, ChargingStatisticsQueryDto statisticsQueryDto) { return this.baseMapper.needElec(siteIds, statisticsQueryDto); } @Override public Map getChargingOrderCount(List siteIds, String time) { time = time.split(" ")[0]; return this.baseMapper.getChargingOrderCount(siteIds, time); } @Override public Map getLargeScreenSummary(List siteIds) { String cacheKey = largeScreenCacheKey("summary", siteIds); Map cached = redisService.getCacheObject(cacheKey); if (cached != null) { return cached; } Map result = new HashMap<>(); try { Map summary = this.baseMapper.summaryFromStat(siteIds); if (summary != null) { result.putAll(summary); } // 充电用户数精确去重(用户-站点对表, 跨站不重复); 对表未回填时回退到按站点求和 Long pairCount = this.baseMapper.userSiteTableCount(); if (pairCount != null && pairCount > 0) { Long exactUserCount = this.baseMapper.userCountFromStat(siteIds); if (exactUserCount != null) { result.put("chargingUserCount", exactUserCount); } } Long registered = this.baseMapper.registeredFromStat(); result.put("registeredUserCount", registered == null ? 0L : registered); } catch (Exception e) { log.warn("大屏累计指标读取预聚合表失败, siteIds={}", siteIds, e); } Arrays.asList("validOrderCount", "chargingUserCount", "registeredUserCount", "totalChargingCapacity", "electricityFee", "serviceFee", "totalIncome") .forEach(key -> result.putIfAbsent(key, 0)); redisService.setCacheObject(cacheKey, result, LARGE_SCREEN_CACHE_SECONDS, TimeUnit.SECONDS); return result; } @Override public List getLargeScreenIncomeTrend(List siteIds, LocalDate sixBefore) { String cacheKey = largeScreenCacheKey("incomeTrend", siteIds); List cached = redisService.getCacheObject(cacheKey); if (cached != null) { return cached; } LocalDate currentMonth = LocalDate.now().withDayOfMonth(1); List result = new ArrayList<>(); try { String sixBeforeMonth = sixBefore.withDayOfMonth(1).format(LARGE_SCREEN_MONTH_FMT); List incomeList = this.baseMapper.incomeTrendFromStat(siteIds, sixBeforeMonth); Map incomeByMonth = new HashMap<>(); if (incomeList != null) { for (SixChargingDto income : incomeList) { if (income != null && income.getMonth() != null) { incomeByMonth.put(income.getMonth(), income); } } } LocalDate month = sixBefore.withDayOfMonth(1); while (!month.isAfter(currentMonth)) { SixChargingDto income = incomeByMonth.get(month.format(LARGE_SCREEN_MONTH_FMT)); if (income == null) { income = new SixChargingDto(); } income.setMonth(String.format("%02d", month.getMonthValue())); if (income.getElectrovalence() == null) { income.setElectrovalence(BigDecimal.ZERO); } if (income.getServiceCharge() == null) { income.setServiceCharge(BigDecimal.ZERO); } result.add(income); month = month.plusMonths(1); } } catch (Exception e) { log.warn("大屏收入趋势读取预聚合表失败, siteIds={}, sixBefore={}", siteIds, sixBefore, e); LocalDate month = sixBefore.withDayOfMonth(1); while (!month.isAfter(currentMonth)) { SixChargingDto income = new SixChargingDto(); income.setMonth(String.format("%02d", month.getMonthValue())); income.setElectrovalence(BigDecimal.ZERO); income.setServiceCharge(BigDecimal.ZERO); result.add(income); month = month.plusMonths(1); } } redisService.setCacheObject(cacheKey, result, LARGE_SCREEN_CACHE_SECONDS, TimeUnit.SECONDS); return result; } @Override public List getLargeScreenChargingPower(List siteIds, LocalDateTime startTime, LocalDateTime endTime) { String cacheKey = largeScreenCacheKey("power", siteIds); List cached = redisService.getCacheObject(cacheKey); if (cached != null) { return cached; } List result = new ArrayList<>(); try { String dayStart = startTime.toLocalDate().format(LARGE_SCREEN_DAY_FMT); String dayEnd = endTime.toLocalDate().format(LARGE_SCREEN_DAY_FMT); List> dayAggs = this.baseMapper.powerFromStat(siteIds, dayStart, dayEnd); if (dayAggs != null) { for (Map agg : dayAggs) { BigDecimal totalPower = decimalValue(agg, "total_power"); BigDecimal orderCount = decimalValue(agg, "order_count"); BigDecimal power = orderCount.signum() == 0 ? BigDecimal.ZERO : totalPower.divide(orderCount, 0, RoundingMode.HALF_UP); LargeChargingPowerVo item = new LargeChargingPowerVo(); Object day = agg.get("day"); item.setTime(day == null ? null : String.valueOf(day)); item.setPower(power); result.add(item); } } } catch (Exception e) { log.warn("大屏功率曲线读取预聚合表失败, siteIds={}", siteIds, e); } redisService.setCacheObject(cacheKey, result, LARGE_SCREEN_CACHE_SECONDS, TimeUnit.SECONDS); return result; } @Override public BigDecimal getLargeScreenTotalCapacity(List siteIds) { String cacheKey = largeScreenCacheKey("capacity", siteIds); BigDecimal cached = redisService.getCacheObject(cacheKey); if (cached != null) { return cached; } BigDecimal capacity = BigDecimal.ZERO; try { BigDecimal fromStat = this.baseMapper.capacityFromStat(siteIds); if (fromStat != null) { capacity = fromStat; } } catch (Exception e) { log.warn("大屏累计充电量读取预聚合表失败, siteIds={}", siteIds, e); } redisService.setCacheObject(cacheKey, capacity, LARGE_SCREEN_CACHE_SECONDS, TimeUnit.SECONDS); return capacity; } @Override public void rebuildLargeScreenStat() { log.info("大屏预聚合表全量重建开始"); long start = System.currentTimeMillis(); // 1. 月度切片全量(数据起始月 ~ 当前月), 每次切片一次查询, 避免单条大查询超时 LocalDate month = LARGE_SCREEN_STAT_DATA_START.withDayOfMonth(1); LocalDate currentMonth = LocalDate.now().withDayOfMonth(1); while (!month.isAfter(currentMonth)) { writeMonthSlice(month, month.plusMonths(1)); writeUserSitePairs(month, month.plusMonths(1)); month = month.plusMonths(1); } // 2. 近90天日切片 LocalDate day = LocalDate.now().minusDays(89); LocalDate today = LocalDate.now(); while (!day.isAfter(today)) { writeDaySlice(day); day = day.plusDays(1); } // 3. 全局注册用户数 writeGlobal(); clearLargeScreenCache(); log.info("大屏预聚合表全量重建完成, 耗时 {}ms", System.currentTimeMillis() - start); } @Override public void refreshLargeScreenStat() { long start = System.currentTimeMillis(); LocalDate currentMonth = LocalDate.now().withDayOfMonth(1); writeMonthSlice(currentMonth, currentMonth.plusMonths(1)); writeDaySlice(LocalDate.now()); writeUserSitePairs(LocalDate.now(), LocalDate.now().plusDays(1)); writeGlobal(); clearLargeScreenCache(); log.info("大屏预聚合表刷新完成, 耗时 {}ms", System.currentTimeMillis() - start); } /** * 写入时间切片内的 (site_id, app_user_id) 对(充电用户精确去重用, INSERT IGNORE 幂等) */ private void writeUserSitePairs(LocalDate sliceStart, LocalDate sliceEnd) { List> pairs = this.baseMapper.userSitePairsAgg( sliceStart.atStartOfDay().format(LARGE_SCREEN_SQL_FMT), sliceEnd.atStartOfDay().format(LARGE_SCREEN_SQL_FMT)); if (pairs == null || pairs.isEmpty()) { return; } List> rows = new ArrayList<>(); for (Map pair : pairs) { if (pair == null || pair.get("site_id") == null || pair.get("app_user_id") == null) { continue; } Map row = new HashMap<>(); row.put("siteId", pair.get("site_id")); row.put("appUserId", pair.get("app_user_id")); rows.add(row); } if (!rows.isEmpty()) { this.baseMapper.insertUserSitePairs(rows); } } /** * 写入一个月度切片聚合(M 行) * 订单数取未删除、状态1-5口径;金额取已结束且已支付口径。 * 充电用户数取 monthSliceUserAgg(工作台口径: 该站点所有订单的去重用户, 不区分状态/删除标记) */ private void writeMonthSlice(LocalDate monthStart, LocalDate monthEnd) { String startTime = monthStart.atStartOfDay().format(LARGE_SCREEN_SQL_FMT); String endTime = monthEnd.atStartOfDay().format(LARGE_SCREEN_SQL_FMT); List> rows = this.baseMapper.monthSliceAgg(startTime, endTime); List> userRows = this.baseMapper.monthSliceUserAgg(startTime, endTime); if ((rows == null || rows.isEmpty()) && (userRows == null || userRows.isEmpty())) { return; } Map userCountBySite = new HashMap<>(); if (userRows != null) { for (Map userRow : userRows) { if (userRow != null && userRow.get("site_id") != null) { userCountBySite.put(userRow.get("site_id"), decimalValue(userRow, "user_count")); } } } String period = monthStart.format(LARGE_SCREEN_MONTH_FMT); List> statRows = new ArrayList<>(); Map written = new HashMap<>(); if (rows != null) { for (Map row : rows) { if (row == null || row.get("site_id") == null) { continue; } BigDecimal electricFee = decimalValue(row, "electric_fee"); BigDecimal serviceFee = decimalValue(row, "service_fee"); Map stat = new HashMap<>(); stat.put("statType", "M"); stat.put("period", period); stat.put("siteId", row.get("site_id")); stat.put("orderCount", decimalValue(row, "order_count")); stat.put("userCount", userCountBySite.getOrDefault(row.get("site_id"), BigDecimal.ZERO)); stat.put("capacity", decimalValue(row, "capacity")); stat.put("electricFee", electricFee); stat.put("serviceFee", serviceFee); stat.put("income", electricFee.add(serviceFee)); stat.put("totalPower", BigDecimal.ZERO); stat.put("registeredUserCount", 0L); statRows.add(stat); written.put(row.get("site_id"), Boolean.TRUE); } } // 当月仅有充电用户、暂无已结束订单的站点, 补一行保用户数(金额为0) for (Map.Entry entry : userCountBySite.entrySet()) { if (!written.containsKey(entry.getKey())) { Map stat = new HashMap<>(); stat.put("statType", "M"); stat.put("period", period); stat.put("siteId", entry.getKey()); stat.put("orderCount", BigDecimal.ZERO); stat.put("userCount", entry.getValue()); stat.put("capacity", BigDecimal.ZERO); stat.put("electricFee", BigDecimal.ZERO); stat.put("serviceFee", BigDecimal.ZERO); stat.put("income", BigDecimal.ZERO); stat.put("totalPower", BigDecimal.ZERO); stat.put("registeredUserCount", 0L); statRows.add(stat); } } if (!statRows.isEmpty()) { this.baseMapper.replaceSiteStatBatch(statRows); } } /** * 写入一个日切片聚合(D 行) */ private void writeDaySlice(LocalDate day) { List> rows = this.baseMapper.daySliceAgg( day.atStartOfDay().format(LARGE_SCREEN_SQL_FMT), day.plusDays(1).atStartOfDay().format(LARGE_SCREEN_SQL_FMT)); if (rows == null || rows.isEmpty()) { return; } List> statRows = new ArrayList<>(); for (Map row : rows) { if (row == null || row.get("site_id") == null) { continue; } Map stat = new HashMap<>(); stat.put("statType", "D"); stat.put("period", day.format(LARGE_SCREEN_DAY_FMT)); stat.put("siteId", row.get("site_id")); stat.put("orderCount", decimalValue(row, "order_count")); stat.put("userCount", BigDecimal.ZERO); stat.put("capacity", BigDecimal.ZERO); stat.put("electricFee", BigDecimal.ZERO); stat.put("serviceFee", BigDecimal.ZERO); stat.put("income", BigDecimal.ZERO); stat.put("totalPower", decimalValue(row, "total_power")); stat.put("registeredUserCount", 0L); statRows.add(stat); } if (!statRows.isEmpty()) { this.baseMapper.replaceSiteStatBatch(statRows); } } /** * 写入全局注册用户数(G 行) */ private void writeGlobal() { Long registered = this.baseMapper.globalRegisteredUsers(); Map stat = new HashMap<>(); stat.put("statType", "G"); stat.put("period", "ALL"); stat.put("siteId", 0); stat.put("orderCount", 0L); stat.put("userCount", 0L); stat.put("capacity", BigDecimal.ZERO); stat.put("electricFee", BigDecimal.ZERO); stat.put("serviceFee", BigDecimal.ZERO); stat.put("income", BigDecimal.ZERO); stat.put("totalPower", BigDecimal.ZERO); stat.put("registeredUserCount", registered == null ? 0L : registered); this.baseMapper.replaceSiteStatBatch(Collections.singletonList(stat)); } private String largeScreenCacheKey(String endpoint, List siteIds) { String idPart = siteIds == null ? "all" : DigestUtils.md5DigestAsHex( siteIds.stream().sorted().map(String::valueOf).collect(Collectors.joining(",")) .getBytes(StandardCharsets.UTF_8)); return LARGE_SCREEN_CACHE_PREFIX + endpoint + ":" + idPart; } private void clearLargeScreenCache() { Collection keys = redisService.keys(LARGE_SCREEN_CACHE_PREFIX + "*"); if (keys != null && !keys.isEmpty()) { redisService.deleteObject(keys); } } private BigDecimal decimalValue(Map values, String key) { if (values == null) { return BigDecimal.ZERO; } Object value = values.get(key); if (value == null) { value = values.get(key.toUpperCase(Locale.ROOT)); } return value == null ? BigDecimal.ZERO : new BigDecimal(value.toString()); } private String toSiteIdsCsv(List siteIds) { return siteIds == null ? null : siteIds.stream() .map(String::valueOf).collect(Collectors.joining(",")); } @Override public List> getHourType(List siteIds, ChargingDetailQueryDto statisticsQueryDto) { return this.baseMapper.getHourType(siteIds, statisticsQueryDto); } @Override public List> getDateType(List siteIds, ChargingDetailQueryDto statisticsQueryDto) { return this.baseMapper.getDateType(siteIds, statisticsQueryDto); } @Override public List> getMonthType(List siteIds, ChargingDetailQueryDto statisticsQueryDto) { return this.baseMapper.getMonthType(siteIds, statisticsQueryDto); } @Override public List> getchargingCapacity(List siteIds, ChargingDetailQueryDto statisticsQueryDto) { return this.baseMapper.getchargingCapacity(siteIds, statisticsQueryDto); } @Override public List> countAllUserData() { return this.baseMapper.countAllUserData(); } @Override public List> needElec1(List siteIds, ChargingStatisticsQueryDto statisticsQueryDto) { return this.baseMapper.needElec1(siteIds, statisticsQueryDto); } /** * 获取充电结束页面详情 * * @param id * @return */ @Override public EndOfChargePageInfo getEndOfChargePageInfo(String id) { EndOfChargePageInfo info = new EndOfChargePageInfo(); TChargingOrder chargingOrder = this.getById(id); Long userId = tokenService.getLoginUserApplet().getUserId(); if (!chargingOrder.getAppUserId().equals(userId)) { throw new RuntimeException("权限不足"); } info.setId(id); info.setCode(chargingOrder.getCode()); info.setPaymentAmount(chargingOrder.getPaymentAmount()); info.setCouponDiscountAmount(chargingOrder.getCouponDiscountAmount()); TAppUserIntegralChange integralChange = appUserIntegralChangeClient.getTAppUserIntegralChangeByOrderCode(chargingOrder.getCode()).getData(); info.setIntegral(null == integralChange ? 0 : integralChange.getCurrentIntegral() - integralChange.getHistoricalIntegral()); info.setRechargeAmount(chargingOrder.getRechargeAmount()); info.setRefundAmount(chargingOrder.getRefundAmount()); LocalDateTime startTime = chargingOrder.getStartTime(); LocalDateTime endTime = chargingOrder.getEndTime(); info.setStartDay(startTime.format(DateTimeFormatter.ofPattern("MM月dd日")) + " " + startTime.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.SIMPLIFIED_CHINESE)); info.setStartHour(startTime.format(DateTimeFormatter.ofPattern("HH:mm"))); info.setEndDay(endTime.format(DateTimeFormatter.ofPattern("MM月dd日")) + " " + endTime.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.SIMPLIFIED_CHINESE)); info.setEndHour(endTime.format(DateTimeFormatter.ofPattern("HH:mm"))); long time = (endTime.toEpochSecond(ZoneOffset.UTC) - startTime.toEpochSecond(ZoneOffset.UTC)) / 60; long hour = time / 60; long second = time % 60; info.setDuration(0 == hour ? String.format("%s分钟", second) : String.format("%s小时%s分钟", hour, second)); return info; } @Override public Long countNoTag() { return this.baseMapper.countNoTag(); } @Override public List getChargingOrder(TChargingOrderVo order) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper().eq(TChargingOrder::getDelFlag, 0); if (null != order.getChargingPileId()) { queryWrapper.eq(TChargingOrder::getChargingPileId, order.getChargingPileId()); } if (null != order.getChargingGunId()) { queryWrapper.eq(TChargingOrder::getChargingGunId, order.getChargingGunId()); } if (null != order.getStatus()) { queryWrapper.eq(TChargingOrder::getStatus, order.getStatus()); } if (null != order.getAppUserId()) { queryWrapper.eq(TChargingOrder::getAppUserId, order.getAppUserId()); } if (null != order.getStatusList()) { queryWrapper.in(TChargingOrder::getStatus, order.getStatusList()); } if (null != order.getEndMode()) { queryWrapper.eq(TChargingOrder::getEndMode, order.getEndMode()); } return this.list(queryWrapper.orderByDesc(TChargingOrder::getCreateTime)); } @Override public List getAppUserIdsBySiteIds(List siteIds) { if (siteIds == null || siteIds.isEmpty()) { return Collections.emptyList(); } return this.baseMapper.getAppUserIdsBySiteIds(siteIds); } @Override public Long countCar(List brands) { return this.baseMapper.countCar(brands); } /** * 添加三方平台充电订单数据 * * @param query */ @Override public R addTripartitePlatformOrder(AddTripartitePlatformOrder query) { //检查当前枪是否是正在使用中 TChargingOrder one = this.getOne(new LambdaQueryWrapper().eq(TChargingOrder::getChargingGunId, query.getChargingGunId()) .in(TChargingOrder::getStatus, Arrays.asList(1, 2, 3)).eq(TChargingOrder::getDelFlag, 0)); if (null != one) { return R.fail("充电枪正在充电中,启动失败"); } //构建订单数据 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); TChargingOrder chargingOrder = new TChargingOrder(); String code = "CD" + Double.valueOf(Math.random() * 1000).intValue() + sdf.format(new Date()); chargingOrder.setCode(code); chargingOrder.setOrderType(1); chargingOrder.setOrderClassification(1); TChargingGun tChargingGun = chargingGunClient.getChargingGunById(query.getChargingGunId()).getData(); TChargingPile chargingPile = chargingPileClient.getChargingPileById(tChargingGun.getChargingPileId()).getData(); chargingOrder.setSiteId(tChargingGun.getSiteId()); chargingOrder.setChargingPileId(tChargingGun.getChargingPileId()); chargingOrder.setChargingGunId(query.getChargingGunId()); chargingOrder.setRechargePaymentType(query.getRechargePaymentType()); chargingOrder.setRechargePaymentStatus(2); chargingOrder.setRechargeAmount(query.getPaymentAmount()); chargingOrder.setVipDiscountAmount(BigDecimal.ZERO); chargingOrder.setOrderSource(2); Operator operator = operatorClient.getOperator(query.getOperatorId()).getData(); chargingOrder.setOperatorId(operator.getId()); chargingOrder.setAppUserId(operator.getId().longValue()); chargingOrder.setTripartitePlatformName(operator.getName()); chargingOrder.setStartChargeSeq(query.getStartChargeSeq()); //车辆信息 if (StringUtils.hasLength(query.getPlateNum())) { TAppUserCar appUserCar = appUserCarClient.getAppUserCarByLicensePlate(query.getPlateNum()).getData(); if (null != appUserCar) { chargingOrder.setAppUserCarId(appUserCar.getId()); } chargingOrder.setPlateNum(query.getPlateNum()); } chargingOrder.setTitle("【充电桩充电】" + chargingPile.getNumber() + "号桩/" + tChargingGun.getCode() + "号枪"); chargingOrder.setStatus(2); chargingOrder.setPayTime(LocalDateTime.now()); chargingOrder.setChargeAmount(chargingOrder.getRechargeAmount()); this.save(chargingOrder); Integer accountingStrategyId = tChargingGun.getAccountingStrategyId(); if (null == accountingStrategyId) { Site site = siteClient.getSiteByIds(Arrays.asList(tChargingGun.getSiteId())).getData().get(0); //查询站点上面的计费策略 accountingStrategyId = site.getAccountingStrategyId(); } TAccountingStrategy accountingStrategy = accountingStrategyClient.getAccountingStrategyById(accountingStrategyId).getData(); //添加订单的计费策略 List strategyDetailList = accountingStrategyDetailClient.getListByAccountingStrategyId(accountingStrategyId).getData(); AccountingStrategyOrder accountingStrategyOrder = new AccountingStrategyOrder(); BeanUtils.copyProperties(accountingStrategy, accountingStrategyOrder); accountingStrategyOrder.setChargingOrderId(chargingOrder.getId()); accountingStrategyOrderService.save(accountingStrategyOrder); List list1 = new ArrayList<>(); for (TAccountingStrategyDetail tAccountingStrategyDetail : strategyDetailList) { AccountingStrategyDetailOrder accountingStrategyDetailOrder = new AccountingStrategyDetailOrder(); BeanUtils.copyProperties(tAccountingStrategyDetail, accountingStrategyDetailOrder); accountingStrategyDetailOrder.setChargingOrderId(chargingOrder.getId()); accountingStrategyDetailOrder.setCreateTime(LocalDateTime.now()); accountingStrategyDetailOrder.setDelFlag(false); list1.add(accountingStrategyDetailOrder); } accountingStrategyDetailOrderService.saveBatch(list1); //添加安全检测数据到缓存中,每步安全检测完成后需要更新缓存数据 PreChargeCheck preChargeCheck = new PreChargeCheck(); preChargeCheck.setElectronicLockLock(false); preChargeCheck.setInsulationTesting(false); preChargeCheck.setSecureConnectionDetection(false); preChargeCheck.setStartupSuccess(1); preChargeCheck.setStartCommandSent(false); preChargeCheck.setAddTime(chargingOrder.getReservationTime() != null ? chargingOrder.getReservationTime() : LocalDateTime.now()); String key = "AQJC_" + chargingOrder.getChargingGunId(); redisService.setCacheObject(key, preChargeCheck, 24L, TimeUnit.HOURS); BigDecimal rechargeAmount = chargingOrder.getRechargeAmount(); //调用远程启动充电消息 PlatformStartCharging platformStartCharging = new PlatformStartCharging(); platformStartCharging.setTransaction_serial_number(chargingOrder.getCode()); platformStartCharging.setCharging_pile_code(chargingPile.getCode()); platformStartCharging.setCharging_gun_code(tChargingGun.getCode()); //使用订单id作为逻辑卡号 platformStartCharging.setCard_number(chargingOrder.getId().toString()); platformStartCharging.setAccount_balance(rechargeAmount); log.info(chargingOrder.getCode() + ":-------------------远程调起开始充电请求-------------------" + platformStartCharging.toString()); sendMessageClient.platformStartCharging(platformStartCharging); // //异步线程检测远程启动的应答结果。如果失败,则需要全额退款 // Long id = chargingOrder.getId(); // //执行5分钟的定时任务检测 // ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); // scheduler.scheduleAtFixedRate(()->{ // if(timingDetection(id)){ // scheduler.shutdown(); // } // }, 5, 1, TimeUnit.SECONDS); Site site = siteClient.getSiteByIds(Arrays.asList(chargingOrder.getSiteId())).getData().get(0); if (1 == site.getIsOpenSynchronization()) { //推送三方平台订单状态 tcecClient.notificationEquipChargeStatus(chargingOrder.getStartChargeSeq(), chargingOrder.getOperatorId()); } ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); threadPoolExecutor.execute(() -> { //推送成都监管 NotificationConnectorChargeStatusVo statusVo = new NotificationConnectorChargeStatusVo(); statusVo.setOrderCode(chargingOrder.getCode()); statusVo.setChargingGunId(chargingOrder.getChargingGunId()); chengDuJianGuanClient.notificationConnectorChargeStatus(statusVo); }); return R.ok(); } /** * 三方平台停止充电操作 * * @param startChargeSeq * @return */ @Override public R tripartitePlatformStopCharge(String startChargeSeq) { log.info("三方平台请求停止充电:" + startChargeSeq); TChargingOrder order = this.getOne(new LambdaQueryWrapper().eq(TChargingOrder::getStartChargeSeq, startChargeSeq)); Integer status = order.getStatus(); String code1 = order.getCode(); if (status != 3) { log.info("【" + code1 + "】三方平台请求停止充电失败:还未开始充电"); return R.fail("还未开始充电"); } if (status == 4 || status == 5) { log.info("【" + code1 + "】三方平台请求停止充电:不能重复操作"); return R.fail("不能重复操作"); } TChargingOrder chargingOrder = new TChargingOrder(); chargingOrder.setId(order.getId()); chargingOrder.setAppUserId(order.getOperatorId().longValue()); chargingOrder.setEndTime(LocalDateTime.now()); chargingOrder.setStatus(4); chargingOrder.setEndMode(1); this.updateById(chargingOrder); Site site = siteClient.getSiteByIds(Arrays.asList(order.getSiteId())).getData().get(0); if (1 == site.getIsOpenSynchronization()) { tcecClient.notificationEquipChargeStatus(order.getStartChargeSeq(), order.getOperatorId()); } TChargingGun chargingGun = chargingGunClient.getChargingGunById(order.getChargingGunId()).getData(); //异步线程处理停机 ExecutorService cachedThreadPool = Executors.newFixedThreadPool(1); cachedThreadPool.execute(() -> { //调用硬件停止充电,停止成功后开始计算费用退款 TChargingPile chargingPile = chargingPileClient.getChargingPileById(order.getChargingPileId()).getData(); PlatformStopCharging platformStopCharging = new PlatformStopCharging(); platformStopCharging.setCharging_pile_code(chargingPile.getCode()); platformStopCharging.setCharging_gun_code(chargingGun.getCode()); log.info("【" + code1 + "】三方平台请求停止充电发起消息:" + JSON.toJSONString(platformStopCharging)); sendMessageClient.platformStopCharging(platformStopCharging); log.info("【" + code1 + "】三方平台请求停止充电发起消息结束"); log.info(platformStopCharging.toString()); }); ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); threadPoolExecutor.execute(() -> { //推送成都监管 NotificationConnectorChargeStatusVo statusVo = new NotificationConnectorChargeStatusVo(); statusVo.setOrderCode(chargingOrder.getCode()); statusVo.setChargingGunId(chargingOrder.getChargingGunId()); chengDuJianGuanClient.notificationConnectorChargeStatus(statusVo); }); return R.ok(); } /** * 获取充电后没有扣除费用的数据 * * @return */ @Override public R getNotPaymentChargingOrder() { List list = this.list(new LambdaQueryWrapper().eq(TChargingOrder::getPaymentAmount, 0) .ne(TChargingOrder::getElectricity, 0).in(TChargingOrder::getStatus, Arrays.asList(4, 5))); List> mapList = new ArrayList<>(); for (TChargingOrder order : list) { TransactionRecord transactionRecord = transactionRecordService.findOne(order.getCode()); Map map = new HashMap<>(); map.put("order", order); map.put("transactionRecord", transactionRecord); mapList.add(map); } return R.ok(mapList); } /** * 手动推送订单给第三方平台 * * @param code * @return */ @Override public R pushOrderInfo(String code) { TChargingOrder chargingOrder = this.getOne(new LambdaQueryWrapper().eq(TChargingOrder::getCode, code)); // tcecClient.notificationEquipChargeStatus(chargingOrder.getStartChargeSeq(), chargingOrder.getOperatorId()); // tcecClient.notificationStopChargeResult(chargingOrder.getStartChargeSeq(), chargingOrder.getChargingGunId().toString(), // chargingOrder.getOperatorId()); return tcecClient.notificationChargeOrderInfo(chargingOrder.getStartChargeSeq(), chargingOrder.getOperatorId()); } public R addChargingOrderSummaryData() { List list3 = chargingOrderAccountingStrategyService.list(); List list1 = this.list(new LambdaQueryWrapper().eq(TChargingOrder::getDelFlag, 0)); for (TChargingOrder chargingOrderVO : list1) { if (null == chargingOrderVO.getStatus() || 5 != chargingOrderVO.getStatus()) { continue; } List list4 = list3.stream().filter(e -> e.getChargingOrderId().equals(chargingOrderVO.getId())).collect(Collectors.toList()); 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 = new BigDecimal("0"); BigDecimal paymentMoney = new BigDecimal("0"); if (!chargingOrderVO.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"))); } Long time = 0L; if (chargingOrderVO.getStartTime() != null && chargingOrderVO.getEndTime() != null) { LocalDateTime startTime = chargingOrderVO.getStartTime(); LocalDateTime endTime = chargingOrderVO.getEndTime(); // 计算时间差 单位秒 long between = ChronoUnit.SECONDS.between(startTime, endTime); time += between; } TChargingOrderSummaryData summaryData = new TChargingOrderSummaryData(); summaryData.setChargingOrderId(chargingOrderVO.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); } return R.ok(); } /** * 大屏-新能源汽车电池安全检测次数统计 */ @Override public R getStatisticsOfBattery(List siteIds) { String cacheKey = largeScreenCacheKey("battery", siteIds); StatisticsOfBatteryVO cached = redisService.getCacheObject(cacheKey); if (cached != null) { return R.ok(cached); } StatisticsOfBatteryVO vo = new StatisticsOfBatteryVO(); List effectiveSiteIds = siteIds == null ? Arrays.asList(25, 26) : siteIds; if (effectiveSiteIds.isEmpty()) { vo.setTotal(0); vo.setDates(Collections.emptyList()); vo.setCounts(Collections.emptyList()); return R.ok(vo); } // 快充和超充订单作为电池安全检测次数。 R> r = chargingGunClient.getGunIdsBySiteIdsAndChargeModels(effectiveSiteIds, Arrays.asList(1, 2)); if (r.getCode() != 200) { return R.ok(vo); } List gunIds = r.getData(); // 统计: gunIds 分批查询, 避免超大 IN 列表撑爆 MyCat 规划器 Long total = 0L; if (gunIds != null && !gunIds.isEmpty()) { for (List chunk : chunkList(gunIds, 500)) { Long chunkCount = this.baseMapper.selectCount(new LambdaQueryWrapper() .eq(TChargingOrder::getDelFlag, 0)//未删除 .in(TChargingOrder::getStatus, Arrays.asList(3, 5))//充电中、已结束 .in(TChargingOrder::getChargingGunId, chunk));//符合条件的充电枪 total += chunkCount == null ? 0L : chunkCount; } } vo.setTotal(total.intValue()); List dates = new ArrayList<>(); List counts = new ArrayList<>(); LocalDate today = LocalDate.now(); for (int i = 7; i >= 1; i--) {//升序 LocalDate day = today.minusDays(i); LocalDateTime dayStart = day.atStartOfDay(); LocalDateTime dayEnd = day.plusDays(1).atStartOfDay(); long dayCount = 0L; if (gunIds != null && !gunIds.isEmpty()) { for (List chunk : chunkList(gunIds, 500)) { Long chunkCount = this.baseMapper.countOrdersInRangeByGunIds(dayStart, dayEnd, chunk); dayCount += chunkCount == null ? 0L : chunkCount; } } dates.add(day); counts.add((int) dayCount); } vo.setDates(dates); vo.setCounts(counts); redisService.setCacheObject(cacheKey, vo, LARGE_SCREEN_CACHE_SECONDS, TimeUnit.SECONDS); return R.ok(vo); } /** * 列表分批, 控制单条 SQL 的 IN 列表规模 */ private List> chunkList(List list, int size) { List> chunks = new ArrayList<>(); for (int i = 0; i < list.size(); i += size) { chunks.add(list.subList(i, Math.min(i + size, list.size()))); } return chunks; } @Override public Double getSumDegreeBySiteIds(List siteIds) { Double sumDegreeBySiteIds = this.baseMapper.getSumDegreeBySiteIds(siteIds); return sumDegreeBySiteIds; } /** * 获取启动失败且时间超过1小时的订单 * * @return */ @Override public List findFailedStartupOrder() { return this.baseMapper.findFailedStartupOrder(); } /** * 获取停止中的数据 * * @return */ @Override public List findStoppedOrder() { return this.baseMapper.findStoppedOrder(); } /** * 获取充电中的数据 * * @return */ @Override public List findChargingOrder() { return this.baseMapper.findChargingOrder(); } /** * 获取已结束的数据 * * @return */ @Override public List findEndChargingOrder() { return this.baseMapper.findEndChargingOrder(); } /** * 获取需要手动修改订单状态的数据 * * @return */ @Override public List findUpStatusChargingOrder() { return this.baseMapper.findUpStatusChargingOrder(); } /** * 查询充电算账单数据 * * @param startTime * @param endTime */ @Override public List> getChargingSettlementBill(String startTime, String endTime, Integer dateType) { return this.baseMapper.getChargingSettlementBill(startTime, endTime, dateType); } /** * 查询账户结算账单数据 * * @param startTime * @param endTime * @return */ @Override public List> getAccountSettlementStatement(String startTime, String endTime) { return this.baseMapper.getAccountSettlementStatement(startTime, endTime); } /** * 手动处理退款数据 * * @param refund_out_trade_no 退款商户单号 */ @Override public void orderQueryRefund(Long orderId, String refund_out_trade_no) { TChargingOrder chargingOrder = this.getById(orderId); BigDecimal rechargeAmount = chargingOrder.getRechargeAmount(); List orderRefunds = chargingOrderRefundService.list(new QueryWrapper().eq("charging_order_id", orderId)); List collect = orderRefunds.stream().map(TChargingOrderRefund::getRefundCode).collect(Collectors.toList()); if (!collect.contains(refund_out_trade_no)) { if (1 == chargingOrder.getRechargePaymentType()) { Map data = wxPaymentClient.queryRefund(refund_out_trade_no).getData(); if (null == data) { throw new ServiceException("退款查询失败"); } log.info("退款查询结果:{}", JSON.toJSONString(data)); Map amount = (Map) data.get("amount"); TChargingOrderRefund chargingOrderRefund = new TChargingOrderRefund(); chargingOrderRefund.setChargingOrderId(chargingOrder.getId()); chargingOrderRefund.setRefundCode(refund_out_trade_no); chargingOrderRefund.setRefundAmount(new BigDecimal(amount.get("refund").toString()).divide(new BigDecimal(100))); chargingOrderRefund.setRefundStatus(2); chargingOrderRefund.setPayType(1); chargingOrderRefund.setRefundSerialNumber(data.get("refund_id").toString()); String success_time = data.get("success_time").toString(); success_time = success_time.replace("T", " "); success_time = success_time.substring(0, success_time.lastIndexOf("+")); chargingOrderRefund.setRefundTime(LocalDateTime.parse(success_time, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); chargingOrderRefund.setCode(chargingOrder.getCode()); chargingOrderRefund.setRefundTitle(rechargeAmount.compareTo(chargingOrderRefund.getRefundAmount()) == 0 ? "充电失败" : "充电完成退款"); chargingOrderRefund.setRefundContent(rechargeAmount.compareTo(chargingOrderRefund.getRefundAmount()) == 0 ? "充电失败" : "充电完成退款"); chargingOrderRefund.setRefundReason(rechargeAmount.compareTo(chargingOrderRefund.getRefundAmount()) == 0 ? "充电失败" : "充电完成退款"); chargingOrderRefund.setRefundRemark(rechargeAmount.compareTo(chargingOrderRefund.getRefundAmount()) == 0 ? "充电失败" : "充电完成退款"); chargingOrderRefund.setRefundTotalAmount(new BigDecimal(amount.get("refund").toString()).divide(new BigDecimal(100))); chargingOrderRefund.setPayAmount(rechargeAmount); chargingOrderRefund.setCreateTime(chargingOrderRefund.getRefundTime()); chargingOrderRefundService.save(chargingOrderRefund); if (rechargeAmount.compareTo(chargingOrderRefund.getRefundAmount()) == 0) { chargingOrder.setStatus(-1); } else { chargingOrder.setStatus(5); } this.updateById(chargingOrder); } if (2 == chargingOrder.getRechargePaymentType()) { QueryRefundReq dto = new QueryRefundReq(); dto.setOutTradeNo(refund_out_trade_no); QueryRefundResp data = aliPaymentClient.queryRefund(dto).getData(); if (null == data) { throw new ServiceException("退款查询失败"); } TChargingOrderRefund chargingOrderRefund = new TChargingOrderRefund(); chargingOrderRefund.setChargingOrderId(chargingOrder.getId()); chargingOrderRefund.setRefundCode(refund_out_trade_no); chargingOrderRefund.setRefundAmount(new BigDecimal(data.getRefundAmount())); chargingOrderRefund.setRefundStatus(2); chargingOrderRefund.setPayType(1); chargingOrderRefund.setRefundSerialNumber(data.getOutRequestNo()); chargingOrderRefund.setRefundTime(LocalDateTime.parse(data.getRefundTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); chargingOrderRefund.setCode(chargingOrder.getCode()); chargingOrderRefund.setRefundTitle(rechargeAmount.compareTo(chargingOrderRefund.getRefundAmount()) == 0 ? "充电失败" : "充电完成退款"); chargingOrderRefund.setRefundContent(rechargeAmount.compareTo(chargingOrderRefund.getRefundAmount()) == 0 ? "充电失败" : "充电完成退款"); chargingOrderRefund.setRefundReason(rechargeAmount.compareTo(chargingOrderRefund.getRefundAmount()) == 0 ? "充电失败" : "充电完成退款"); chargingOrderRefund.setRefundRemark(rechargeAmount.compareTo(chargingOrderRefund.getRefundAmount()) == 0 ? "充电失败" : "充电完成退款"); chargingOrderRefund.setRefundTotalAmount(new BigDecimal(data.getRefundAmount())); chargingOrderRefund.setPayAmount(rechargeAmount); chargingOrderRefund.setCreateTime(chargingOrderRefund.getRefundTime()); chargingOrderRefundService.save(chargingOrderRefund); if (rechargeAmount.compareTo(chargingOrderRefund.getRefundAmount()) == 0) { chargingOrder.setStatus(-1); } else { chargingOrder.setStatus(5); } this.updateById(chargingOrder); } } } /** * 手动添加完善订单费用明细 * * @param orderId */ @Override public void addOrderExpenseBreakdown(Long orderId) { try { TChargingOrder chargingOrder = this.getById(orderId); TransactionRecord one = transactionRecordService.findOne(chargingOrder.getCode()); if (null != one) { TransactionRecordMessageVO vo = new TransactionRecordMessageVO(); BeanUtils.copyProperties(one, vo); //如果使用优惠券需要判断优惠券是否满足使用条件 //退款金额=优惠券金额+剩余充电金额 BigDecimal periodElectricPrice_total = BigDecimal.ZERO; BigDecimal periodServicePrice_total = BigDecimal.ZERO; BigDecimal vipDiscountAmount_total = BigDecimal.ZERO; BigDecimal serviceCharge_total = BigDecimal.ZERO; BigDecimal total = BigDecimal.ZERO; //获取订单的计费策略 List accountingStrategyDetailOrderList = accountingStrategyDetailOrderClient.getAllAccountingStrategyDetailOrder(chargingOrder.getId()).getData(); if (accountingStrategyDetailOrderList.isEmpty()) { throw new ServiceException("计费策略为空,无法计算费用"); } accountingStrategyDetailOrderList.get(accountingStrategyDetailOrderList.size() - 1).setEndTime("23:59"); //开始处理计费明细数据和优惠数据 chargingOrderAccountingStrategyService.remove(new LambdaQueryWrapper().eq(TChargingOrderAccountingStrategy::getChargingOrderId, chargingOrder.getId())); SimpleDateFormat sdfs = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); List lists = new ArrayList<>(); List dataList = uploadRealTimeMonitoringDataService.getDataByOrderCode(chargingOrder.getCode()); if (!dataList.isEmpty()) { vo.setStart_time(sdfs.format(dataList.get(dataList.size() - 1).getCreate_time()) + ".00"); vo.setEnd_time(sdfs.format(dataList.get(0).getCreate_time()) + ".00"); } //跨天 if (!vo.getStart_time().split(" ")[0].equals(vo.getEnd_time().split(" ")[0])) { //当天的 //开始通过计费策略遍历解析每个时段的费用明细 String start_time = vo.getStart_time(); String end_time = vo.getEnd_time(); long start = sdfs.parse(start_time).getTime(); long end = sdfs.parse(end_time).getTime(); Class clazz = vo.getClass(); /** * "start_time": "2025-01-03 23:06:43.00", * "end_time": "2025-01-04 00:39:39.00", */ for (int i = accountingStrategyDetailOrderList.size(); i > 0; i--) { AccountingStrategyDetailOrder strategyDetail = accountingStrategyDetailOrderList.get(i - 1); long time = sdfs.parse(start_time.split(" ")[0] + " " + strategyDetail.getEndTime() + ":00").getTime(); if (time < start) { break; } //阶段(1=尖阶段,2=峰阶段,3=平阶段,4=谷阶段) Object invoke = null; switch (strategyDetail.getType()) { case 1: //充电度数 invoke = clazz.getMethod("getSpike_charge" + (i)).invoke(vo); break; case 2: //充电度数 invoke = clazz.getMethod("getPeak_charge" + (i)).invoke(vo); break; case 3: //充电度数 invoke = clazz.getMethod("getFlat_charge" + (i)).invoke(vo); break; case 4: //充电度数 invoke = clazz.getMethod("getValley_charge" + (i)).invoke(vo); break; } if (null == invoke || Double.valueOf(invoke.toString()) == 0) { continue; } BigDecimal sharp_peak_charge = new BigDecimal(invoke.toString()); AccountingStrategyDetailOrderVo vo1 = new AccountingStrategyDetailOrderVo(); BeanUtils.copyProperties(strategyDetail, vo1); vo1.setChargingCapacity(sharp_peak_charge); lists.add(0, vo1); } //第二天的 //开始通过计费策略遍历解析每个时段的费用明细 for (int i = 0; i < accountingStrategyDetailOrderList.size(); i++) { AccountingStrategyDetailOrder strategyDetail = accountingStrategyDetailOrderList.get(i); long time = sdfs.parse(end_time.split(" ")[0] + " " + strategyDetail.getStartTime() + ":00").getTime(); if (time > end) { break; } //阶段(1=尖阶段,2=峰阶段,3=平阶段,4=谷阶段) Object invoke = null; switch (strategyDetail.getType()) { case 1: //充电度数 invoke = clazz.getMethod("getSpike_charge" + (i + 1)).invoke(vo); break; case 2: //充电度数 invoke = clazz.getMethod("getPeak_charge" + (i + 1)).invoke(vo); break; case 3: //充电度数 invoke = clazz.getMethod("getFlat_charge" + (i + 1)).invoke(vo); break; case 4: //充电度数 invoke = clazz.getMethod("getValley_charge" + (i + 1)).invoke(vo); break; } if (null == invoke || Double.valueOf(invoke.toString()) == 0) { continue; } BigDecimal sharp_peak_charge = new BigDecimal(invoke.toString()); AccountingStrategyDetailOrderVo vo1 = new AccountingStrategyDetailOrderVo(); BeanUtils.copyProperties(strategyDetail, vo1); vo1.setChargingCapacity(sharp_peak_charge); lists.add(vo1); } } else { for (int i = 0; i < accountingStrategyDetailOrderList.size(); i++) { Class clazz = vo.getClass(); AccountingStrategyDetailOrder strategyDetail = accountingStrategyDetailOrderList.get(i); //阶段(1=尖阶段,2=峰阶段,3=平阶段,4=谷阶段) Object invoke = null; switch (strategyDetail.getType()) { case 1: //充电度数 invoke = clazz.getMethod("getSpike_charge" + (i + 1)).invoke(vo); break; case 2: //充电度数 invoke = clazz.getMethod("getPeak_charge" + (i + 1)).invoke(vo); break; case 3: //充电度数 invoke = clazz.getMethod("getFlat_charge" + (i + 1)).invoke(vo); break; case 4: //充电度数 invoke = clazz.getMethod("getValley_charge" + (i + 1)).invoke(vo); break; } if (null == invoke || Double.valueOf(invoke.toString()) == 0) { continue; } BigDecimal sharp_peak_charge = new BigDecimal(invoke.toString()); AccountingStrategyDetailOrderVo vo1 = new AccountingStrategyDetailOrderVo(); BeanUtils.copyProperties(strategyDetail, vo1); vo1.setChargingCapacity(sharp_peak_charge); lists.add(vo1); } } //开始处理明细 for (int i = 0; i < lists.size(); i++) { AccountingStrategyDetailOrderVo strategyDetail = lists.get(i); BigDecimal sharp_peak_charge = strategyDetail.getChargingCapacity(); TChargingOrderAccountingStrategy chargingOrderAccountingStrategy = new TChargingOrderAccountingStrategy(); chargingOrderAccountingStrategy.setChargingOrderId(chargingOrder.getId()); chargingOrderAccountingStrategy.setAccountingStrategyDetailId(strategyDetail.getId()); chargingOrderAccountingStrategy.setType(strategyDetail.getType()); chargingOrderAccountingStrategy.setElectrovalence(strategyDetail.getElectrovalence()); chargingOrderAccountingStrategy.setServiceCharge(strategyDetail.getServiceCharge()); chargingOrderAccountingStrategy.setCostServiceCharge(strategyDetail.getCostServiceCharge()); if (i == 0) { String time = vo.getStart_time().split(" ")[1]; chargingOrderAccountingStrategy.setStartTime(time.substring(0, time.lastIndexOf(":"))); } else { chargingOrderAccountingStrategy.setStartTime(strategyDetail.getStartTime()); } if (i == lists.size() - 1) { String time = vo.getEnd_time().split(" ")[1]; chargingOrderAccountingStrategy.setEndTime(time.substring(0, time.lastIndexOf(":"))); } else { chargingOrderAccountingStrategy.setEndTime(strategyDetail.getEndTime()); } //已充电总度数 BigDecimal electrovalenc = strategyDetail.getElectrovalence().multiply(sharp_peak_charge); BigDecimal originalServicePrice = strategyDetail.getServiceCharge().multiply(sharp_peak_charge); BigDecimal serviceCharge = originalServicePrice; BigDecimal vipDiscountAmount = BigDecimal.ZERO; //计算优惠金额 if (null != chargingOrder.getVipDiscount()) { //0.58折 vipDiscountAmount = serviceCharge.multiply(new BigDecimal(1).subtract(chargingOrder.getVipDiscount())); serviceCharge = serviceCharge.multiply(chargingOrder.getVipDiscount()); } chargingOrderAccountingStrategy.setChargingCapacity(sharp_peak_charge); 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()); chargingOrderAccountingStrategyService.save(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)); } BigDecimal orderAmount = BigDecimal.valueOf(total.doubleValue()); //原金额 BigDecimal rechargeAmount = chargingOrder.getRechargeAmount(); //支付金额 BigDecimal payAmount = periodElectricPrice_total.add(serviceCharge_total); //退款金额=真实预付金额-实际支付金额;兼容曾在预付阶段错误抵扣优惠券的在途订单。 BigDecimal settlementPrepayment = ChargingCouponSupport.resolveSettlementPrepayment( rechargeAmount, chargingOrder.getBalancePayAmount(), chargingOrder.getThirdPayAmount(), chargingOrder.getCouponDiscountAmount()); BigDecimal refundAmount = settlementPrepayment.subtract(payAmount); UploadRealTimeMonitoringData uploadRealTimeMonitoringData = uploadRealTimeMonitoringDataService.getLastDataById(chargingOrder.getCode()); if (null != uploadRealTimeMonitoringData && null == chargingOrder.getEndMode()) { Integer soc = uploadRealTimeMonitoringData.getSoc(); if (soc >= 98) { chargingOrder.setEndMode(2); } else if (null != chargingOrder.getResidualAmount() && chargingOrder.getResidualAmount().compareTo(new BigDecimal(1)) <= 0) { chargingOrder.setEndMode(3); } else { chargingOrder.setEndMode(1); } } chargingOrder.setResidualAmount(rechargeAmount.subtract(total).setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setStartTime(LocalDateTime.parse(vo.getStart_time(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SS"))); chargingOrder.setEndTime(LocalDateTime.parse(vo.getEnd_time(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SS"))); chargingOrder.setStatus(5); chargingOrder.setOrderAmount(orderAmount.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setElectrovalence(periodElectricPrice_total.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setChargingCapacity(vo.getTotal_electricity()); chargingOrder.setElectricity(vo.getTotal_electricity()); chargingOrder.setVipDiscountAmount(vipDiscountAmount_total); if (vipDiscountAmount_total.compareTo(BigDecimal.ZERO) == 0) { chargingOrder.setVipDiscount(BigDecimal.valueOf(1)); } //计算优惠券 BigDecimal couponDiscount = BigDecimal.ZERO; if (null != chargingOrder.getAppCouponId()) { Long appCouponId = chargingOrder.getAppCouponId(); TAppCoupon appCoupon = appCouponClient.getAppCouponById(chargingOrder.getAppCouponId()).getData(); String couponJson = appCoupon.getCouponJson(); TCoupon tCoupon = JSON.parseObject(couponJson, TCoupon.class); couponDiscount = ChargingCouponSupport.calculateSettlementDiscount( tCoupon, payAmount, serviceCharge_total); if (couponDiscount.compareTo(BigDecimal.ZERO) > 0) { appCoupon.setStatus(2); appCouponClient.updateAppCoupon(appCoupon); } else { appCouponClient.refund(appCouponId.toString()); chargingOrder.setAppCouponId(null); chargingOrder.setCouponDiscountAmount(BigDecimal.ZERO); } } //优惠券优惠金额 couponDiscount = couponDiscount.setScale(4, RoundingMode.HALF_EVEN); refundAmount = refundAmount.add(couponDiscount); //实际支付金额-优惠券 payAmount = payAmount.subtract(couponDiscount).max(BigDecimal.ZERO); serviceCharge_total = serviceCharge_total.subtract(couponDiscount).max(BigDecimal.ZERO); chargingOrder.setCouponDiscountAmount(couponDiscount.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setServiceCharge(serviceCharge_total.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setPaymentAmount(payAmount.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setRefundAmount(refundAmount.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setRefundStatus(1); UploadRealTimeMonitoringData asc = uploadRealTimeMonitoringDataService.getDataByOrderCode(chargingOrder.getCode(), "asc"); UploadRealTimeMonitoringData desc = uploadRealTimeMonitoringDataService.getDataByOrderCode(chargingOrder.getCode(), "desc"); if (null != asc) { chargingOrder.setStartSoc(new BigDecimal(asc.getSoc())); } if (null != desc) { chargingOrder.setEndSoc(new BigDecimal(desc.getSoc())); } this.updateById(chargingOrder); } else { List dataList = uploadRealTimeMonitoringDataService.getDataByOrderCode(chargingOrder.getCode()); if (!dataList.isEmpty()) { //获取当前订单的计费规则,然后分段计算总的度数和充电金额 List detailOrders = accountingStrategyDetailOrderService.list(new LambdaQueryWrapper() .eq(AccountingStrategyDetailOrder::getChargingOrderId, chargingOrder.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 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()); } } SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMMddHHmm"); List 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 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() { @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(chargingOrder.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 electrovalenc = detailOrder.getElectrovalence().multiply(subtract); BigDecimal originalServicePrice = detailOrder.getServiceCharge().multiply(subtract); BigDecimal serviceCharge = originalServicePrice; BigDecimal vipDiscountAmount = BigDecimal.ZERO; //计算优惠金额 if (null != chargingOrder.getVipDiscount()) { //0.58折 vipDiscountAmount = serviceCharge.multiply(new BigDecimal(1).subtract(chargingOrder.getVipDiscount())); serviceCharge = serviceCharge.multiply(chargingOrder.getVipDiscount()); } 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 = chargingOrder.getRechargeAmount(); //支付金额 BigDecimal payAmount = periodElectricPrice_total.add(serviceCharge_total); //退款金额=真实预付金额-实际支付金额;兼容曾在预付阶段错误抵扣优惠券的在途订单。 BigDecimal settlementPrepayment = ChargingCouponSupport.resolveSettlementPrepayment( rechargeAmount, chargingOrder.getBalancePayAmount(), chargingOrder.getThirdPayAmount(), chargingOrder.getCouponDiscountAmount()); BigDecimal refundAmount = settlementPrepayment.subtract(payAmount); UploadRealTimeMonitoringData uploadRealTimeMonitoringData = uploadRealTimeMonitoringDataService.getLastDataById(chargingOrder.getCode()); if (null != uploadRealTimeMonitoringData && null == chargingOrder.getEndMode()) { Integer soc = uploadRealTimeMonitoringData.getSoc(); if (soc >= 98) { chargingOrder.setEndMode(2); } else if (null != chargingOrder.getResidualAmount() && chargingOrder.getResidualAmount().compareTo(new BigDecimal(1)) <= 0) { chargingOrder.setEndMode(3); } else { chargingOrder.setEndMode(1); } } chargingOrder.setResidualAmount(rechargeAmount.subtract(total).setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setStartTime(list.get(0).getCreateTime()); chargingOrder.setEndTime(list.get(list.size() - 1).getCreateTime()); chargingOrder.setStatus(5); chargingOrder.setOrderAmount(orderAmount.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setElectrovalence(periodElectricPrice_total.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setChargingCapacity(list.get(list.size() - 1).getChargingCapacity()); chargingOrder.setElectricity(list.get(list.size() - 1).getChargingCapacity()); chargingOrder.setVipDiscountAmount(vipDiscountAmount_total); if (vipDiscountAmount_total.compareTo(BigDecimal.ZERO) == 0) { chargingOrder.setVipDiscount(BigDecimal.valueOf(1)); } //计算优惠券 BigDecimal couponDiscount = BigDecimal.ZERO; if (null != chargingOrder.getAppCouponId()) { Long appCouponId = chargingOrder.getAppCouponId(); TAppCoupon appCoupon = appCouponClient.getAppCouponById(chargingOrder.getAppCouponId()).getData(); String couponJson = appCoupon.getCouponJson(); TCoupon tCoupon = com.alibaba.fastjson2.JSON.parseObject(couponJson, TCoupon.class); couponDiscount = ChargingCouponSupport.calculateSettlementDiscount( tCoupon, payAmount, serviceCharge_total); if (couponDiscount.compareTo(BigDecimal.ZERO) > 0) { appCoupon.setStatus(2); appCouponClient.updateAppCoupon(appCoupon); } else { appCouponClient.refund(appCouponId.toString()); chargingOrder.setAppCouponId(null); chargingOrder.setCouponDiscountAmount(BigDecimal.ZERO); } } //优惠券优惠金额 couponDiscount = couponDiscount.setScale(4, RoundingMode.HALF_EVEN); //退款金额+优惠券 refundAmount = refundAmount.add(couponDiscount); //实际支付金额-优惠券 payAmount = payAmount.subtract(couponDiscount).max(BigDecimal.ZERO); serviceCharge_total = serviceCharge_total.subtract(couponDiscount).max(BigDecimal.ZERO); chargingOrder.setCouponDiscountAmount(couponDiscount.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setServiceCharge(serviceCharge_total.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setPaymentAmount(payAmount.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setRefundAmount(refundAmount.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setRefundStatus(1); UploadRealTimeMonitoringData asc = uploadRealTimeMonitoringDataService.getDataByOrderCode(chargingOrder.getCode(), "asc"); UploadRealTimeMonitoringData desc = uploadRealTimeMonitoringDataService.getDataByOrderCode(chargingOrder.getCode(), "desc"); if (null != asc) { chargingOrder.setStartSoc(new BigDecimal(asc.getSoc())); } if (null != desc) { chargingOrder.setEndSoc(new BigDecimal(desc.getSoc())); } chargingOrderService.updateById(chargingOrder); } } } catch (Exception e) { 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 String buildIcbcPaymentFailReason(IcbcPaymentResp resp) { if (resp == null) { return "工行支付服务未返回支付数据"; } String reason = firstNotBlank(resp.getRawResponse(), resp.getStatus()); if (!StringUtils.hasLength(reason)) { return "工行支付服务返回数据不完整"; } if (reason.length() > 500) { return reason.substring(0, 500); } return reason; } private void triggerChargingOrderShare(TChargingOrder chargingOrder, Site site) { if (!isNeedChargingOrderShare(chargingOrder)) { return; } if (TransactionSynchronizationManager.isActualTransactionActive() && TransactionSynchronizationManager.isSynchronizationActive()) { Long chargingOrderId = chargingOrder.getId(); Integer siteId = chargingOrder.getSiteId(); String resourceKey = SHARE_AFTER_COMMIT_RESOURCE_PREFIX + chargingOrderId; if (TransactionSynchronizationManager.hasResource(resourceKey)) { return; } TransactionSynchronizationManager.bindResource(resourceKey, Boolean.TRUE); try { TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public void afterCommit() { executeChargingOrderShareAfterCommit(chargingOrderId, siteId); } @Override public void afterCompletion(int status) { if (TransactionSynchronizationManager.hasResource(resourceKey)) { TransactionSynchronizationManager.unbindResource(resourceKey); } } }); } catch (RuntimeException e) { TransactionSynchronizationManager.unbindResourceIfPossible(resourceKey); throw e; } log.info("充电订单分账已登记为事务提交后执行,订单号:{}", chargingOrder.getCode()); return; } executeChargingOrderShare(chargingOrder, site); } private void executeChargingOrderShareAfterCommit(Long chargingOrderId, Integer siteId) { try { TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); transactionTemplate.execute(status -> { TChargingOrder latestOrder = chargingOrderId == null ? null : this.getById(chargingOrderId); Site latestSite = loadChargingOrderSite(siteId); executeChargingOrderShare(latestOrder, latestSite); return null; }); } catch (Exception e) { log.error("事务提交后执行充电订单分账失败,订单ID:{}", chargingOrderId, e); } } /** * 物理删除旧的非成功/处理中的分账记录,为退款后重新计算分账做准备。 * 仅删除 onlineShareStatus 不是 SUCCESS 或 PROCESSING 的记录, * 已成功线上分账的记录不可删除,避免重复分账。 *

* 注意:采用物理删除而非软删除,因为表唯一键 uk_order_channel (charging_order_code, pay_channel) * 不包含 del_flag 字段,软删除后的记录仍占据唯一键,导致退款后无法插入同订单同渠道的新分账记录。 */ private void softDeleteStaleChargingOrderShareRecords(TChargingOrder chargingOrder) { if (chargingOrder == null || !StringUtils.hasLength(chargingOrder.getCode())) { return; } // 使用自定义 Mapper 方法执行原生 DELETE,绕过 MyBatis-Plus @TableLogic 逻辑删除拦截 int count = chargingOrderShareRecordMapper.physicalDeleteStaleByOrderCode(chargingOrder.getCode()); if (count > 0) { log.info("物理删除充电订单旧分账记录,订单号:{},删除数量:{}", chargingOrder.getCode(), count); } } private void executeChargingOrderShare(TChargingOrder chargingOrder, Site site) { try { if (!isNeedChargingOrderShare(chargingOrder)) { return; } // 先软删除旧的非成功分账记录,再基于剩余支付金额重新计算 softDeleteStaleChargingOrderShareRecords(chargingOrder); Partner partner = loadPartner(site); for (TChargingOrderShareRecord shareRecord : buildChargingOrderShareRecords(chargingOrder, site, partner)) { TChargingOrderShareRecord exist = chargingOrderShareRecordMapper.selectOne(new LambdaQueryWrapper() .eq(TChargingOrderShareRecord::getChargingOrderCode, shareRecord.getChargingOrderCode()) .eq(TChargingOrderShareRecord::getPayChannel, shareRecord.getPayChannel()) .eq(TChargingOrderShareRecord::getDelFlag, false) .last("limit 1")); if (exist != null) { // 已有成功/处理中的线上分账记录,跳过不重复处理 continue; } 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; } if (TransactionSynchronizationManager.isActualTransactionActive() && TransactionSynchronizationManager.isSynchronizationActive()) { triggerChargingOrderShare(chargingOrder, null); return; } Site site = loadChargingOrderSite(chargingOrder.getSiteId()); triggerChargingOrderShare(chargingOrder, site); } catch (Exception e) { log.error("充电订单分账站点加载失败,订单号:{}", chargingOrder == null ? null : chargingOrder.getCode(), e); } } private Site loadChargingOrderSite(Integer siteId) { if (siteId == null) { return null; } R> siteResult = siteClient.getSiteByIds(Collections.singletonList(siteId)); List sites = siteResult == null ? null : siteResult.getData(); return sites == null || sites.isEmpty() ? null : sites.get(0); } private boolean isNeedChargingOrderShare(TChargingOrder chargingOrder) { return chargingOrder != null && Integer.valueOf(2).equals(chargingOrder.getRechargePaymentStatus()) && (Integer.valueOf(2).equals(chargingOrder.getRefundStatus()) || Integer.valueOf(5).equals(chargingOrder.getStatus())) && getChargingOrderShareBaseAmount(chargingOrder).compareTo(BigDecimal.ZERO) > 0; } private Partner loadPartner(Site site) { if (site == null || site.getPartnerId() == null) { return null; } R partnerResult = partnerClient.getPartnerById(site.getPartnerId()); return partnerResult == null ? null : partnerResult.getData(); } private List 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 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)); 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()); } boolean needOnlineShare = shouldProcessIcbcOnlineShare(icbcOnlineShareEnabled, payChannel, record.getShareAmount(), partner); record.setNeedOnlineShare(needOnlineShare); record.setOnlineShareStatus(needOnlineShare ? "INIT" : "RECORDED"); if ("ICBC".equals(payChannel) && record.getShareAmount().compareTo(BigDecimal.ZERO) > 0 && !needOnlineShare) { record.setFailReason(icbcOnlineShareEnabled ? "未配置完整工行二级商户参数,仅记录本地分账" : "工行线上分账已关闭,仅记录本地分账"); } 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> rows = this.baseMapper.exchangeCodeBalanceIncomeByOrderIds(Collections.singletonList(chargingOrder.getId())); if (rows == null || rows.isEmpty()) { return new BalanceShareAmount(fallbackBalancePay, BigDecimal.ZERO); } Map 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); BigDecimal[] normalized = normalizeBalanceShareAmounts(fallbackBalancePay, principalAmount, giftAmount); return new BalanceShareAmount(normalized[0], normalized[1]); } /** * 余额退款流水可能在跨服务事务提交前暂时不可见,按订单净余额约束本金和赠送金额总和。 */ static BigDecimal[] normalizeBalanceShareAmounts(BigDecimal netBalanceAmount, BigDecimal principalAmount, BigDecimal giftAmount) { BigDecimal netAmount = netBalanceAmount == null ? BigDecimal.ZERO : netBalanceAmount.max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP); BigDecimal principal = principalAmount == null ? BigDecimal.ZERO : principalAmount.max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP); BigDecimal gift = giftAmount == null ? BigDecimal.ZERO : giftAmount.max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP); BigDecimal recordedTotal = principal.add(gift).setScale(2, RoundingMode.HALF_UP); if (netAmount.compareTo(BigDecimal.ZERO) <= 0) { return new BigDecimal[]{BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP), BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)}; } if (recordedTotal.compareTo(BigDecimal.ZERO) <= 0) { return new BigDecimal[]{netAmount, BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)}; } BigDecimal normalizedPrincipal = netAmount.multiply(principal) .divide(recordedTotal, 2, RoundingMode.HALF_UP); BigDecimal normalizedGift = netAmount.subtract(normalizedPrincipal).setScale(2, RoundingMode.HALF_UP); return new BigDecimal[]{normalizedPrincipal, normalizedGift}; } private BigDecimal getChargingOrderShareElectricityAmount(TChargingOrder chargingOrder) { 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 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() .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); } static boolean shouldProcessIcbcOnlineShare(String payChannel, BigDecimal shareAmount, Partner partner) { return shouldProcessIcbcOnlineShare(true, payChannel, shareAmount, partner); } static boolean shouldProcessIcbcOnlineShare(boolean onlineShareEnabled, String payChannel, BigDecimal shareAmount, Partner partner) { return onlineShareEnabled && "ICBC".equals(payChannel) && shareAmount != null && shareAmount.compareTo(BigDecimal.ZERO) > 0 && partner != null && StringUtils.hasLength(partner.getIcbcSubMerId()) && StringUtils.hasLength(partner.getIcbcSubMerPrtclNo()); } private IcbcSubOrderSplitReq buildIcbcSubOrderSplitReq(TChargingOrderShareRecord record) { IcbcSubOrderSplitReq req = new IcbcSubOrderSplitReq(); req.setChargingOrderCode(record.getChargingOrderCode()); req.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.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 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()); 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 ""; } }