| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797 |
- package com.zsElectric.boot.business.service.impl;
- import cn.hutool.core.util.ObjectUtil;
- import cn.hutool.core.util.StrUtil;
- import com.baomidou.mybatisplus.core.metadata.IPage;
- import com.baomidou.mybatisplus.core.toolkit.Assert;
- import com.baomidou.mybatisplus.core.toolkit.Wrappers;
- import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.fasterxml.jackson.core.JsonProcessingException;
- import com.zsElectric.boot.business.converter.ChargeOrderInfoConverter;
- import com.zsElectric.boot.business.mapper.*;
- import com.zsElectric.boot.business.mapper.ThirdPartyStationInfoMapper;
- import com.zsElectric.boot.business.model.dto.ChargeOrderInfoExportDTO;
- import com.zsElectric.boot.business.model.entity.*;
- import com.zsElectric.boot.business.model.form.ChargeOrderInfoForm;
- import com.zsElectric.boot.business.model.form.applet.AppInvokeChargeForm;
- import com.zsElectric.boot.business.model.form.applet.AppStopChargeForm;
- import com.zsElectric.boot.business.model.query.ChargeOrderInfoQuery;
- import com.zsElectric.boot.business.model.query.applet.AppChargeOrderInfoQuery;
- import com.zsElectric.boot.business.model.vo.ChargeOrderInfoVO;
- import com.zsElectric.boot.business.model.vo.UserVehicleVO;
- import com.zsElectric.boot.business.model.vo.applet.AppChargeVO;
- import com.zsElectric.boot.business.model.vo.applet.AppUserInfoVO;
- import com.zsElectric.boot.business.service.AppletHomeService;
- import com.zsElectric.boot.business.service.ChargeOrderInfoService;
- import com.zsElectric.boot.business.service.UserAccountService;
- import com.zsElectric.boot.business.service.UserInfoService;
- import com.zsElectric.boot.charging.dto.StartChargingRequestDTO;
- import com.zsElectric.boot.charging.dto.StartChargingResponseVO;
- import com.zsElectric.boot.charging.service.ChargingBusinessService;
- import com.zsElectric.boot.charging.vo.EquipmentAuthResponseVO;
- import com.zsElectric.boot.charging.vo.StopChargingOperationResponseVO;
- import com.zsElectric.boot.common.constant.ConnectivityConstants;
- import com.zsElectric.boot.common.constant.SystemConstants;
- import com.zsElectric.boot.core.exception.BusinessException;
- import com.zsElectric.boot.security.util.SecurityUtils;
- import com.zsElectric.boot.system.mapper.UserMapper;
- import com.zsElectric.boot.system.model.entity.DictItem;
- import com.zsElectric.boot.system.model.entity.User;
- import com.zsElectric.boot.system.service.DictItemService;
- import lombok.RequiredArgsConstructor;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.stereotype.Service;
- import java.math.BigDecimal;
- import java.math.RoundingMode;
- import java.text.SimpleDateFormat;
- import java.time.LocalDateTime;
- import java.time.format.DateTimeFormatter;
- import java.util.*;
- import com.zsElectric.boot.charging.entity.ThirdPartyChargeStatus;
- import com.zsElectric.boot.charging.entity.ThirdPartyConnectorInfo;
- import com.zsElectric.boot.charging.entity.ThirdPartyStationInfo;
- import com.zsElectric.boot.charging.entity.ThirdPartyEquipmentPricePolicy;
- import com.zsElectric.boot.charging.entity.ThirdPartyPolicyInfo;
- import com.zsElectric.boot.charging.mapper.ThirdPartyChargeStatusMapper;
- import com.zsElectric.boot.charging.mapper.ThirdPartyConnectorInfoMapper;
- import com.zsElectric.boot.charging.mapper.ThirdPartyEquipmentPricePolicyMapper;
- import com.zsElectric.boot.charging.mapper.ThirdPartyPolicyInfoMapper;
- import com.zsElectric.boot.charging.mapper.ThirdPartyApiLogMapper;
- import com.zsElectric.boot.charging.entity.ThirdPartyApiLog;
- import com.zsElectric.boot.common.util.DateUtils;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import com.fasterxml.jackson.databind.JsonNode;
- import org.springframework.transaction.annotation.Transactional;
- import org.redisson.api.RLock;
- import org.redisson.api.RedissonClient;
- import java.util.concurrent.TimeUnit;
- import com.zsElectric.boot.business.model.entity.FirmAccountLog;
- import com.zsElectric.boot.common.util.OkHttpUtil;
- import static com.zsElectric.boot.business.service.WFTOrderService.USER_FUND_LOCK_KEY;
- import static com.zsElectric.boot.business.service.WFTOrderService.USER_FUND_LOCK_EXPIRE;
- /**
- * 充电订单信息服务实现类
- *
- * @author zsElectric
- * @since 2025-12-17 19:13
- */
- @Slf4j
- @Service
- @RequiredArgsConstructor
- public class ChargeOrderInfoServiceImpl extends ServiceImpl<ChargeOrderInfoMapper, ChargeOrderInfo> implements ChargeOrderInfoService {
- private final ChargeOrderInfoConverter chargeOrderInfoConverter;
- private final ChargingBusinessService chargingBusinessService;
- private final ThirdPartyInfoMapper thirdPartyInfoMapper;
- private final UserInfoMapper userInfoMapper;
- private final UserInfoService userInfoService;
- private final FirmInfoMapper firmInfoMapper;
- private final CouponMapper couponMapper;
- private final CouponTemplateMapper couponTemplateMapper;
- private final UserAccountService userAccountService;
- private final AppletHomeService appletHomeService;
- private final UserMapper userMapper;
- private final UserVehicleMapper userVehicleMapper;
- private final ThirdPartyChargeStatusMapper chargeStatusMapper;
- private final ThirdPartyConnectorInfoMapper connectorInfoMapper;
- private final ThirdPartyStationInfoMapper thirdPartyStationInfoMapper;
- private final PolicyFeeMapper policyFeeMapper;
- private final ThirdPartyEquipmentPricePolicyMapper thirdPartyEquipmentPricePolicyMapper;
- private final ThirdPartyPolicyInfoMapper thirdPartyPolicyInfoMapper;
- private final DiscountsActivityMapper discountsActivityMapper;
- private final ThirdPartyApiLogMapper thirdPartyApiLogMapper;
- private final ObjectMapper objectMapper;
- private final DictItemService dictItemService;
- private final RedissonClient redissonClient;
- private final OkHttpUtil okHttpUtil;
- private final FirmAccountLogMapper firmAccountLogMapper;
- //充电订单号前缀
- private final String ORDER_NO_PREFIX = "CD";
- //设备流水号前缀
- private final String EQUIPMENT_NO_PREFIX = "SB";
- /**
- * 获取充电订单信息分页列表
- *
- * @param queryParams 查询参数
- * @return {@link IPage <ChargeOrderInfoVO>} 充电订单信息分页列表
- */
- @Override
- public IPage<ChargeOrderInfoVO> getChargeOrderInfoPage(ChargeOrderInfoQuery queryParams) {
- Page<ChargeOrderInfoVO> pageVO = this.baseMapper.getChargeOrderInfoPage(
- new Page<>(queryParams.getPageNum(), queryParams.getPageSize()),
- queryParams
- );
- return pageVO;
- }
-
- /**
- * 获取充电订单信息表单数据
- *
- * @param id 充电订单信息ID
- * @return 充电订单信息表单数据
- */
- @Override
- public ChargeOrderInfoForm getChargeOrderInfoFormData(Long id) {
- ChargeOrderInfo entity = this.getById(id);
- return chargeOrderInfoConverter.toForm(entity);
- }
-
- /**
- * 新增充电订单信息
- *
- * @param formData 充电订单信息表单对象
- * @return 是否新增成功
- */
- @Override
- public boolean saveChargeOrderInfo(ChargeOrderInfoForm formData) {
- ChargeOrderInfo entity = chargeOrderInfoConverter.toEntity(formData);
- return this.save(entity);
- }
-
- /**
- * 更新充电订单信息
- *
- * @param id 充电订单信息ID
- * @param formData 充电订单信息表单对象
- * @return 是否修改成功
- */
- @Override
- public boolean updateChargeOrderInfo(Long id,ChargeOrderInfoForm formData) {
- ChargeOrderInfo entity = chargeOrderInfoConverter.toEntity(formData);
- return this.updateById(entity);
- }
-
- /**
- * 删除充电订单信息
- *
- * @param ids 充电订单信息ID,多个以英文逗号(,)分割
- * @return 是否删除成功
- */
- @Override
- public boolean deleteChargeOrderInfos(String ids) {
- Assert.isTrue(StrUtil.isNotBlank(ids), "删除的充电订单信息数据为空");
- // 逻辑删除
- List<Long> idList = Arrays.stream(ids.split(","))
- .map(Long::parseLong)
- .toList();
- return this.removeByIds(idList);
- }
- @Override
- public IPage<ChargeOrderInfoVO> getPage(AppChargeOrderInfoQuery queryParams) {
- queryParams.setUserId(SecurityUtils.getUserId());
- Page<ChargeOrderInfoVO> pageVO = this.baseMapper.getPage(
- new Page<>(queryParams.getPageNum(), queryParams.getPageSize()),
- queryParams
- );
- return pageVO;
- }
- @Override
- public AppChargeVO invokeCharge(AppInvokeChargeForm formData) {
- log.info("启动充电开始,用户ID:{},设备认证流水号:{},充电桩编号:{}", SecurityUtils.getUserId(), formData.getEquipAuthSeq(), formData.getEquipmentId());
- //校验设备占用状态
- chargingBusinessService.checkEquipmentOccupancyStatus(formData.getConnectorId());
- // 渠道方启动充电不需要用户资金锁
- if (Objects.equals(formData.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_CHANNEL)) {
- log.info("渠道方启动充电开始,用户ID:{},设备认证流水号:{},充电桩编号:{}", SecurityUtils.getUserId(), formData.getEquipAuthSeq(), formData.getEquipmentId());
- try {
- return channelInvokeCharge(formData);
- } catch (Exception e) {
- log.error("渠道方启动充电失败", e);
- throw new BusinessException("启动充电失败 !" + e.getMessage());
- }
- }
- // 必要校验
- Long userId = SecurityUtils.getUserId();
- Assert.isTrue(userId != null, "用户ID不能为空");
- // 获取用户资金操作统一锁,防止充电启动与退款并发
- String lockKey = USER_FUND_LOCK_KEY + userId;
- RLock lock = redissonClient.getLock(lockKey);
- boolean locked = false;
- try {
- // 尝试获取锁,等待3秒,锁过期时间60秒
- locked = lock.tryLock(3, USER_FUND_LOCK_EXPIRE, TimeUnit.SECONDS);
- if (!locked) {
- log.warn("用户:{}资金操作正在进行中,无法启动充电", userId);
- throw new BusinessException("操作正在进行中,请稍后重试");
- }
- return doInvokeCharge(formData, userId);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- throw new BusinessException("启动充电失败,请稍后重试");
- } finally {
- // 释放锁
- if (locked && lock.isHeldByCurrentThread()) {
- lock.unlock();
- }
- }
- }
- /**
- * 执行启动充电逻辑(内部方法)
- */
- private AppChargeVO doInvokeCharge(AppInvokeChargeForm formData, Long userId) {
- try {
- AppChargeVO appInvokeChargeVO = new AppChargeVO();
- AppUserInfoVO userInfo = userInfoMapper.getAppletUserInfo(userId);
- Assert.isTrue(userInfo != null, "用户信息不存在");
- //判断有没有正在进行中的订单
- Long count = this.baseMapper.selectCount(Wrappers.lambdaQuery(ChargeOrderInfo.class).eq(ChargeOrderInfo::getUserId, userId).in(ChargeOrderInfo::getStatus, SystemConstants.STATUS_ZERO, SystemConstants.STATUS_ONE, SystemConstants.STATUS_TWO));
- if (count > 0){
- throw new BusinessException("您有正在进行中的订单,请先停止充电");
- }
- //校验用户余额是否满足起充价(防止充值后立即退款绕过前端校验)
- DictItem upRecharge = dictItemService.getOne(Wrappers.<DictItem>lambdaQuery()
- .eq(DictItem::getDictCode, "up_recharge")
- .last("limit 1")
- );
- if (ObjectUtil.isNotEmpty(upRecharge)) {
- BigDecimal chargeFee = new BigDecimal(upRecharge.getValue());
- UserAccount userAccount = userAccountService.getOne(Wrappers.lambdaQuery(UserAccount.class)
- .eq(UserAccount::getUserId, userId)
- .last("limit 1"));
- if (userAccount == null || userAccount.getBalance().compareTo(chargeFee) < 0) {
- throw new BusinessException("用户余额低于起充值 " + chargeFee + " 元,请前往充值后再充电!");
- }
- }
- //生成系统充电订单号及互联互通充电订单号 startChargeSeq equipAuthSeq (格式"运营商ID+唯一编号")
- String chargeOrderNo = generateNo(ORDER_NO_PREFIX, userId);
- String seq = ConnectivityConstants.OPERATOR_ID + chargeOrderNo;
- //请求设备认证
- EquipmentAuthResponseVO equipmentAuthResponseVO = chargingBusinessService.queryEquipAuth(seq,
- formData.getConnectorId());
- if (Objects.equals(equipmentAuthResponseVO.getSuccStat(), SystemConstants.STATUS_ONE)) {
- throw new BusinessException("设备认证失败,请检查枪是否正确接入充电槽!");
- }else {
- log.info("设备认证成功,设备认证流水号:{}", equipmentAuthResponseVO.getEquipAuthSeq());
- }
- //创建订单
- ChargeOrderInfo chargeOrderInfo = new ChargeOrderInfo();
- chargeOrderInfo.setUserId(userId);
- chargeOrderInfo.setConnectorId(formData.getConnectorId());
- chargeOrderInfo.setEquipmentId(formData.getEquipmentId());
- chargeOrderInfo.setEquipAuthSeq(seq);
- chargeOrderInfo.setChargeOrderNo(chargeOrderNo);
- chargeOrderInfo.setStartChargeSeq(seq);
- chargeOrderInfo.setPhoneNum(userInfo.getPhone());
- chargeOrderInfo.setThirdPartyStationId(formData.getStationId());
- chargeOrderInfo.setOrderType(formData.getOrderType());
- //预支付金额
- BigDecimal preAmt = appletHomeService.calculateAvailableChargingAmount(formData.getConnectorId());
- chargeOrderInfo.setPreAmt(preAmt);
- //渠道方订单设置运营商ID
- if (Objects.equals(formData.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_CHANNEL)){
- chargeOrderInfo.setOperatorId(formData.getOperatorId());
- chargeOrderInfo.setPreAmt(formData.getChannelPreAmt());
- }
- //判断用户是否绑定企业
- if (ObjectUtil.isNotEmpty(userInfo.getFirmId())) {
- FirmInfo firmInfo = firmInfoMapper.selectById(userInfo.getFirmId());
- if(firmInfo != null && Objects.equals(firmInfo.getStatus(), SystemConstants.STATUS_ONE)) {
- chargeOrderInfo.setFirmId(firmInfo.getId());
- chargeOrderInfo.setOrderType(SystemConstants.STATUS_ONE);
- }
- }
- //优惠券
- if(ObjectUtil.isNotEmpty(formData.getCouponId())) {
- Coupon coupon = couponMapper.selectById(formData.getCouponId());
- if(coupon != null && Objects.equals(coupon.getStatus(), SystemConstants.STATUS_ONE)) {
- CouponTemplate couponTemplate = couponTemplateMapper.selectById(coupon.getTemplateId());
- chargeOrderInfo.setCouponId(coupon.getId());
- chargeOrderInfo.setCouponPrice(couponTemplate.getDiscountPrice());
- }
- }
- //启动充电
- StartChargingRequestDTO requestDTO = new StartChargingRequestDTO();
- requestDTO
- .setStartChargeSeq(seq)
- .setConnectorID(formData.getConnectorId())
- .setPhoneNum(userInfo.getPhone())
- //预支付金额
- .setChargingAmt(preAmt.toString())
- ;
- //车牌号
- if(ObjectUtil.isNotEmpty(formData.getPlateNum())) {
- chargeOrderInfo.setPlateNum(formData.getPlateNum());
- requestDTO.setPlateNum(formData.getPlateNum());
- }else {
- //获取当前用户的默认车牌号
- UserVehicle userVehicle =
- userVehicleMapper.selectOne(Wrappers.lambdaQuery(UserVehicle.class).eq(UserVehicle::getUserId, userId).eq(UserVehicle::getIsDefault, SystemConstants.STATUS_ONE).last("limit 1"));
- if (ObjectUtil.isNotEmpty(userVehicle)) {
- requestDTO.setPlateNum(userVehicle.getLicensePlate());
- }
- }
- StartChargingResponseVO startChargingResponseVO = chargingBusinessService.startCharging(requestDTO);
- if (!Objects.equals(startChargingResponseVO.getSuccStat(), SystemConstants.STATUS_ZERO)) {
- throw new BusinessException(startChargingResponseVO.getFailReasonMsg());
- }
- //保存订单
- this.save(chargeOrderInfo);
- appInvokeChargeVO.setChargeOrderNo(chargeOrderNo);
- appInvokeChargeVO.setChargeOrderId(chargeOrderInfo.getId());
- return appInvokeChargeVO;
- } catch (Exception e) {
- log.error("启动充电失败,系统错误", e);
- throw new BusinessException("启动充电失败 !" + e.getMessage());
- }
- }
- /**
- * 渠道方启动充电
- *
- * @param formData
- * @return
- */
- public AppChargeVO channelInvokeCharge(AppInvokeChargeForm formData) throws JsonProcessingException {
- if (StrUtil.isBlank(formData.getOperatorId())) {
- throw new BusinessException("运营商ID不能为空");
- }
- if (StrUtil.isBlank(formData.getChannelOrderNo())) {
- throw new BusinessException("渠道订单号不能为空");
- }
- if (StrUtil.isBlank(formData.getChannelUserPhone())) {
- throw new BusinessException("渠道用户手机号不能为空");
- }
- if (formData.getChannelPreAmt() == null || formData.getChannelPreAmt().compareTo(BigDecimal.ZERO) <= 0) {
- throw new BusinessException("渠道预支付金额必须大于0");
- }
- ThirdPartyInfo thirdPartyInfo = thirdPartyInfoMapper.selectOne(
- Wrappers.lambdaQuery(ThirdPartyInfo.class)
- .eq(ThirdPartyInfo::getOperatorId, formData.getOperatorId())
- .last("limit 1"));
- if (thirdPartyInfo == null) {
- throw new BusinessException("渠道运营商不存在");
- }
- UserInfo channelUser = userInfoService.getUserInfoByPhoneAndOperatorId(
- formData.getChannelUserPhone(),
- thirdPartyInfo.getId()
- );
- if (channelUser == null) {
- channelUser = userInfoService.registerThirdPartyUserByPhone(
- formData.getChannelUserPhone(),
- thirdPartyInfo.getId()
- );
- }
- if (channelUser == null) {
- throw new BusinessException("渠道用户不存在且自动注册失败");
- }
- String seq = ConnectivityConstants.OPERATOR_ID + formData.getChannelOrderNo();
- //请求设备认证
- EquipmentAuthResponseVO equipmentAuthResponseVO = chargingBusinessService.queryEquipAuth(seq,
- formData.getConnectorId());
- if (Objects.equals(equipmentAuthResponseVO.getSuccStat(), SystemConstants.STATUS_ONE)) {
- throw new BusinessException("设备认证失败,请检查枪是否正确接入充电槽!");
- } else {
- log.info("设备认证成功,设备认证流水号:{}", equipmentAuthResponseVO.getEquipAuthSeq());
- }
- //创建订单
- ChargeOrderInfo chargeOrderInfo = new ChargeOrderInfo();
- Long currentUserId = SecurityUtils.getUserId();
- chargeOrderInfo.setUserId(channelUser.getId());
- User user = currentUserId == null ? null : userMapper.selectById(currentUserId);
- if(ObjectUtil.isNotEmpty(user)){
- FirmInfo firmInfo = firmInfoMapper.selectOne(Wrappers.lambdaQuery(FirmInfo.class).eq(FirmInfo::getDeptId, user.getDeptId()).last("limit 1"));
- if(firmInfo != null) {
- chargeOrderInfo.setFirmId(firmInfo.getId());
- }
- }
- chargeOrderInfo.setOrderType(SystemConstants.CHARGE_ORDER_TYPE_CHANNEL);
- chargeOrderInfo.setConnectorId(formData.getConnectorId());
- chargeOrderInfo.setEquipmentId(formData.getEquipmentId());
- chargeOrderInfo.setEquipAuthSeq(seq);
- chargeOrderInfo.setChargeOrderNo(formData.getChannelOrderNo());
- chargeOrderInfo.setStartChargeSeq(seq);
- chargeOrderInfo.setPhoneNum(formData.getChannelUserPhone());
- chargeOrderInfo.setThirdPartyStationId(formData.getStationId());
- //渠道手机号
- if(ObjectUtil.isNotEmpty(formData.getChannelUserPhone())){
- chargeOrderInfo.setPhoneNum(formData.getChannelUserPhone());
- }
- //预支付金额
- chargeOrderInfo.setPreAmt(formData.getChannelPreAmt());
- //渠道方订单设置运营商ID
- chargeOrderInfo.setOperatorId(formData.getOperatorId());
- //启动充电
- StartChargingRequestDTO requestDTO = new StartChargingRequestDTO();
- requestDTO
- .setStartChargeSeq(seq)
- .setConnectorID(formData.getConnectorId())
- .setPhoneNum(formData.getChannelUserPhone())
- //预支付金额
- .setChargingAmt(formData.getChannelPreAmt().toString());
- //车牌号
- if(ObjectUtil.isNotEmpty(formData.getPlateNum())) {
- chargeOrderInfo.setPlateNum(formData.getPlateNum());
- requestDTO.setPlateNum(formData.getPlateNum());
- }
- StartChargingResponseVO startChargingResponseVO = chargingBusinessService.startCharging(requestDTO);
- if (!Objects.equals(startChargingResponseVO.getSuccStat(), SystemConstants.STATUS_ZERO)) {
- throw new BusinessException(startChargingResponseVO.getFailReasonMsg());
- }
- //保存订单
- this.save(chargeOrderInfo);
- AppChargeVO appInvokeChargeVO = new AppChargeVO();
- appInvokeChargeVO.setChargeOrderId(chargeOrderInfo.getId());
- appInvokeChargeVO.setChargeOrderNo(chargeOrderInfo.getChargeOrderNo());
- appInvokeChargeVO.setStatus(SystemConstants.STATUS_ZERO);
- return appInvokeChargeVO;
- }
- @Override
- public AppChargeVO stopCharge(AppStopChargeForm formData) {
- try {
- AppChargeVO appInvokeChargeVO = new AppChargeVO();
- //订单
- ChargeOrderInfo chargeOrderInfo = this.getOne(Wrappers.lambdaQuery(ChargeOrderInfo.class)
- .eq(ChargeOrderInfo::getChargeOrderNo, formData.getChargeOrderNo())
- .eq(StrUtil.isNotBlank(formData.getOperatorId()), ChargeOrderInfo::getOperatorId, formData.getOperatorId())
- .last("limit 1"));
- if(ObjectUtil.isEmpty(chargeOrderInfo)){
- throw new BusinessException("订单不存在");
- }
- StopChargingOperationResponseVO stopChargingOperationResponseVO = chargingBusinessService.stopCharging(chargeOrderInfo.getStartChargeSeq(), chargeOrderInfo.getConnectorId());
- if (!Objects.equals(stopChargingOperationResponseVO.getSuccStat(), SystemConstants.STATUS_ZERO)) {
- throw new BusinessException("停止充电失败,请稍后再重试!");
- }
- chargeOrderInfo.setStopType(1); // 1-主动停止
- this.updateById(chargeOrderInfo);
- appInvokeChargeVO.setChargeOrderId(chargeOrderInfo.getId());
- appInvokeChargeVO.setChargeOrderNo(chargeOrderInfo.getChargeOrderNo());
- return appInvokeChargeVO;
- } catch (Exception e) {
- log.error("停止充电失败,系统错误", e);
- throw new BusinessException("停止充电失败,请稍后再重试!");
- }
- }
- @Override
- public void orderSettlement(Long chargeOrderId) {
- ChargeOrderInfo chargeOrderInfo = this.getById(chargeOrderId);
- Long userId = chargeOrderInfo.getUserId();
- //平台收费总金额
- BigDecimal totalCharge = chargeOrderInfo.getRealCost();
- //加积分
- userAccountService.update(Wrappers.lambdaUpdate(UserAccount.class)
- .eq(UserAccount::getUserId, userId)
- .setSql("`integral` = `integral` +" + totalCharge)
- );
- //账户变动及日志记录
- userAccountService.updateAccountBalanceAndLog(
- userId,
- totalCharge,
- SystemConstants.CHANGE_TYPE_REDUCE,
- SystemConstants.CHARGE_DEDUCT_NOTE,
- chargeOrderInfo.getId()
- );
- }
- @Override
- public ChargeOrderInfoVO queryOrder(String chargeOrderNo) {
- return baseMapper.queryOrder(chargeOrderNo);
- }
- /**
- * 获取充电订单信息导出列表
- *
- * @param queryParams 查询参数
- * @return 充电订单信息导出列表
- */
- @Override
- public List<ChargeOrderInfoExportDTO> listExportChargeOrderInfo(ChargeOrderInfoQuery queryParams) {
- return baseMapper.listExportChargeOrderInfo(queryParams);
- }
- /**
- * 创建商户订单号
- * 要求 32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一
- * 组成 两位前缀 + 17位时间戳 + 9位id补零 + 4位随机数 合计32位
- *
- * @param head 例如 商品-SP 退款-TK 等等
- * @param id 用户id
- * @return
- */
- public String generateNo(String head, Long id) {
- StringBuilder uid = new StringBuilder(id.toString());
- Date date = new Date();
- SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
- int length = uid.length();
- for (int i = 0; i < 9 - length; i++) {
- uid.insert(0, "0");
- }
- return head + sdf.format(date) + uid + (int) ((Math.random() * 9 + 1) * 1000);
- }
- /**
- * 修复未处理的充电订单
- * 优先通过third_party_api_log表获取推送充电订单信息,
- * 若没有则通过third_party_charge_status表查询total_power>0的数据
- * 重新处理这些订单:更新状态、计算费用、扣减余额
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public String repairUnprocessedOrders() {
- log.info("开始修复未处理的充电订单...");
-
- // 1. 查询状态为5(未成功充电)的订单
- List<ChargeOrderInfo> unprocessedOrders = this.list(Wrappers.<ChargeOrderInfo>lambdaQuery()
- .eq(ChargeOrderInfo::getStatus, 5)
- .isNotNull(ChargeOrderInfo::getStartChargeSeq));
-
- if (unprocessedOrders.isEmpty()) {
- log.info("没有找到需要修复的订单");
- return "没有找到需要修复的订单";
- }
-
- int totalCount = 0;
- int successCount = 0;
- int skipCount = 0;
- int apiLogCount = 0;
- int chargeStatusCount = 0;
- List<String> failedOrders = new ArrayList<>();
-
- for (ChargeOrderInfo order : unprocessedOrders) {
- totalCount++;
- try {
- // 2. 优先从third_party_api_log表查询推送充电订单信息
- ThirdPartyApiLog apiLog = thirdPartyApiLogMapper.selectOne(
- Wrappers.<ThirdPartyApiLog>lambdaQuery()
- .eq(ThirdPartyApiLog::getInterfaceDescription, "推送充电订单信息")
- .like(ThirdPartyApiLog::getDecryptedRequestData, order.getStartChargeSeq())
- .orderByDesc(ThirdPartyApiLog::getCreatedTime)
- .last("LIMIT 1"));
-
- if (apiLog != null && apiLog.getDecryptedRequestData() != null) {
- // 使用API日志中的推送数据处理订单
- boolean success = processOrderFromApiLog(order, apiLog);
- if (success) {
- // 设置补偿状态为已补偿
- order.setCompensateStatus(1);
- this.updateById(order);
- successCount++;
- apiLogCount++;
- log.info("订单{}通过API日志修复成功", order.getChargeOrderNo());
- continue;
- }
- }
-
- // 3. 备选方案:从third_party_charge_status表查询
- ThirdPartyChargeStatus chargeStatus = chargeStatusMapper.selectOne(
- Wrappers.<ThirdPartyChargeStatus>lambdaQuery()
- .eq(ThirdPartyChargeStatus::getStartChargeSeq, order.getStartChargeSeq()));
-
- if (chargeStatus == null || chargeStatus.getTotalPower() == null
- || chargeStatus.getTotalPower().compareTo(BigDecimal.ZERO) <= 0) {
- log.info("订单{}无有效充电数据,设置为异常无须补偿", order.getChargeOrderNo());
- // 设置补偿状态为异常无须补偿
- order.setCompensateStatus(2);
- this.updateById(order);
- skipCount++;
- continue;
- }
-
- log.info("开始通过充电状态表修复订单: {}, 充电量: {}", order.getChargeOrderNo(), chargeStatus.getTotalPower());
-
- // 4. 获取站点信息
- ThirdPartyConnectorInfo connectorInfo = connectorInfoMapper.selectOne(
- Wrappers.<ThirdPartyConnectorInfo>lambdaQuery()
- .eq(ThirdPartyConnectorInfo::getConnectorId, order.getConnectorId())
- .last("LIMIT 1"));
- if (connectorInfo == null) {
- log.warn("订单{}找不到充电接口信息", order.getChargeOrderNo());
- failedOrders.add(order.getChargeOrderNo());
- continue;
- }
-
- ThirdPartyStationInfo stationInfo = thirdPartyStationInfoMapper.selectOne(
- Wrappers.<ThirdPartyStationInfo>lambdaQuery()
- .eq(ThirdPartyStationInfo::getStationId, connectorInfo.getStationId())
- .last("LIMIT 1"));
- if (stationInfo == null) {
- log.warn("订单{}找不到站点信息", order.getChargeOrderNo());
- failedOrders.add(order.getChargeOrderNo());
- continue;
- }
-
- // 5. 设置第三方费用信息
- order.setTotalCharge(chargeStatus.getTotalPower());
- order.setThirdPartyTotalCost(chargeStatus.getTotalMoney() != null ? chargeStatus.getTotalMoney() : BigDecimal.ZERO);
- order.setThirdPartyServerfee(chargeStatus.getServiceMoney() != null ? chargeStatus.getServiceMoney() : BigDecimal.ZERO);
- order.setThirdPartyElecfee(chargeStatus.getElecMoney() != null ? chargeStatus.getElecMoney() : BigDecimal.ZERO);
- if (StrUtil.isNotBlank(chargeStatus.getChargeDetails())) {
- order.setChargeDetails(chargeStatus.getChargeDetails());
- }
-
- // 6. 计算平台服务费
- BigDecimal serviceFee = calculateServiceFee(order, chargeStatus, stationInfo);
-
- // 7. 更新订单信息
- order.setRealServiceCost(serviceFee.setScale(2, RoundingMode.HALF_UP));
- order.setRealCost(serviceFee.add(order.getThirdPartyTotalCost()));
- order.setStatus(SystemConstants.STATUS_THREE); // 已完成
-
- // 设置充电时间
- if (chargeStatus.getStartTime() != null && chargeStatus.getEndTime() != null) {
- DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
- order.setStartTime(chargeStatus.getStartTime().format(formatter));
- order.setEndTime(chargeStatus.getEndTime().format(formatter));
- order.setChargeTime(DateUtils.getDuration(order.getStartTime(), order.getEndTime()));
- }
-
- // 设置修复备注
- order.setRemark("通过补偿修复处理");
- // 设置补偿状态为已补偿
- order.setCompensateStatus(1);
-
- this.updateById(order);
-
- // 8. 执行账户余额扣减(仅平台订单和企业订单)
- if (Objects.equals(order.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_PLATFORM)
- || Objects.equals(order.getOrderType(), SystemConstants.STATUS_ONE)) {
- orderSettlement(order.getId());
- log.info("订单{}余额扣减完成", order.getChargeOrderNo());
- }
-
- successCount++;
- chargeStatusCount++;
- log.info("订单{}通过充电状态表修复成功,实际费用: {}", order.getChargeOrderNo(), order.getRealCost());
-
- } catch (Exception e) {
- log.error("修复订单{}失败: {}", order.getChargeOrderNo(), e.getMessage(), e);
- failedOrders.add(order.getChargeOrderNo());
- }
- }
-
- String result = String.format("修复完成!总计: %d, 成功: %d (API日志: %d, 充电状态: %d), 跳过: %d, 失败: %d",
- totalCount, successCount, apiLogCount, chargeStatusCount, skipCount, failedOrders.size());
- if (!failedOrders.isEmpty()) {
- result += ", 失败订单: " + String.join(",", failedOrders);
- }
- log.info(result);
- return result;
- }
-
- /**
- * 通过API日志中的推送数据处理订单
- * @param order 订单信息
- * @param apiLog API日志
- * @return 是否处理成功
- */
- private boolean processOrderFromApiLog(ChargeOrderInfo order, ThirdPartyApiLog apiLog) {
- try {
- JsonNode jsonNode = objectMapper.readTree(apiLog.getDecryptedRequestData());
-
- // 解析推送数据
- String startChargeSeq = getJsonTextValue(jsonNode, "StartChargeSeq");
- if (startChargeSeq == null || !startChargeSeq.equals(order.getStartChargeSeq())) {
- log.warn("订单{}的StartChargeSeq不匹配,跳过API日志处理", order.getChargeOrderNo());
- return false;
- }
-
- String totalPowerStr = getJsonTextValue(jsonNode, "TotalPower");
- if (totalPowerStr == null || new BigDecimal(totalPowerStr).compareTo(BigDecimal.ZERO) <= 0) {
- log.info("订单{}的API日志充电量为0,跳过处理", order.getChargeOrderNo());
- return false;
- }
-
- log.info("开始通过API日志修复订单: {}, 充电量: {}", order.getChargeOrderNo(), totalPowerStr);
-
- // 设置充电信息
- order.setTotalCharge(new BigDecimal(totalPowerStr));
- order.setStopReason(getJsonTextValue(jsonNode, "StopReason"));
- order.setStartTime(getJsonTextValue(jsonNode, "StartTime"));
- order.setEndTime(getJsonTextValue(jsonNode, "EndTime"));
- order.setChargeDetails(jsonNode.toString());
-
- // 第三方费用
- String totalMoney = getJsonTextValue(jsonNode, "TotalMoney");
- String totalSeviceMoney = getJsonTextValue(jsonNode, "TotalSeviceMoney");
- String totalElecMoney = getJsonTextValue(jsonNode, "TotalElecMoney");
- order.setThirdPartyTotalCost(totalMoney != null ? new BigDecimal(totalMoney) : BigDecimal.ZERO);
- order.setThirdPartyServerfee(totalSeviceMoney != null ? new BigDecimal(totalSeviceMoney) : BigDecimal.ZERO);
- order.setThirdPartyElecfee(totalElecMoney != null ? new BigDecimal(totalElecMoney) : BigDecimal.ZERO);
-
- // 获取连接器和站点信息
- String connectorId = getJsonTextValue(jsonNode, "ConnectorID");
- if (connectorId == null) {
- connectorId = order.getConnectorId();
- }
-
- ThirdPartyConnectorInfo connectorInfo = connectorInfoMapper.selectOne(
- Wrappers.<ThirdPartyConnectorInfo>lambdaQuery()
- .eq(ThirdPartyConnectorInfo::getConnectorId, connectorId)
- .last("LIMIT 1"));
- if (connectorInfo == null) {
- log.warn("订单{}找不到充电接口信息", order.getChargeOrderNo());
- return false;
- }
-
- ThirdPartyStationInfo stationInfo = thirdPartyStationInfoMapper.selectOne(
- Wrappers.<ThirdPartyStationInfo>lambdaQuery()
- .eq(ThirdPartyStationInfo::getStationId, connectorInfo.getStationId())
- .last("LIMIT 1"));
- if (stationInfo == null) {
- log.warn("订单{}找不到站点信息", order.getChargeOrderNo());
- return false;
- }
-
- // 计算平台服务费(从ChargeDetails中解析)
- BigDecimal serviceFee = calculateServiceFeeFromChargeDetails(order, jsonNode, stationInfo);
-
- // 更新订单信息
- order.setRealServiceCost(serviceFee.setScale(2, RoundingMode.HALF_UP));
- order.setRealCost(serviceFee.add(order.getThirdPartyTotalCost()));
- order.setStatus(SystemConstants.STATUS_THREE); // 已完成
-
- // 计算充电时间
- if (order.getStartTime() != null && order.getEndTime() != null) {
- order.setChargeTime(DateUtils.getDuration(order.getStartTime(), order.getEndTime()));
- }
-
- // 设置修复备注
- order.setRemark("通过补偿修复处理");
-
- this.updateById(order);
-
- // 执行账户余额扣减(仅平台订单和企业订单)
- if (Objects.equals(order.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_PLATFORM)
- || Objects.equals(order.getOrderType(), SystemConstants.STATUS_ONE)) {
- orderSettlement(order.getId());
- log.info("订单{}余额扣减完成", order.getChargeOrderNo());
- }
-
- log.info("订单{}通过API日志处理成功,实际费用: {}", order.getChargeOrderNo(), order.getRealCost());
- return true;
-
- } catch (Exception e) {
- log.error("通过API日志处理订单{}失败: {}", order.getChargeOrderNo(), e.getMessage(), e);
- return false;
- }
- }
-
- /**
- * 从ChargeDetails计算平台服务费
- */
- private BigDecimal calculateServiceFeeFromChargeDetails(ChargeOrderInfo order, JsonNode jsonNode,
- ThirdPartyStationInfo stationInfo) {
- BigDecimal serviceFee = BigDecimal.ZERO;
-
- JsonNode chargeDetails = jsonNode.get("ChargeDetails");
- if (chargeDetails != null && chargeDetails.isArray()) {
- for (JsonNode node : chargeDetails) {
- String itemFlag = getJsonTextValue(node, "ItemFlag");
- String detailPowerStr = getJsonTextValue(node, "DetailPower");
-
- if (itemFlag != null && detailPowerStr != null) {
- BigDecimal detailPower = new BigDecimal(detailPowerStr);
- PolicyFee policyFee = policyFeeMapper.selectOne(Wrappers.<PolicyFee>lambdaQuery()
- .eq(PolicyFee::getStationInfoId, stationInfo.getId())
- .eq(PolicyFee::getPeriodFlag, Integer.parseInt(itemFlag))
- .last("LIMIT 1"));
- if (policyFee != null && policyFee.getOpFee() != null) {
- serviceFee = serviceFee.add(policyFee.getOpFee().multiply(detailPower));
- }
- }
- }
- } else {
- // 无明细时,使用最高费用时段计算
- log.info("订单{}无充电明细,使用简化计算", order.getChargeOrderNo());
- ThirdPartyEquipmentPricePolicy pricePolicy = thirdPartyEquipmentPricePolicyMapper.selectOne(
- Wrappers.<ThirdPartyEquipmentPricePolicy>lambdaQuery()
- .eq(ThirdPartyEquipmentPricePolicy::getConnectorId, order.getConnectorId())
- .last("LIMIT 1"));
- if (pricePolicy != null) {
- List<ThirdPartyPolicyInfo> policyInfos = thirdPartyPolicyInfoMapper.selectList(
- Wrappers.<ThirdPartyPolicyInfo>lambdaQuery()
- .eq(ThirdPartyPolicyInfo::getPricePolicyId, pricePolicy.getId())
- .eq(ThirdPartyPolicyInfo::getIsDeleted, 0));
- PolicyFee maxPolicyFee = null;
- for (ThirdPartyPolicyInfo policyInfo : policyInfos) {
- PolicyFee policyFee = policyFeeMapper.selectOne(Wrappers.<PolicyFee>lambdaQuery()
- .eq(PolicyFee::getStationInfoId, stationInfo.getId())
- .eq(PolicyFee::getPeriodFlag, policyInfo.getPeriodFlag())
- .last("LIMIT 1"));
- if (policyFee != null && policyFee.getOpFee() != null) {
- if (maxPolicyFee == null || policyFee.getOpFee().compareTo(maxPolicyFee.getOpFee()) > 0) {
- maxPolicyFee = policyFee;
- }
- }
- }
- if (maxPolicyFee != null) {
- serviceFee = maxPolicyFee.getOpFee().multiply(order.getTotalCharge());
- }
- }
- }
-
- return serviceFee;
- }
-
- /**
- * 安全获取JSON节点的文本值
- */
- private String getJsonTextValue(JsonNode node, String fieldName) {
- if (node == null || !node.has(fieldName) || node.get(fieldName).isNull()) {
- return null;
- }
- return node.get(fieldName).asText();
- }
-
- /**
- * 计算平台服务费
- */
- private BigDecimal calculateServiceFee(ChargeOrderInfo order, ThirdPartyChargeStatus chargeStatus,
- ThirdPartyStationInfo stationInfo) {
- BigDecimal serviceFee = BigDecimal.ZERO;
-
- // 查询价格策略
- ThirdPartyEquipmentPricePolicy pricePolicy = thirdPartyEquipmentPricePolicyMapper.selectOne(
- Wrappers.<ThirdPartyEquipmentPricePolicy>lambdaQuery()
- .eq(ThirdPartyEquipmentPricePolicy::getConnectorId, chargeStatus.getConnectorId())
- .last("LIMIT 1"));
-
- if (pricePolicy == null) {
- log.warn("订单{}找不到价格策略", order.getChargeOrderNo());
- return serviceFee;
- }
-
- // 查询时段信息
- List<ThirdPartyPolicyInfo> allPolicyInfos = thirdPartyPolicyInfoMapper.selectList(
- Wrappers.<ThirdPartyPolicyInfo>lambdaQuery()
- .eq(ThirdPartyPolicyInfo::getPricePolicyId, pricePolicy.getId())
- .eq(ThirdPartyPolicyInfo::getIsDeleted, 0)
- .orderByAsc(ThirdPartyPolicyInfo::getStartTime));
-
- if (allPolicyInfos.isEmpty()) {
- log.warn("订单{}找不到时段信息", order.getChargeOrderNo());
- return serviceFee;
- }
-
- // 根据充电时间段匹配费用
- if (chargeStatus.getStartTime() != null && chargeStatus.getEndTime() != null) {
- String chargeStartTimeStr = chargeStatus.getStartTime().format(DateTimeFormatter.ofPattern("HHmmss"));
- String chargeEndTimeStr = chargeStatus.getEndTime().format(DateTimeFormatter.ofPattern("HHmmss"));
-
- // 找到充电时间跨越的所有时段,使用最高费用时段计算
- PolicyFee maxPolicyFee = null;
- for (ThirdPartyPolicyInfo policyInfo : allPolicyInfos) {
- if (chargeStartTimeStr.compareTo(policyInfo.getStartTime()) >= 0
- || chargeEndTimeStr.compareTo(policyInfo.getStartTime()) >= 0) {
- PolicyFee policyFee = policyFeeMapper.selectOne(
- Wrappers.<PolicyFee>lambdaQuery()
- .eq(PolicyFee::getStationInfoId, stationInfo.getId())
- .eq(PolicyFee::getPeriodFlag, policyInfo.getPeriodFlag())
- .last("LIMIT 1"));
- if (policyFee != null && policyFee.getOpFee() != null) {
- if (maxPolicyFee == null || policyFee.getOpFee().compareTo(maxPolicyFee.getOpFee()) > 0) {
- maxPolicyFee = policyFee;
- }
- }
- }
- }
-
- if (maxPolicyFee != null) {
- serviceFee = maxPolicyFee.getOpFee().multiply(order.getTotalCharge());
- }
- }
-
- return serviceFee;
- }
- /**
- * 根据充电订单号修复已完成订单
- * 修复状态为3(已完成)的订单,重新处理订单状态变更、数据修改及余额扣减
- * 优先通过third_party_api_log表获取推送数据,备选通过third_party_charge_status表查询
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public String repairOrderByOrderNo(String chargeOrderNo) {
- log.info("开始根据订单号修复订单: {}", chargeOrderNo);
-
- // 1. 查询订单
- ChargeOrderInfo order = this.getOne(Wrappers.<ChargeOrderInfo>lambdaQuery()
- .eq(ChargeOrderInfo::getChargeOrderNo, chargeOrderNo)
- .last("LIMIT 1"));
-
- if (order == null) {
- return "订单不存在: " + chargeOrderNo;
- }
-
- log.info("开始修复订单: {}, 当前状态: {}", chargeOrderNo, order.getStatus());
-
- // 2. 检查订单是否需要修复(只有充电度数、平台费用、三方费用同时为0扏null的订单才处理)
- boolean needRepair = isZeroOrNull(order.getTotalCharge())
- && isZeroOrNull(order.getRealCost())
- && isZeroOrNull(order.getRealServiceCost())
- && isZeroOrNull(order.getThirdPartyTotalCost())
- && isZeroOrNull(order.getThirdPartyServerfee());
-
- if (!needRepair) {
- return "订单" + chargeOrderNo + "已有充电数据,无需修复。充电度数:" + order.getTotalCharge()
- + ", 平台收取金额:" + order.getRealCost()
- + ", 平台服务费:" + order.getRealServiceCost()
- + ", 三方消费总额:" + order.getThirdPartyTotalCost()
- + ", 三方服务费:" + order.getThirdPartyServerfee();
- }
-
- if (order.getStartChargeSeq() == null) {
- return "订单缺少StartChargeSeq,无法修复";
- }
-
- try {
- // 3. 优先从third_party_api_log表查询推送充电订单信息
- ThirdPartyApiLog apiLog = thirdPartyApiLogMapper.selectOne(
- Wrappers.<ThirdPartyApiLog>lambdaQuery()
- .eq(ThirdPartyApiLog::getInterfaceDescription, "推送充电订单信息")
- .like(ThirdPartyApiLog::getDecryptedRequestData, order.getStartChargeSeq())
- .orderByDesc(ThirdPartyApiLog::getCreatedTime)
- .last("LIMIT 1"));
-
- if (apiLog != null && apiLog.getDecryptedRequestData() != null) {
- // 使用API日志中的推送数据处理订单
- boolean success = repairOrderFromApiLogByOrderNo(order, apiLog);
- if (success) {
- // 设置补偿状态为已补偿
- order.setCompensateStatus(1);
- order.setStatus(3);
- this.updateById(order);
- log.info("订单{}通过API日志修复成功", chargeOrderNo);
- return "订单" + chargeOrderNo + "通过API日志修复成功,实际费用: " + order.getRealCost();
- }
- }
-
- // 4. 备选方案:从third_party_charge_status表查询
- ThirdPartyChargeStatus chargeStatus = chargeStatusMapper.selectOne(
- Wrappers.<ThirdPartyChargeStatus>lambdaQuery()
- .eq(ThirdPartyChargeStatus::getStartChargeSeq, order.getStartChargeSeq()));
-
- if (chargeStatus == null || chargeStatus.getTotalPower() == null
- || chargeStatus.getTotalPower().compareTo(BigDecimal.ZERO) <= 0) {
- // 设置补偿状态为异常无须补偿
- order.setCompensateStatus(2);
- order.setStatus(5);
- this.updateById(order);
- return "订单" + chargeOrderNo + "无有效充电数据,设置为异常无须补偿";
- }
-
- log.info("开始通过充电状态表修复订单: {}, 充电量: {}", chargeOrderNo, chargeStatus.getTotalPower());
-
- // 5. 获取站点信息
- ThirdPartyConnectorInfo connectorInfo = connectorInfoMapper.selectOne(
- Wrappers.<ThirdPartyConnectorInfo>lambdaQuery()
- .eq(ThirdPartyConnectorInfo::getConnectorId, order.getConnectorId())
- .last("LIMIT 1"));
- if (connectorInfo == null) {
- return "订单" + chargeOrderNo + "找不到充电接口信息";
- }
-
- ThirdPartyStationInfo stationInfo = thirdPartyStationInfoMapper.selectOne(
- Wrappers.<ThirdPartyStationInfo>lambdaQuery()
- .eq(ThirdPartyStationInfo::getStationId, connectorInfo.getStationId())
- .last("LIMIT 1"));
- if (stationInfo == null) {
- return "订单" + chargeOrderNo + "找不到站点信息";
- }
-
- // 6. 设置第三方费用信息
- order.setTotalCharge(chargeStatus.getTotalPower());
- order.setThirdPartyTotalCost(chargeStatus.getTotalMoney() != null ? chargeStatus.getTotalMoney() : BigDecimal.ZERO);
- order.setThirdPartyServerfee(chargeStatus.getServiceMoney() != null ? chargeStatus.getServiceMoney() : BigDecimal.ZERO);
- order.setThirdPartyElecfee(chargeStatus.getElecMoney() != null ? chargeStatus.getElecMoney() : BigDecimal.ZERO);
- if (StrUtil.isNotBlank(chargeStatus.getChargeDetails())) {
- order.setChargeDetails(chargeStatus.getChargeDetails());
- }
-
- // 7. 计算平台服务费
- BigDecimal serviceFee = calculateServiceFee(order, chargeStatus, stationInfo);
-
- // 8. 更新订单信息
- order.setRealServiceCost(serviceFee.setScale(2, RoundingMode.HALF_UP));
- order.setRealCost(serviceFee.add(order.getThirdPartyTotalCost()));
-
- // 设置充电时间
- if (chargeStatus.getStartTime() != null && chargeStatus.getEndTime() != null) {
- DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
- order.setStartTime(chargeStatus.getStartTime().format(formatter));
- order.setEndTime(chargeStatus.getEndTime().format(formatter));
- order.setChargeTime(DateUtils.getDuration(order.getStartTime(), order.getEndTime()));
- }
-
- // 设置修复备注
- order.setRemark("通过补偿修复处理");
- // 设置补偿状态为已补偿
- order.setCompensateStatus(1);
- order.setStatus(3);
- this.updateById(order);
-
- // 9. 执行账户余额扣减(仅平台订单和企业订单)
- if (Objects.equals(order.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_PLATFORM)
- || Objects.equals(order.getOrderType(), SystemConstants.STATUS_ONE)) {
- orderSettlement(order.getId());
- log.info("订单{}余额扣减完成", chargeOrderNo);
- }
-
- log.info("订单{}通过充电状态表修复成功,实际费用: {}", chargeOrderNo, order.getRealCost());
- return "订单" + chargeOrderNo + "通过充电状态表修复成功,实际费用: " + order.getRealCost();
-
- } catch (Exception e) {
- log.error("修复订单{}失败: {}", chargeOrderNo, e.getMessage(), e);
- throw new BusinessException("修复订单失败: " + e.getMessage());
- }
- }
-
- /**
- * 通过API日志修复单个订单(根据订单号)
- * @param order 订单信息
- * @param apiLog API日志
- * @return 是否处理成功
- */
- private boolean repairOrderFromApiLogByOrderNo(ChargeOrderInfo order, ThirdPartyApiLog apiLog) {
- try {
- JsonNode jsonNode = objectMapper.readTree(apiLog.getDecryptedRequestData());
-
- // 解析推送数据
- String startChargeSeq = getJsonTextValue(jsonNode, "StartChargeSeq");
- if (startChargeSeq == null || !startChargeSeq.equals(order.getStartChargeSeq())) {
- log.warn("订单{}的StartChargeSeq不匹配,跳过API日志处理", order.getChargeOrderNo());
- return false;
- }
-
- String totalPowerStr = getJsonTextValue(jsonNode, "TotalPower");
- if (totalPowerStr == null || new BigDecimal(totalPowerStr).compareTo(BigDecimal.ZERO) <= 0) {
- log.info("订单{}的API日志充电量为0,跳过处理", order.getChargeOrderNo());
- return false;
- }
-
- log.info("开始通过API日志修复订单: {}, 充电量: {}", order.getChargeOrderNo(), totalPowerStr);
-
- // 设置充电信息
- order.setTotalCharge(new BigDecimal(totalPowerStr));
- order.setStopReason(getJsonTextValue(jsonNode, "StopReason"));
- order.setStartTime(getJsonTextValue(jsonNode, "StartTime"));
- order.setEndTime(getJsonTextValue(jsonNode, "EndTime"));
- order.setChargeDetails(jsonNode.toString());
-
- // 第三方费用
- String totalMoney = getJsonTextValue(jsonNode, "TotalMoney");
- String totalSeviceMoney = getJsonTextValue(jsonNode, "TotalSeviceMoney");
- String totalElecMoney = getJsonTextValue(jsonNode, "TotalElecMoney");
- order.setThirdPartyTotalCost(totalMoney != null ? new BigDecimal(totalMoney) : BigDecimal.ZERO);
- order.setThirdPartyServerfee(totalSeviceMoney != null ? new BigDecimal(totalSeviceMoney) : BigDecimal.ZERO);
- order.setThirdPartyElecfee(totalElecMoney != null ? new BigDecimal(totalElecMoney) : BigDecimal.ZERO);
-
- // 获取连接器和站点信息
- String connectorId = getJsonTextValue(jsonNode, "ConnectorID");
- if (connectorId == null) {
- connectorId = order.getConnectorId();
- }
-
- ThirdPartyConnectorInfo connectorInfo = connectorInfoMapper.selectOne(
- Wrappers.<ThirdPartyConnectorInfo>lambdaQuery()
- .eq(ThirdPartyConnectorInfo::getConnectorId, connectorId)
- .last("LIMIT 1"));
- if (connectorInfo == null) {
- log.warn("订单{}找不到充电接口信息", order.getChargeOrderNo());
- return false;
- }
-
- ThirdPartyStationInfo stationInfo = thirdPartyStationInfoMapper.selectOne(
- Wrappers.<ThirdPartyStationInfo>lambdaQuery()
- .eq(ThirdPartyStationInfo::getStationId, connectorInfo.getStationId())
- .last("LIMIT 1"));
- if (stationInfo == null) {
- log.warn("订单{}找不到站点信息", order.getChargeOrderNo());
- return false;
- }
-
- // 计算平台服务费(从ChargeDetails中解析)
- BigDecimal serviceFee = calculateServiceFeeFromChargeDetails(order, jsonNode, stationInfo);
-
- // 更新订单信息
- order.setRealServiceCost(serviceFee.setScale(2, RoundingMode.HALF_UP));
- order.setRealCost(serviceFee.add(order.getThirdPartyTotalCost()));
-
- // 计算充电时间
- if (order.getStartTime() != null && order.getEndTime() != null) {
- order.setChargeTime(DateUtils.getDuration(order.getStartTime(), order.getEndTime()));
- }
-
- // 设置修复备注
- order.setRemark("通过补偿修复处理");
-
- this.updateById(order);
-
- // 执行账户余额扣减(仅平台订单和企业订单)
- if (Objects.equals(order.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_PLATFORM)
- || Objects.equals(order.getOrderType(), SystemConstants.STATUS_ONE)) {
- orderSettlement(order.getId());
- log.info("订单{}余额扣减完成", order.getChargeOrderNo());
- }
- // 渠道方订单:推送充电订单信息 + 渠道方账户余额扣减
- if (Objects.equals(order.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_CHANNEL)) {
- compensateChannelOrder(order, apiLog.getDecryptedRequestData());
- }
-
- log.info("订单{}通过API日志处理成功,实际费用: {}", order.getChargeOrderNo(), order.getRealCost());
- return true;
-
- } catch (Exception e) {
- log.error("通过API日志处理订单{}失败: {}", order.getChargeOrderNo(), e.getMessage(), e);
- return false;
- }
- }
-
- /**
- * 判断BigDecimal是否为null或0
- * @param value 要判断的值
- * @return 如果为null或0返回true,否则返回false
- */
- private boolean isZeroOrNull(BigDecimal value) {
- return value == null || value.compareTo(BigDecimal.ZERO) == 0;
- }
- /**
- * 补偿未处理的充电订单(定时任务调用)
- * 查找状态为3(已完成)或5(未成功充电)且充电数据为0的订单
- */
- @Override
- @Transactional(rollbackFor = Exception.class)
- public String compensateUnprocessedOrders() {
- log.info("开始执行充电订单补偿定时任务...");
-
- // 1. 查询状态为3(已完成)或5(未成功充电)且充电数据为0的订单
- List<ChargeOrderInfo> unprocessedOrders = this.list(Wrappers.<ChargeOrderInfo>lambdaQuery()
- .in(ChargeOrderInfo::getStatus, 3, 5)
- .isNotNull(ChargeOrderInfo::getStartChargeSeq)
- // 充电度数为0或null
- .and(wrapper -> wrapper
- .isNull(ChargeOrderInfo::getTotalCharge)
- .or()
- .eq(ChargeOrderInfo::getTotalCharge, BigDecimal.ZERO))
- // 平台实际收取金额为0或null
- .and(wrapper -> wrapper
- .isNull(ChargeOrderInfo::getRealCost)
- .or()
- .eq(ChargeOrderInfo::getRealCost, BigDecimal.ZERO))
- // 平台总服务费为0或null
- .and(wrapper -> wrapper
- .isNull(ChargeOrderInfo::getRealServiceCost)
- .or()
- .eq(ChargeOrderInfo::getRealServiceCost, BigDecimal.ZERO))
- // 三方充电消费总额为0或null
- .and(wrapper -> wrapper
- .isNull(ChargeOrderInfo::getThirdPartyTotalCost)
- .or()
- .eq(ChargeOrderInfo::getThirdPartyTotalCost, BigDecimal.ZERO))
- // 三方充电服务费为0或null
- .and(wrapper -> wrapper
- .isNull(ChargeOrderInfo::getThirdPartyServerfee)
- .or()
- .eq(ChargeOrderInfo::getThirdPartyServerfee, BigDecimal.ZERO))
- // 补偿状态为0或null
- .and(wrapper -> wrapper
- .isNull(ChargeOrderInfo::getCompensateStatus)
- .or()
- .eq(ChargeOrderInfo::getCompensateStatus, 0))
- );
-
- if (unprocessedOrders.isEmpty()) {
- log.info("充电订单补偿定时任务: 没有找到需要补偿的订单");
- return "没有找到需要补偿的订单";
- }
-
- log.info("充电订单补偿定时任务: 找到{}个需要补偿的订单", unprocessedOrders.size());
-
- int totalCount = 0;
- int successCount = 0;
- int skipCount = 0;
- int apiLogCount = 0;
- int chargeStatusCount = 0;
- List<String> failedOrders = new ArrayList<>();
-
- for (ChargeOrderInfo order : unprocessedOrders) {
- totalCount++;
- try {
- // 2. 优先从third_party_api_log表查询推送充电订单信息
- ThirdPartyApiLog apiLog = thirdPartyApiLogMapper.selectOne(
- Wrappers.<ThirdPartyApiLog>lambdaQuery()
- .eq(ThirdPartyApiLog::getInterfaceDescription, "推送充电订单信息")
- .like(ThirdPartyApiLog::getDecryptedRequestData, order.getStartChargeSeq())
- .orderByDesc(ThirdPartyApiLog::getCreatedTime)
- .last("LIMIT 1"));
-
- if (apiLog != null && apiLog.getDecryptedRequestData() != null) {
- // 使用API日志中的推送数据处理订单
- boolean success = processOrderFromApiLog(order, apiLog);
- if (success) {
- // 设置补偿状态为已补偿
- order.setCompensateStatus(1);
- this.updateById(order);
- successCount++;
- apiLogCount++;
- log.info("补偿任务: 订单{}通过API日志补偿成功", order.getChargeOrderNo());
- continue;
- }
- }
-
- // 3. 备选方案:从third_party_charge_status表查询
- ThirdPartyChargeStatus chargeStatus = chargeStatusMapper.selectOne(
- Wrappers.<ThirdPartyChargeStatus>lambdaQuery()
- .eq(ThirdPartyChargeStatus::getStartChargeSeq, order.getStartChargeSeq()));
-
- if (chargeStatus == null || chargeStatus.getTotalPower() == null
- || chargeStatus.getTotalPower().compareTo(BigDecimal.ZERO) <= 0) {
- log.info("补偿任务: 订单{}无有效充电数据,设置为异常无须补偿", order.getChargeOrderNo());
- // 设置补偿状态为异常无须补偿
- order.setCompensateStatus(2);
- this.updateById(order);
- skipCount++;
- continue;
- }
-
- log.info("补偿任务: 开始通过充电状态表补偿订单: {}, 充电量: {}", order.getChargeOrderNo(), chargeStatus.getTotalPower());
-
- // 4. 获取站点信息
- ThirdPartyConnectorInfo connectorInfo = connectorInfoMapper.selectOne(
- Wrappers.<ThirdPartyConnectorInfo>lambdaQuery()
- .eq(ThirdPartyConnectorInfo::getConnectorId, order.getConnectorId())
- .last("LIMIT 1"));
- if (connectorInfo == null) {
- log.warn("补偿任务: 订单{}找不到充电接口信息", order.getChargeOrderNo());
- failedOrders.add(order.getChargeOrderNo());
- continue;
- }
-
- ThirdPartyStationInfo stationInfo = thirdPartyStationInfoMapper.selectOne(
- Wrappers.<ThirdPartyStationInfo>lambdaQuery()
- .eq(ThirdPartyStationInfo::getStationId, connectorInfo.getStationId())
- .last("LIMIT 1"));
- if (stationInfo == null) {
- log.warn("补偿任务: 订单{}找不到站点信息", order.getChargeOrderNo());
- failedOrders.add(order.getChargeOrderNo());
- continue;
- }
-
- // 5. 设置第三方费用信息
- order.setTotalCharge(chargeStatus.getTotalPower());
- order.setThirdPartyTotalCost(chargeStatus.getTotalMoney() != null ? chargeStatus.getTotalMoney() : BigDecimal.ZERO);
- order.setThirdPartyServerfee(chargeStatus.getServiceMoney() != null ? chargeStatus.getServiceMoney() : BigDecimal.ZERO);
- order.setThirdPartyElecfee(chargeStatus.getElecMoney() != null ? chargeStatus.getElecMoney() : BigDecimal.ZERO);
- if (StrUtil.isNotBlank(chargeStatus.getChargeDetails())) {
- order.setChargeDetails(chargeStatus.getChargeDetails());
- }
-
- // 6. 计算平台服务费
- BigDecimal serviceFee = calculateServiceFee(order, chargeStatus, stationInfo);
-
- // 7. 更新订单信息
- order.setRealServiceCost(serviceFee.setScale(2, RoundingMode.HALF_UP));
- order.setRealCost(serviceFee.add(order.getThirdPartyTotalCost()));
- order.setStatus(SystemConstants.STATUS_THREE); // 已完成
-
- // 设置充电时间
- if (chargeStatus.getStartTime() != null && chargeStatus.getEndTime() != null) {
- DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
- order.setStartTime(chargeStatus.getStartTime().format(formatter));
- order.setEndTime(chargeStatus.getEndTime().format(formatter));
- order.setChargeTime(DateUtils.getDuration(order.getStartTime(), order.getEndTime()));
- }
-
- // 设置修复备注
- order.setRemark("通过补偿修复处理");
-
- this.updateById(order);
-
- // 8. 执行账户余额扣减(仅平台订单和企业订单)
- if (Objects.equals(order.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_PLATFORM)
- || Objects.equals(order.getOrderType(), SystemConstants.STATUS_ONE)) {
- orderSettlement(order.getId());
- log.info("补偿任务: 订单{}余额扣减完成", order.getChargeOrderNo());
- }
- // 渠道方订单:推送充电订单信息 + 渠道方账户余额扣减
- if (Objects.equals(order.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_CHANNEL)) {
- compensateChannelOrder(order, null);
- }
-
- successCount++;
- chargeStatusCount++;
- log.info("补偿任务: 订单{}通过充电状态表补偿成功,实际费用: {}", order.getChargeOrderNo(), order.getRealCost());
-
- } catch (Exception e) {
- log.error("补偿任务: 补偿订单{}失败: {}", order.getChargeOrderNo(), e.getMessage(), e);
- failedOrders.add(order.getChargeOrderNo());
- }
- }
-
- String result = String.format("充电订单补偿完成!总计: %d, 成功: %d (API日志: %d, 充电状态: %d), 跳过: %d, 失败: %d",
- totalCount, successCount, apiLogCount, chargeStatusCount, skipCount, failedOrders.size());
- if (!failedOrders.isEmpty()) {
- result += ", 失败订单: " + String.join(",", failedOrders);
- }
- log.info(result);
- return result;
- }
- /**
- * 渠道方订单补偿:推送充电订单信息给渠道方 + 渠道方账户余额扣减
- * 推送失败只记日志,不回滚补偿
- *
- * @param order 补偿完成的订单
- * @param apiLogData API日志原始数据(可为null,则从订单字段构建推送数据)
- */
- private void compensateChannelOrder(ChargeOrderInfo order, String apiLogData) {
- try {
- FirmInfo firmInfo = firmInfoMapper.selectById(order.getFirmId());
- if (firmInfo == null) {
- log.warn("补偿任务: 订单{}找不到渠道方信息,firmId: {}", order.getChargeOrderNo(), order.getFirmId());
- return;
- }
- // 1. 推送充电订单信息给渠道方
- pushChargeOrderInfoToChannel(order, firmInfo, apiLogData);
- // 2. 渠道方账户余额扣减
- deductChannelFirmBalance(order, firmInfo);
- } catch (Exception e) {
- log.error("补偿任务: 渠道方订单{}补偿处理异常: {}", order.getChargeOrderNo(), e.getMessage(), e);
- }
- }
- /**
- * 推送充电订单信息给渠道方(参考 /notification_charge_order_info 接口推送格式)
- * 失败只记日志,不影响补偿结果
- */
- private void pushChargeOrderInfoToChannel(ChargeOrderInfo order, FirmInfo firmInfo, String apiLogData) {
- try {
- // 构建推送数据
- Map<String, Object> pushData;
- if (apiLogData != null) {
- // 使用API日志原始数据
- pushData = objectMapper.readValue(apiLogData, Map.class);
- } else {
- // 从order字段构建推送数据
- pushData = buildCompensationPushData(order);
- }
- normalizeCompensationPushData(pushData, order);
- pushData.put("chargeOrderNo", order.getChargeOrderNo());
- String url = firmInfo.getChannelUrl() + "/notification_charge_order_info";
- String pushJson = objectMapper.writeValueAsString(pushData);
- int maxRetries = 3;
- int retryIntervalMs = 5000;
- for (int attempt = 1; attempt <= maxRetries; attempt++) {
- try {
- JsonNode response = okHttpUtil.doPostJson(url, pushJson, null);
- log.info("补偿任务: 渠道方推送充电订单信息成功 - chargeOrderNo: {}, firmId: {}, response: {}",
- order.getChargeOrderNo(), order.getFirmId(), response);
- return;
- } catch (Exception e) {
- log.error("补偿任务: 渠道方推送充电订单信息失败(第{}次) - chargeOrderNo: {}, firmId: {}, url: {}, 错误: {}",
- attempt, order.getChargeOrderNo(), order.getFirmId(), url, e.getMessage(), e);
- if (attempt < maxRetries) {
- try {
- Thread.sleep(retryIntervalMs);
- } catch (InterruptedException ie) {
- Thread.currentThread().interrupt();
- break;
- }
- }
- }
- }
- } catch (Exception e) {
- log.error("补偿任务: 构建渠道方推送数据失败 - chargeOrderNo: {}, 错误: {}", order.getChargeOrderNo(), e.getMessage(), e);
- }
- }
- /**
- * 渠道方账户余额扣减 + 记录资金流水
- */
- private Map<String, Object> buildCompensationPushData(ChargeOrderInfo order) throws JsonProcessingException {
- Map<String, Object> pushData = new LinkedHashMap<>();
- if (StrUtil.isNotBlank(order.getChargeDetails())) {
- JsonNode chargeDetailsNode = objectMapper.readTree(order.getChargeDetails());
- if (chargeDetailsNode.isObject()) {
- pushData.putAll(objectMapper.convertValue(chargeDetailsNode, LinkedHashMap.class));
- } else if (chargeDetailsNode.isArray()) {
- pushData.put("ChargeDetails", objectMapper.convertValue(chargeDetailsNode, List.class));
- }
- }
- normalizeCompensationPushData(pushData, order);
- return pushData;
- }
- private void normalizeCompensationPushData(Map<String, Object> pushData, ChargeOrderInfo order) throws JsonProcessingException {
- List<Map<String, Object>> chargeDetails = resolveChargeDetails(pushData, order);
- putIfBlank(pushData, "ArrearsAmt", BigDecimal.ZERO);
- pushData.put("ChargeDetails", chargeDetails);
- pushData.put("SumPeriod", chargeDetails.size());
- putIfBlank(pushData, "ConnectorID", order.getConnectorId());
- putIfBlank(pushData, "EndTime", order.getEndTime());
- putIfBlank(pushData, "OriginElecMoney", defaultDecimal(order.getThirdPartyElecfee()));
- putIfBlank(pushData, "OriginMoney", defaultDecimal(order.getThirdPartyTotalCost()));
- putIfBlank(pushData, "OriginServiceMoney", defaultDecimal(order.getThirdPartyServerfee()));
- putIfBlank(pushData, "PlatOrderId", order.getId());
- putIfBlank(pushData, "ReceiptsAmt", defaultDecimal(order.getThirdPartyTotalCost()));
- putIfBlank(pushData, "StartChargeSeq", order.getStartChargeSeq());
- putIfBlank(pushData, "StartTime", order.getStartTime());
- putIfBlank(pushData, "StopReason", parseIntegerOrDefault(order.getStopReason(), 0));
- putIfBlank(pushData, "TotalElecMoney", defaultDecimal(order.getThirdPartyElecfee()));
- putIfBlank(pushData, "TotalMoney", defaultDecimal(order.getThirdPartyTotalCost()));
- putIfBlank(pushData, "TotalPower", defaultDecimal(order.getTotalCharge()));
- putIfBlank(pushData, "TotalSeviceMoney", defaultDecimal(order.getThirdPartyServerfee()));
- }
- private List<Map<String, Object>> resolveChargeDetails(Map<String, Object> pushData, ChargeOrderInfo order) throws JsonProcessingException {
- List<Map<String, Object>> chargeDetails = convertChargeDetails(pushData.get("ChargeDetails"));
- if (!chargeDetails.isEmpty()) {
- return chargeDetails;
- }
- if (StrUtil.isNotBlank(order.getChargeDetails())) {
- JsonNode chargeDetailsNode = objectMapper.readTree(order.getChargeDetails());
- if (chargeDetailsNode.isArray()) {
- chargeDetails = convertChargeDetails(chargeDetailsNode);
- } else if (chargeDetailsNode.isObject() && chargeDetailsNode.has("ChargeDetails")) {
- chargeDetails = convertChargeDetails(chargeDetailsNode.get("ChargeDetails"));
- }
- }
- if (!chargeDetails.isEmpty()) {
- return chargeDetails;
- }
- return buildFallbackChargeDetails(order);
- }
- private List<Map<String, Object>> convertChargeDetails(Object rawChargeDetails) {
- if (rawChargeDetails == null) {
- return new ArrayList<>();
- }
- List<Map<String, Object>> chargeDetails = new ArrayList<>();
- if (rawChargeDetails instanceof JsonNode jsonNode && jsonNode.isArray()) {
- for (JsonNode detailNode : jsonNode) {
- chargeDetails.add(objectMapper.convertValue(detailNode, LinkedHashMap.class));
- }
- return chargeDetails;
- }
- if (rawChargeDetails instanceof List<?> detailList) {
- for (Object detail : detailList) {
- chargeDetails.add(objectMapper.convertValue(detail, LinkedHashMap.class));
- }
- }
- return chargeDetails;
- }
- private List<Map<String, Object>> buildFallbackChargeDetails(ChargeOrderInfo order) {
- List<Map<String, Object>> chargeDetails = new ArrayList<>();
- if (order == null) {
- return chargeDetails;
- }
- Map<String, Object> detail = new LinkedHashMap<>();
- BigDecimal totalPower = defaultDecimal(order.getTotalCharge());
- BigDecimal totalElecMoney = defaultDecimal(order.getThirdPartyElecfee());
- BigDecimal totalServiceMoney = defaultDecimal(order.getThirdPartyServerfee());
- detail.put("DetailStartTime", order.getStartTime());
- detail.put("DetailEndTime", order.getEndTime());
- detail.put("ItemFlag", resolvePeriodFlag(order));
- detail.put("ElecPrice", calculateUnitPrice(totalElecMoney, totalPower));
- detail.put("SevicePrice", calculateUnitPrice(totalServiceMoney, totalPower));
- detail.put("DetailPower", totalPower);
- detail.put("DetailElecMoney", totalElecMoney);
- detail.put("DetailSeviceMoney", totalServiceMoney);
- chargeDetails.add(detail);
- return chargeDetails;
- }
- private Integer resolvePeriodFlag(ChargeOrderInfo order) {
- if (order == null || StrUtil.isBlank(order.getConnectorId())) {
- return 3;
- }
- ThirdPartyEquipmentPricePolicy pricePolicy = thirdPartyEquipmentPricePolicyMapper.selectOne(
- Wrappers.<ThirdPartyEquipmentPricePolicy>lambdaQuery()
- .eq(ThirdPartyEquipmentPricePolicy::getConnectorId, order.getConnectorId())
- .eq(ThirdPartyEquipmentPricePolicy::getIsDeleted, 0)
- .last("LIMIT 1"));
- if (pricePolicy == null) {
- return 3;
- }
- List<ThirdPartyPolicyInfo> policyInfos = thirdPartyPolicyInfoMapper.selectList(
- Wrappers.<ThirdPartyPolicyInfo>lambdaQuery()
- .eq(ThirdPartyPolicyInfo::getPricePolicyId, pricePolicy.getId())
- .eq(ThirdPartyPolicyInfo::getIsDeleted, 0)
- .orderByAsc(ThirdPartyPolicyInfo::getStartTime));
- if (policyInfos == null || policyInfos.isEmpty()) {
- return 3;
- }
- String chargeStartTime = extractTimePart(order.getStartTime());
- if (StrUtil.isBlank(chargeStartTime)) {
- return policyInfos.get(0).getPeriodFlag() != null ? policyInfos.get(0).getPeriodFlag() : 3;
- }
- ThirdPartyPolicyInfo matchedPolicy = null;
- for (ThirdPartyPolicyInfo policyInfo : policyInfos) {
- if (policyInfo.getStartTime() == null) {
- continue;
- }
- if (chargeStartTime.compareTo(policyInfo.getStartTime()) >= 0) {
- matchedPolicy = policyInfo;
- } else {
- break;
- }
- }
- if (matchedPolicy == null) {
- matchedPolicy = policyInfos.get(policyInfos.size() - 1);
- }
- return matchedPolicy.getPeriodFlag() != null ? matchedPolicy.getPeriodFlag() : 3;
- }
- private String extractTimePart(String dateTime) {
- if (StrUtil.isBlank(dateTime)) {
- return null;
- }
- try {
- return LocalDateTime.parse(dateTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
- .format(DateTimeFormatter.ofPattern("HHmmss"));
- } catch (Exception ex) {
- return null;
- }
- }
- private BigDecimal calculateUnitPrice(BigDecimal amount, BigDecimal power) {
- if (power == null || power.compareTo(BigDecimal.ZERO) <= 0) {
- return BigDecimal.ZERO;
- }
- return amount.divide(power, 4, RoundingMode.HALF_UP);
- }
- private BigDecimal defaultDecimal(BigDecimal value) {
- return value != null ? value : BigDecimal.ZERO;
- }
- private void putIfBlank(Map<String, Object> data, String key, Object value) {
- if (!data.containsKey(key) || isNullLikeValue(data.get(key))) {
- data.put(key, value);
- }
- }
- private boolean isNullLikeValue(Object value) {
- if (value == null) {
- return true;
- }
- if (value instanceof String str) {
- return StrUtil.isBlank(str) || "null".equalsIgnoreCase(str.trim());
- }
- return false;
- }
- private Integer parseIntegerOrDefault(String value, Integer defaultValue) {
- if (StrUtil.isBlank(value) || "null".equalsIgnoreCase(value.trim())) {
- return defaultValue;
- }
- try {
- return Integer.parseInt(value.trim());
- } catch (NumberFormatException ex) {
- return defaultValue;
- }
- }
- private void deductChannelFirmBalance(ChargeOrderInfo order, FirmInfo firmInfo) {
- BigDecimal cost = order.getRealCost();
- if (cost == null || cost.compareTo(BigDecimal.ZERO) <= 0) {
- log.info("补偿任务: 订单{}实际费用为0,跳过渠道方余额扣减", order.getChargeOrderNo());
- return;
- }
- // 记录资金流水
- FirmAccountLog accountLog = new FirmAccountLog();
- accountLog.setFirmId(firmInfo.getId());
- accountLog.setFirmType(firmInfo.getFirmType());
- accountLog.setEventDesc("渠道方充电订单下账(补偿)");
- accountLog.setSerialNo(order.getChargeOrderNo());
- accountLog.setIncomeType(2);
- accountLog.setBeforeChange(firmInfo.getBalance());
- accountLog.setAfterChange(firmInfo.getBalance().subtract(cost));
- accountLog.setMoneyChange(cost);
- firmAccountLogMapper.insert(accountLog);
- // 渠道方账户余额修改
- firmInfo.setBalance(firmInfo.getBalance().subtract(cost));
- firmInfoMapper.updateById(firmInfo);
- log.info("补偿任务: 订单{}渠道方余额扣减完成,扣减金额: {}, 扣减后余额: {}",
- order.getChargeOrderNo(), cost, firmInfo.getBalance());
- }
- @Override
- @Transactional(rollbackFor = Exception.class)
- public String compensateChannelOrderPush() {
- log.info("开始执行渠道方订单推送补偿...");
- // 查询已完成(status=3)、备注为"通过补偿修复处理"的渠道方订单
- List<ChargeOrderInfo> channelOrders = this.list(Wrappers.<ChargeOrderInfo>lambdaQuery()
- .eq(ChargeOrderInfo::getStatus, 3)
- .eq(ChargeOrderInfo::getOrderType, SystemConstants.CHARGE_ORDER_TYPE_CHANNEL)
- .eq(ChargeOrderInfo::getRemark, "通过补偿修复处理")
- );
- if (channelOrders.isEmpty()) {
- log.info("渠道方推送补偿: 没有找到需要补偿的渠道方订单");
- return "没有找到需要补偿的渠道方订单";
- }
- log.info("渠道方推送补偿: 找到{}个需要补偿的渠道方订单", channelOrders.size());
- int totalCount = 0;
- int successCount = 0;
- List<String> failedOrders = new ArrayList<>();
- for (ChargeOrderInfo order : channelOrders) {
- totalCount++;
- try {
- // 优先从third_party_api_log获取原始推送数据
- String apiLogData = null;
- if (order.getStartChargeSeq() != null) {
- ThirdPartyApiLog apiLog = thirdPartyApiLogMapper.selectOne(
- Wrappers.<ThirdPartyApiLog>lambdaQuery()
- .eq(ThirdPartyApiLog::getInterfaceDescription, "推送充电订单信息")
- .like(ThirdPartyApiLog::getDecryptedRequestData, order.getStartChargeSeq())
- .orderByDesc(ThirdPartyApiLog::getCreatedTime)
- .last("LIMIT 1"));
- if (apiLog != null && apiLog.getDecryptedRequestData() != null) {
- apiLogData = apiLog.getDecryptedRequestData();
- }
- }
- compensateChannelOrder(order, apiLogData);
- successCount++;
- log.info("渠道方推送补偿: 订单{}补偿成功", order.getChargeOrderNo());
- } catch (Exception e) {
- log.error("渠道方推送补偿: 订单{}补偿失败: {}", order.getChargeOrderNo(), e.getMessage(), e);
- failedOrders.add(order.getChargeOrderNo());
- }
- }
- String result = String.format("渠道方推送补偿完成!总计: %d, 成功: %d, 失败: %d",
- totalCount, successCount, failedOrders.size());
- if (!failedOrders.isEmpty()) {
- result += ", 失败订单: " + String.join(",", failedOrders);
- }
- log.info(result);
- return result;
- }
- @Override
- public void failOrderDispose() {
- //1.查询所有待启动的订单
- List<ChargeOrderInfo> orderInfoList = this.list(Wrappers.<ChargeOrderInfo>lambdaQuery().eq(ChargeOrderInfo::getStatus, 0).last("LIMIT 1000"));
- //2.遍历判断订单创建时间是否已超过3分钟
- for (ChargeOrderInfo chargeOrderInfo : orderInfoList) {
- LocalDateTime createTime = chargeOrderInfo.getCreateTime();
- if (createTime != null && createTime.plusMinutes(3).isBefore(LocalDateTime.now())) {
- String chargeOrderNo = chargeOrderInfo.getChargeOrderNo();
- try {
- log.info("失败订单处理: 订单{}创建时间已超过3分钟,调用订单修复", chargeOrderNo);
- this.repairOrderByOrderNo(chargeOrderNo);
- } catch (Exception e) {
- log.error("失败订单处理: 订单{}修复失败: {}", chargeOrderNo, e.getMessage(), e);
- }
- }
- }
- }
- @Override
- public void settleOrderFailDispose() {
- //1.查询所有结算中的订单
- List<ChargeOrderInfo> orderInfoList = this.list(Wrappers.<ChargeOrderInfo>lambdaQuery().eq(ChargeOrderInfo::getStatus, 2).last("LIMIT 1000"));
- //2.遍历判断订单结算时间是否已超过5分钟
- for (ChargeOrderInfo chargeOrderInfo : orderInfoList) {
- LocalDateTime settleTime = chargeOrderInfo.getSettleTime();
- if (settleTime != null && settleTime.plusMinutes(5).isBefore(LocalDateTime.now())) {
- String chargeOrderNo = chargeOrderInfo.getChargeOrderNo();
- try {
- log.info("失败订单处理: 订单{}结算时间已超过5分钟,调用订单修复", chargeOrderNo);
- this.repairOrderByOrderNo(chargeOrderNo);
- } catch (Exception e) {
- log.error("失败订单处理: 订单{}修复失败: {}", chargeOrderNo, e.getMessage(), e);
- }
- }
- }
- }
- }
|