ChargingReceptionServiceImpl.java 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987
  1. package com.zsElectric.boot.charging.service.impl;
  2. import cn.hutool.core.bean.BeanUtil;
  3. import cn.hutool.core.util.ObjUtil;
  4. import cn.hutool.core.util.ObjectUtil;
  5. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  6. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  7. import com.fasterxml.jackson.databind.JsonNode;
  8. import com.fasterxml.jackson.databind.ObjectMapper;
  9. import com.zsElectric.boot.business.mapper.*;
  10. import com.zsElectric.boot.business.model.entity.*;
  11. import com.zsElectric.boot.business.service.ChargeOrderInfoService;
  12. import com.zsElectric.boot.charging.entity.*;
  13. import com.zsElectric.boot.charging.mapper.ThirdPartyChargeStatusMapper;
  14. import com.zsElectric.boot.charging.mapper.ThirdPartyConnectorInfoMapper;
  15. import com.zsElectric.boot.charging.mapper.ThirdPartyEquipmentPricePolicyMapper;
  16. import com.zsElectric.boot.charging.mapper.ThirdPartyPolicyInfoMapper;
  17. import com.zsElectric.boot.charging.service.ChargingBusinessService;
  18. import com.zsElectric.boot.charging.service.ChargingReceptionService;
  19. import com.zsElectric.boot.charging.vo.ChargeResponseVO;
  20. import com.zsElectric.boot.charging.vo.QueryStationStatusVO;
  21. import com.zsElectric.boot.common.constant.ConnectivityConstants;
  22. import com.zsElectric.boot.common.constant.SystemConstants;
  23. import com.zsElectric.boot.common.util.DateUtils;
  24. import com.zsElectric.boot.common.util.OkHttpUtil;
  25. import com.zsElectric.boot.common.util.electric.ChargingUtil;
  26. import com.zsElectric.boot.common.util.electric.RequestParmsEntity;
  27. import com.zsElectric.boot.common.util.electric.ResponseParmsEntity;
  28. import com.zsElectric.boot.core.exception.BusinessException;
  29. import com.zsElectric.boot.system.mapper.DictItemMapper;
  30. import com.zsElectric.boot.system.model.entity.DictItem;
  31. import lombok.RequiredArgsConstructor;
  32. import lombok.extern.slf4j.Slf4j;
  33. import org.redisson.api.RLock;
  34. import org.redisson.api.RedissonClient;
  35. import org.springframework.beans.factory.annotation.Autowired;
  36. import org.springframework.beans.factory.annotation.Qualifier;
  37. import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  38. import org.springframework.stereotype.Service;
  39. import org.springframework.util.CollectionUtils;
  40. import java.math.BigDecimal;
  41. import java.math.RoundingMode;
  42. import java.time.LocalDateTime;
  43. import java.time.format.DateTimeFormatter;
  44. import java.util.*;
  45. import java.util.concurrent.CompletableFuture;
  46. import java.util.concurrent.TimeUnit;
  47. import java.util.function.Consumer;
  48. import static com.zsElectric.boot.common.constant.ConnectivityConstants.FAIL_REASON_NONE;
  49. import static com.zsElectric.boot.common.constant.ConnectivityConstants.STATUS_OK;
  50. import static com.zsElectric.boot.common.util.HmacMD5Util.genSign;
  51. import static com.zsElectric.boot.common.util.HmacMD5Util.verify;
  52. /**
  53. * 第三方充电推送接收服务实现
  54. *
  55. * @author system
  56. * @since 2025-12-11
  57. */
  58. @Slf4j
  59. @Service
  60. @RequiredArgsConstructor
  61. public class ChargingReceptionServiceImpl implements ChargingReceptionService {
  62. private final ChargingUtil chargingUtil;
  63. private final ThirdPartyConnectorInfoMapper connectorInfoMapper;
  64. private final ThirdPartyChargeStatusMapper chargeStatusMapper;
  65. private final ObjectMapper objectMapper;
  66. private final ChargeOrderInfoService chargeOrderInfoService;
  67. private final RedissonClient redissonClient;
  68. private final UserAccountMapper userAccountMapper;
  69. private final UserFirmMapper userFirmMapper;
  70. private final FirmInfoMapper firmInfoMapper;
  71. private final PolicyFeeMapper policyFeeMapper;
  72. private final DictItemMapper dictItemMapper;
  73. private final ThirdPartyEquipmentInfoMapper thirdPartyEquipmentInfoMapper;
  74. private final ChargingBusinessService chargingBusinessService;
  75. private final ThirdPartyStationInfoMapper thirdPartyStationInfoMapper;
  76. private final ThirdPartyPolicyInfoMapper thirdPartyPolicyInfoMapper;
  77. private final DiscountsActivityMapper discountsActivityMapper;
  78. private final FirmAccountLogMapper firmAccountLogMapper;
  79. private final OkHttpUtil okHttpUtil;
  80. private final ThirdPartyEquipmentPricePolicyMapper thirdPartyEquipmentPricePolicyMapper;
  81. /**
  82. * 熔断检查锁前缀
  83. */
  84. private static final String BREAK_CHECK_LOCK_KEY = "charging:break:check:";
  85. /**
  86. * 锁等待时间(秒)
  87. */
  88. private static final long LOCK_WAIT_TIME = 3;
  89. /**
  90. * 锁持有时间(秒)
  91. */
  92. private static final long LOCK_LEASE_TIME = 10;
  93. private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
  94. // ==================== 接口实现 ====================
  95. @Override
  96. public ResponseParmsEntity chargeResponse(RequestParmsEntity requestDTO) {
  97. log.info("接收推送启动充电结果请求参数:{}", requestDTO);
  98. return processStartChargeResultRequest(requestDTO);
  99. }
  100. @Override
  101. public ResponseParmsEntity chargeStatusResponse(RequestParmsEntity requestDTO) {
  102. log.info("接收推送充电状态请求参数:{}", requestDTO);
  103. return processChargeStatusRequest(requestDTO);
  104. }
  105. @Override
  106. public ResponseParmsEntity stopChargeResponse(RequestParmsEntity requestDTO) {
  107. log.info("接收推送停止充电结果请求参数:{}", requestDTO);
  108. return processStopChargeResultRequest(requestDTO);
  109. }
  110. @Override
  111. public ResponseParmsEntity chargeOrderResponse(RequestParmsEntity requestDTO) throws Exception {
  112. log.info("接收推送充电订单信息请求参数:{}", requestDTO);
  113. return processChargeRequest(requestDTO, jsonNode -> {
  114. log.debug("充电订单信息 - StartChargeSeq: {}", getTextValue(jsonNode, "StartChargeSeq"));
  115. });
  116. }
  117. @Override
  118. public ResponseParmsEntity stationStatus(RequestParmsEntity requestDTO) {
  119. log.info("接收设备状态变化推送请求参数:{}", requestDTO);
  120. return processStationStatusRequest(requestDTO);
  121. }
  122. // ==================== 公共处理方法 ====================
  123. /**
  124. * 通用充电请求处理模板
  125. */
  126. private ResponseParmsEntity processChargeRequest(RequestParmsEntity requestDTO, Consumer<JsonNode> businessHandler) {
  127. try {
  128. JsonNode jsonNode = verifyAndDecrypt(requestDTO);
  129. //查询订单
  130. String startChargeSeq = getTextValue(jsonNode, "StartChargeSeq");
  131. String stopReason = getTextValue(jsonNode, "StopReason");
  132. String endTime = getTextValue(jsonNode, "EndTime");
  133. String startTime = getTextValue(jsonNode, "StartTime");
  134. String totalPower = getTextValue(jsonNode, "TotalPower");
  135. String totalElecMoney = getTextValue(jsonNode, "TotalElecMoney");
  136. String totalMoney = getTextValue(jsonNode, "TotalMoney");
  137. String totalSeviceMoney = getTextValue(jsonNode, "TotalSeviceMoney");
  138. String connectorID = getTextValue(jsonNode, "ConnectorID");
  139. ChargeOrderInfo chargeOrderInfo = chargeOrderInfoService.getOne(new LambdaQueryWrapper<ChargeOrderInfo>()
  140. .eq(ChargeOrderInfo::getStartChargeSeq, startChargeSeq).last("LIMIT 1"));
  141. if (ObjectUtil.isNotEmpty(chargeOrderInfo)) {
  142. if (Objects.equals(chargeOrderInfo.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_CHANNEL)) {
  143. //订单信息渠道方推送
  144. Map<String, Object> map = objectMapper.convertValue(jsonNode, Map.class);
  145. map.put("chargeOrderNo", chargeOrderInfo.getChargeOrderNo());
  146. FirmInfo firmInfo = firmInfoMapper.selectById(chargeOrderInfo.getFirmId());
  147. if (ObjectUtil.isNotNull(firmInfo)) {
  148. okHttpUtil.doPostForm(firmInfo.getChannelUrl() + "/notification_charge_order_info", map, null);
  149. }
  150. }
  151. //推送订单明细
  152. chargeOrderInfo.setChargeDetails(jsonNode.toString());
  153. //优惠单价
  154. BigDecimal discountPrice = BigDecimal.ZERO;
  155. if (Objects.equals(chargeOrderInfo.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_PLATFORM)) {
  156. //判断当前订单是否为首单
  157. Long userId = chargeOrderInfo.getUserId();
  158. List<ChargeOrderInfo> list = chargeOrderInfoService.list(Wrappers.<ChargeOrderInfo>lambdaQuery().eq(ChargeOrderInfo::getUserId, userId).eq(ChargeOrderInfo::getStatus,
  159. SystemConstants.STATUS_THREE));
  160. if (ObjectUtil.isEmpty(list)) {
  161. DiscountsActivity discountsActivity = discountsActivityMapper.selectOne(Wrappers.<DiscountsActivity>lambdaQuery()
  162. .eq(DiscountsActivity::getType, SystemConstants.STATUS_ONE)
  163. .eq(DiscountsActivity::getStatus, SystemConstants.STATUS_ONE)
  164. .last("LIMIT 1"));
  165. if (ObjectUtil.isNotEmpty(discountsActivity)) {
  166. chargeOrderInfo.setDiscountInfoId(discountsActivity.getId());
  167. chargeOrderInfo.setDiscountDesc(discountsActivity.getActivityDesc());
  168. discountPrice = discountsActivity.getDiscount();
  169. }
  170. }
  171. }
  172. chargeOrderInfo.setStopReason(stopReason);
  173. chargeOrderInfo.setStartTime(startTime);
  174. chargeOrderInfo.setEndTime(endTime);
  175. chargeOrderInfo.setTotalCharge(new BigDecimal(totalPower));
  176. chargeOrderInfo.setThirdPartyTotalCost(new BigDecimal(totalMoney));
  177. chargeOrderInfo.setThirdPartyServerfee(new BigDecimal(totalSeviceMoney));
  178. chargeOrderInfo.setThirdPartyElecfee(new BigDecimal(totalElecMoney));
  179. ThirdPartyConnectorInfo thirdPartyConnectorInfo = connectorInfoMapper.selectOne(Wrappers.<ThirdPartyConnectorInfo>lambdaQuery()
  180. .eq(ThirdPartyConnectorInfo::getConnectorId, connectorID).last("LIMIT 1"));
  181. String stationId = thirdPartyConnectorInfo.getStationId();
  182. ThirdPartyStationInfo thirdPartyStationInfo = thirdPartyStationInfoMapper.selectOne(Wrappers.<ThirdPartyStationInfo>lambdaQuery()
  183. .eq(ThirdPartyStationInfo::getStationId, stationId).last("LIMIT 1"));
  184. if (ObjectUtil.isEmpty(thirdPartyConnectorInfo)) {
  185. log.error("thirdPartyConnectorInfo" + "为空===============================================");
  186. }
  187. //平台服务费
  188. BigDecimal serviceFee = BigDecimal.ZERO;
  189. //优惠金额
  190. BigDecimal discountFee = BigDecimal.ZERO;
  191. JsonNode chargeDetails = jsonNode.get("ChargeDetails");
  192. if (ObjectUtil.isNotEmpty(chargeDetails)) {
  193. for (JsonNode node : chargeDetails) {
  194. //提取字段值
  195. String itemFlag = node.get("ItemFlag").asText();
  196. node.get("DetailPower").asText();
  197. BigDecimal detailPower = new BigDecimal(node.get("DetailPower").asText());
  198. PolicyFee policyFee = policyFeeMapper.selectOne(Wrappers.<PolicyFee>lambdaQuery()
  199. .eq(PolicyFee::getStationInfoId, thirdPartyStationInfo.getId())
  200. .eq(PolicyFee::getPeriodFlag, Integer.parseInt(itemFlag))
  201. .last("LIMIT 1"));
  202. if (ObjectUtil.isNotEmpty(policyFee)) {
  203. BigDecimal opFee = policyFee.getOpFee();
  204. if (ObjectUtil.isNotEmpty(chargeOrderInfo.getDiscountInfoId())) {
  205. opFee = opFee.subtract(discountPrice);
  206. }
  207. log.info("策略费用:{}", opFee);
  208. serviceFee = serviceFee.add(opFee.multiply(detailPower));
  209. discountFee = discountFee.add(discountPrice.multiply(detailPower));
  210. }
  211. }
  212. } else {
  213. log.error("充电订单明细为空===============================================");
  214. if (chargeOrderInfo.getThirdPartyTotalCost().compareTo(BigDecimal.ZERO) > 0 && chargeOrderInfo.getTotalCharge().compareTo(BigDecimal.ZERO) > 0) {
  215. //判断当前订单创建时间在哪个时段
  216. ThirdPartyChargeStatus thirdPartyChargeStatus = chargeStatusMapper.selectOne(Wrappers.<ThirdPartyChargeStatus>lambdaQuery()
  217. .eq(ThirdPartyChargeStatus::getStartChargeSeq, chargeOrderInfo.getStartChargeSeq()));
  218. ThirdPartyEquipmentPricePolicy thirdPartyEquipmentPricePolicy = thirdPartyEquipmentPricePolicyMapper.selectOne(Wrappers.<ThirdPartyEquipmentPricePolicy>lambdaQuery()
  219. .eq(ThirdPartyEquipmentPricePolicy::getConnectorId, thirdPartyChargeStatus.getConnectorId())
  220. .last("LIMIT 1"));
  221. // 查询该价格策略下的所有时段信息,按startTime升序排列
  222. List<ThirdPartyPolicyInfo> allPolicyInfos = thirdPartyPolicyInfoMapper.selectList(Wrappers.<ThirdPartyPolicyInfo>lambdaQuery()
  223. .eq(ThirdPartyPolicyInfo::getPricePolicyId, thirdPartyEquipmentPricePolicy.getId())
  224. .orderByAsc(ThirdPartyPolicyInfo::getStartTime));
  225. // 将充电开始时间和结束时间转换为HHmmss格式字符串进行匹配
  226. LocalDateTime chargeStartTime = thirdPartyChargeStatus.getStartTime();
  227. LocalDateTime chargeEndTime = thirdPartyChargeStatus.getEndTime();
  228. String chargeStartTimeStr = chargeStartTime.format(DateTimeFormatter.ofPattern("HHmmss"));
  229. String chargeEndTimeStr = chargeEndTime.format(DateTimeFormatter.ofPattern("HHmmss"));
  230. // 找到充电开始时间所在的时段索引
  231. int startIndex = -1;
  232. for (int i = allPolicyInfos.size() - 1; i >= 0; i--) {
  233. if (chargeStartTimeStr.compareTo(allPolicyInfos.get(i).getStartTime()) >= 0) {
  234. startIndex = i;
  235. break;
  236. }
  237. }
  238. if (startIndex == -1 && !allPolicyInfos.isEmpty()) {
  239. startIndex = allPolicyInfos.size() - 1;
  240. }
  241. // 找到充电结束时间所在的时段索引
  242. int endIndex = -1;
  243. for (int i = allPolicyInfos.size() - 1; i >= 0; i--) {
  244. if (chargeEndTimeStr.compareTo(allPolicyInfos.get(i).getStartTime()) >= 0) {
  245. endIndex = i;
  246. break;
  247. }
  248. }
  249. if (endIndex == -1 && !allPolicyInfos.isEmpty()) {
  250. endIndex = allPolicyInfos.size() - 1;
  251. }
  252. // 获取充电时间跨越的所有时段集合
  253. List<ThirdPartyPolicyInfo> matchedPolicyInfos = new ArrayList<>();
  254. if (startIndex != -1 && endIndex != -1) {
  255. if (startIndex <= endIndex) {
  256. // 同一天内:从开始时段到结束时段
  257. for (int i = startIndex; i <= endIndex; i++) {
  258. matchedPolicyInfos.add(allPolicyInfos.get(i));
  259. }
  260. } else {
  261. // 跨天情况:从开始时段到最后 + 从第一个到结束时段
  262. for (int i = startIndex; i < allPolicyInfos.size(); i++) {
  263. matchedPolicyInfos.add(allPolicyInfos.get(i));
  264. }
  265. for (int i = 0; i <= endIndex; i++) {
  266. matchedPolicyInfos.add(allPolicyInfos.get(i));
  267. }
  268. }
  269. }
  270. log.info("充电时间段:{} - {},匹配到{}个时段", chargeStartTimeStr, chargeEndTimeStr, matchedPolicyInfos.size());
  271. // 遍历所有匹配的时段,找到费用最高的时段计算服务费
  272. if (!matchedPolicyInfos.isEmpty()) {
  273. PolicyFee maxPolicyFee = null;
  274. ThirdPartyPolicyInfo maxPolicyInfo = null;
  275. for (ThirdPartyPolicyInfo policyInfo : matchedPolicyInfos) {
  276. PolicyFee policyFee = policyFeeMapper.selectOne(Wrappers.<PolicyFee>lambdaQuery()
  277. .eq(PolicyFee::getStationInfoId, thirdPartyStationInfo.getId())
  278. .eq(PolicyFee::getPeriodFlag, policyInfo.getPeriodFlag())
  279. .last("LIMIT 1"));
  280. if (ObjectUtil.isNotEmpty(policyFee)) {
  281. if (maxPolicyFee == null || policyFee.getOpFee().compareTo(maxPolicyFee.getOpFee()) > 0) {
  282. maxPolicyFee = policyFee;
  283. maxPolicyInfo = policyInfo;
  284. }
  285. }
  286. }
  287. if (maxPolicyFee != null) {
  288. log.info("使用最高费用时段:startTime={}, periodFlag={}, opFee={}",
  289. maxPolicyInfo.getStartTime(), maxPolicyInfo.getPeriodFlag(), maxPolicyFee.getOpFee());
  290. serviceFee = serviceFee.add(maxPolicyFee.getOpFee().multiply(chargeOrderInfo.getTotalCharge()));
  291. }
  292. }
  293. }
  294. }
  295. log.info("计算后的平台服务费:{}", serviceFee);
  296. if (chargeOrderInfo.getOrderType().equals(SystemConstants.CHARGE_ORDER_TYPE_PLATFORM)) {
  297. chargeOrderInfo.setDiscountMoney(discountFee);
  298. chargeOrderInfo.setRealServiceCost(serviceFee.setScale(2, RoundingMode.HALF_UP));
  299. //订单结算:平台实际收取金额 = 互联互通金额 + 中数电动金额(平台总服务费)
  300. chargeOrderInfo.setRealCost(chargeOrderInfo.getRealServiceCost().add(chargeOrderInfo.getThirdPartyTotalCost()));
  301. //订单状态->已完成
  302. chargeOrderInfo.setStatus(SystemConstants.STATUS_THREE);
  303. //计算充电时间
  304. chargeOrderInfo.setChargeTime(DateUtils.getDuration(chargeOrderInfo.getStartTime(), chargeOrderInfo.getEndTime()));
  305. //修改订单
  306. chargeOrderInfoService.updateById(chargeOrderInfo);
  307. //账户余额扣减(积分增加)
  308. log.info("执行账户余额扣减(积分增加)");
  309. chargeOrderInfoService.orderSettlement(chargeOrderInfo.getId());
  310. }
  311. if (chargeOrderInfo.getOrderType().equals(SystemConstants.CHARGE_ORDER_TYPE_CHANNEL)) {
  312. chargeOrderInfo.setRealServiceCost(BigDecimal.ZERO);
  313. //订单结算:平台实际收取金额 = 互联互通金额 + 中数电动金额(平台总服务费)
  314. chargeOrderInfo.setRealCost(chargeOrderInfo.getRealServiceCost().add(chargeOrderInfo.getThirdPartyTotalCost()));
  315. //订单状态->已完成
  316. chargeOrderInfo.setStatus(SystemConstants.STATUS_THREE);
  317. //计算充电时间
  318. chargeOrderInfo.setChargeTime(DateUtils.getDuration(chargeOrderInfo.getStartTime(), chargeOrderInfo.getEndTime()));
  319. //修改订单
  320. chargeOrderInfoService.updateById(chargeOrderInfo);
  321. //渠道方账户余额扣减
  322. BigDecimal cost = chargeOrderInfo.getRealCost();
  323. FirmInfo firmInfo = firmInfoMapper.selectById(chargeOrderInfo.getFirmId());
  324. FirmAccountLog accountLog = new FirmAccountLog();
  325. accountLog.setFirmId(firmInfo.getId());
  326. accountLog.setFirmType(firmInfo.getFirmType());
  327. accountLog.setEventDesc("渠道方充电订单下账");
  328. accountLog.setSerialNo(chargeOrderInfo.getChargeOrderNo());
  329. accountLog.setIncomeType(2);
  330. accountLog.setBeforeChange(firmInfo.getBalance());
  331. accountLog.setAfterChange(firmInfo.getBalance().subtract(cost));
  332. accountLog.setMoneyChange(cost);
  333. firmAccountLogMapper.insert(accountLog);
  334. // 渠道方账户余额修改
  335. firmInfo.setBalance(firmInfo.getBalance().subtract(cost));
  336. firmInfoMapper.updateById(firmInfo);
  337. }
  338. }
  339. // 执行业务处理
  340. businessHandler.accept(jsonNode);
  341. // 构建响应
  342. return buildChargeResponse(getTextValue(jsonNode, "StartChargeSeq"));
  343. } catch (BusinessException e) {
  344. throw e;
  345. } catch (Exception e) {
  346. log.error("处理请求失败:{}", e.getMessage());
  347. throw new BusinessException("处理请求失败:" + e.getMessage(), e);
  348. }
  349. }
  350. /**
  351. * 处理启动充电结果推送请求
  352. * 数据格式:{"ConnectorID":"xxx","StartChargeSeq":"xxx","StartChargeSeqStat":2,"StartTime":"xxx"}
  353. */
  354. private ResponseParmsEntity processStartChargeResultRequest(RequestParmsEntity requestDTO) {
  355. try {
  356. JsonNode jsonNode = verifyAndDecrypt(requestDTO);
  357. // 启动充电结果业务处理
  358. String startChargeSeq = getTextValue(jsonNode, "StartChargeSeq");
  359. Integer startChargeSeqStat = getIntValue(jsonNode, "StartChargeSeqStat");
  360. String startTime = getTextValue(jsonNode, "StartTime");
  361. ChargeOrderInfo chargeOrderInfo = chargeOrderInfoService.getOne(new LambdaQueryWrapper<ChargeOrderInfo>()
  362. .eq(ChargeOrderInfo::getStartChargeSeq, startChargeSeq).last("LIMIT 1"));
  363. if (startChargeSeqStat != null) {
  364. switch (startChargeSeqStat) {
  365. case 1 -> log.info("启动中 - StartChargeSeq: {}", startChargeSeq);
  366. case 2 -> {
  367. log.info("充电中 - StartChargeSeq: {}", startChargeSeq);
  368. // 修改订单状态
  369. if (ObjectUtil.isNotEmpty(chargeOrderInfo)) {
  370. if (Objects.equals(chargeOrderInfo.getStatus(), SystemConstants.STATUS_ZERO)) {
  371. chargeOrderInfo.setStatus(SystemConstants.STATUS_ONE);
  372. chargeOrderInfo.setStartTime(startTime);
  373. chargeOrderInfoService.updateById(chargeOrderInfo);
  374. // 推送渠道方启动充电结果
  375. FirmInfo firmInfo = firmInfoMapper.selectById(chargeOrderInfo.getFirmId());
  376. if (ObjectUtil.isNotNull(firmInfo)) {
  377. HashMap<String, Object> map = new HashMap<>();
  378. map.put("chargeOrderNo", chargeOrderInfo.getChargeOrderNo());
  379. map.put("status", SystemConstants.STATUS_ONE);
  380. map.put("startTime", startTime);
  381. okHttpUtil.doPostForm(firmInfo.getChannelUrl() + "/notification_start_charge_result", map, null);
  382. }
  383. }
  384. }
  385. }
  386. case 3 -> log.info("停止中 - StartChargeSeq: {}", startChargeSeq);
  387. case 4 -> log.info("已结束 - StartChargeSeq: {}", startChargeSeq);
  388. case 5 -> log.info("未知 - StartChargeSeq: {}", startChargeSeq);
  389. default -> log.warn("未知状态 - StartChargeSeq: {}, Stat: {}", startChargeSeq, startChargeSeqStat);
  390. }
  391. }
  392. // 构建响应
  393. return buildChargeResponse(startChargeSeq);
  394. } catch (BusinessException e) {
  395. throw e;
  396. } catch (Exception e) {
  397. log.error("处理启动充电结果推送失败:{}", e.getMessage(), e);
  398. throw new BusinessException("处理启动充电结果推送失败:" + e.getMessage(), e);
  399. }
  400. }
  401. /**
  402. * 处理停止充电结果推送请求
  403. * 数据格式:{"ConnectorID":"xxx","FailReason":0,"StartChargeSeq":"xxx","StartChargeSeqStat":4,"SuccStat":0}
  404. */
  405. private ResponseParmsEntity processStopChargeResultRequest(RequestParmsEntity requestDTO) {
  406. try {
  407. JsonNode jsonNode = verifyAndDecrypt(requestDTO);
  408. // 停止充电结果业务处理
  409. String startChargeSeq = getTextValue(jsonNode, "StartChargeSeq");
  410. Integer startChargeSeqStat = getIntValue(jsonNode, "StartChargeSeqStat");
  411. Integer succStat = getIntValue(jsonNode, "SuccStat");
  412. Integer failReason = getIntValue(jsonNode, "FailReason");
  413. log.info("停止充电结果 - StartChargeSeq: {}, Stat: {}, SuccStat: {}, FailReason: {}",
  414. startChargeSeq, startChargeSeqStat, succStat, failReason);
  415. ChargeOrderInfo chargeOrderInfo = chargeOrderInfoService.getOne(new LambdaQueryWrapper<ChargeOrderInfo>()
  416. .eq(ChargeOrderInfo::getStartChargeSeq, startChargeSeq).last("LIMIT 1"));
  417. if (startChargeSeqStat != null && ObjectUtil.isNotEmpty(chargeOrderInfo)) {
  418. switch (startChargeSeqStat) {
  419. case 1 -> log.info("启动中 - StartChargeSeq: {}", startChargeSeq);
  420. case 2 -> log.info("充电中 - StartChargeSeq: {}", startChargeSeq);
  421. case 3 -> log.info("停止中 - StartChargeSeq: {}", startChargeSeq);
  422. case 4 -> {
  423. log.info("已结束 - StartChargeSeq: {}", startChargeSeq);
  424. // 修改订单状态为结算中
  425. if (Objects.equals(chargeOrderInfo.getStatus(), 0) ||
  426. Objects.equals(chargeOrderInfo.getStatus(), 1)) {
  427. chargeOrderInfo.setStatus(SystemConstants.STATUS_TWO);
  428. chargeOrderInfoService.updateById(chargeOrderInfo);
  429. log.info("更新订单状态为结算中 - orderId: {}", chargeOrderInfo.getId());
  430. // 推送停止充电订单结果
  431. FirmInfo firmInfo = firmInfoMapper.selectById(chargeOrderInfo.getFirmId());
  432. if (ObjectUtil.isNotNull(firmInfo)) {
  433. HashMap<String, Object> map = new HashMap<>();
  434. map.put("chargeOrderNo", chargeOrderInfo.getChargeOrderNo());
  435. map.put("status", SystemConstants.STATUS_TWO);
  436. okHttpUtil.doPostForm(firmInfo.getChannelUrl() + "/notification_stop_charge_result", map, null);
  437. }
  438. }
  439. }
  440. case 5 -> log.info("未知 - StartChargeSeq: {}", startChargeSeq);
  441. default -> log.warn("未知状态 - StartChargeSeq: {}, Stat: {}", startChargeSeq, startChargeSeqStat);
  442. }
  443. }
  444. // 构建响应
  445. return buildChargeResponse(startChargeSeq);
  446. } catch (BusinessException e) {
  447. throw e;
  448. } catch (Exception e) {
  449. log.error("处理停止充电结果推送失败:{}", e.getMessage(), e);
  450. throw new BusinessException("处理停止充电结果推送失败:" + e.getMessage(), e);
  451. }
  452. }
  453. /**
  454. * 处理实时充电状态推送请求
  455. * 数据格式:{"ConnectorID":"xxx","ConnectorStatus":3,"TotalPower":1.95,"ElecMoney":1.89,...}
  456. */
  457. private ResponseParmsEntity processChargeStatusRequest(RequestParmsEntity requestDTO) {
  458. try {
  459. JsonNode jsonNode = verifyAndDecrypt(requestDTO);
  460. // 保存或更新充电状态
  461. saveOrUpdateChargeStatus(jsonNode);
  462. // 构建响应
  463. return buildChargeResponse(getTextValue(jsonNode, "StartChargeSeq"));
  464. } catch (BusinessException e) {
  465. throw e;
  466. } catch (Exception e) {
  467. log.error("处理充电状态推送失败:{}", e.getMessage(), e);
  468. throw new BusinessException("处理充电状态推送失败:" + e.getMessage(), e);
  469. }
  470. }
  471. /**
  472. * 处理设备状态变化推送请求
  473. */
  474. private ResponseParmsEntity processStationStatusRequest(RequestParmsEntity requestDTO) {
  475. try {
  476. String decryptData = verifyAndDecryptRaw(requestDTO);
  477. // 解析并更新设备状态
  478. QueryStationStatusVO stationStatusVO = objectMapper.readValue(decryptData, QueryStationStatusVO.class);
  479. updateConnectorStatus(stationStatusVO);
  480. // 构建响应
  481. return buildStatusResponse();
  482. } catch (BusinessException e) {
  483. throw e;
  484. } catch (Exception e) {
  485. log.error("处理设备状态推送失败:{}", e.getMessage());
  486. throw new BusinessException("处理设备状态推送失败:" + e.getMessage(), e);
  487. }
  488. }
  489. /**
  490. * 验签并解密请求数据
  491. */
  492. private JsonNode verifyAndDecrypt(RequestParmsEntity requestDTO) throws Exception {
  493. String decryptData = verifyAndDecryptRaw(requestDTO);
  494. return objectMapper.readTree(decryptData);
  495. }
  496. /**
  497. * 验签并解密请求数据(返回原始字符串)
  498. */
  499. private String verifyAndDecryptRaw(RequestParmsEntity requestDTO) throws Exception {
  500. String signData = requestDTO.getOperatorID() + requestDTO.getData() + requestDTO.getTimeStamp() + requestDTO.getSeq();
  501. if (!verify(signData, ConnectivityConstants.SIG_SECRET, requestDTO.getSig())) {
  502. log.error("数据验签失败");
  503. throw new BusinessException("数据验签失败");
  504. }
  505. String decryptData = chargingUtil.decryptData(requestDTO.getData());
  506. log.info("==================== 解密数据开始 ====================");
  507. log.info("操作员ID: {}", requestDTO.getOperatorID());
  508. log.info("解密后的数据:{}", decryptData);
  509. log.info("==================== 解密数据结束 ====================");
  510. return decryptData;
  511. }
  512. // ==================== 响应构建 ====================
  513. /**
  514. * 构建充电响应
  515. */
  516. private ResponseParmsEntity buildChargeResponse(String startChargeSeq) throws Exception {
  517. ChargeResponseVO chargeResponseVO = new ChargeResponseVO();
  518. chargeResponseVO.setStartChargeSeq(startChargeSeq);
  519. chargeResponseVO.setSuccStat(STATUS_OK);
  520. chargeResponseVO.setFailReason(FAIL_REASON_NONE);
  521. String encryptData = chargingUtil.encryptData(objectMapper.writeValueAsString(chargeResponseVO));
  522. String sign = genSign(STATUS_OK, "请求成功", encryptData, ConnectivityConstants.SIG_SECRET);
  523. ResponseParmsEntity response = new ResponseParmsEntity();
  524. response.setRet(STATUS_OK);
  525. response.setMsg("请求成功");
  526. response.setData(encryptData);
  527. response.setSig(sign);
  528. return response;
  529. }
  530. /**
  531. * 构建设备状态响应
  532. */
  533. private ResponseParmsEntity buildStatusResponse() throws Exception {
  534. Map<String, Integer> statusMap = new HashMap<>();
  535. statusMap.put("Status", 0);
  536. String encryptData = chargingUtil.encryptData(objectMapper.writeValueAsString(statusMap));
  537. String sign = genSign(STATUS_OK, "", encryptData, ConnectivityConstants.SIG_SECRET);
  538. ResponseParmsEntity response = new ResponseParmsEntity();
  539. response.setRet(STATUS_OK);
  540. response.setMsg("");
  541. response.setData(encryptData);
  542. response.setSig(sign);
  543. return response;
  544. }
  545. // ==================== 状态更新 ====================
  546. /**
  547. * 更新充电接口状态
  548. */
  549. private void updateConnectorStatus(QueryStationStatusVO stationStatusVO) {
  550. if (stationStatusVO == null) {
  551. return;
  552. }
  553. // 处理单个连接器状态推送格式:{"ConnectorStatusInfo":{"ConnectorID":"xxx","Status":2}}
  554. ConnectorStatusInfo singleConnector = stationStatusVO.getConnectorStatusInfo();
  555. if (singleConnector != null && singleConnector.getConnectorID() != null) {
  556. connectorInfoMapper.update(null, Wrappers.<ThirdPartyConnectorInfo>lambdaUpdate()
  557. .eq(ThirdPartyConnectorInfo::getConnectorId, singleConnector.getConnectorID())
  558. .set(ThirdPartyConnectorInfo::getStatus, singleConnector.getStatus())
  559. .set(ThirdPartyConnectorInfo::getUpdateTime, LocalDateTime.now()));
  560. log.info("更新充电接口状态(单个) - connectorId: {}, status: {}",
  561. singleConnector.getConnectorID(), singleConnector.getStatus());
  562. return;
  563. }
  564. // 处理批量连接器状态推送格式
  565. if (CollectionUtils.isEmpty(stationStatusVO.getStationStatusInfos())) {
  566. return;
  567. }
  568. for (StationStatusInfo stationStatusInfo : stationStatusVO.getStationStatusInfos()) {
  569. List<ConnectorStatusInfo> connectorStatusInfos = stationStatusInfo.getConnectorStatusInfos();
  570. if (CollectionUtils.isEmpty(connectorStatusInfos)) {
  571. continue;
  572. }
  573. for (ConnectorStatusInfo connectorStatus : connectorStatusInfos) {
  574. connectorInfoMapper.update(null, Wrappers.<ThirdPartyConnectorInfo>lambdaUpdate()
  575. .eq(ThirdPartyConnectorInfo::getConnectorId, connectorStatus.getConnectorID())
  576. .set(ThirdPartyConnectorInfo::getStatus, connectorStatus.getStatus())
  577. .set(ThirdPartyConnectorInfo::getUpdateTime, LocalDateTime.now()));
  578. log.debug("更新充电接口状态 - connectorId: {}, status: {}",
  579. connectorStatus.getConnectorID(), connectorStatus.getStatus());
  580. }
  581. }
  582. }
  583. /**
  584. * 保存或更新充电状态数据
  585. */
  586. private void saveOrUpdateChargeStatus(JsonNode jsonNode) {
  587. try {
  588. log.info("保存或更新充电状态数据 - StartChargeSeq: {}", jsonNode);
  589. String startChargeSeq = jsonNode.get("StartChargeSeq").asText();
  590. String connectorId = getTextValue(jsonNode, "ConnectorID");
  591. // 获取第三方推送的实时数据
  592. BigDecimal totalPower = getDecimalValue(jsonNode, "TotalPower"); // 实际充电度数
  593. BigDecimal totalMoney = getDecimalValue(jsonNode, "TotalMoney"); // 第三方总费用
  594. BigDecimal elecMoney = getDecimalValue(jsonNode, "ElecMoney"); // 第三方电费
  595. BigDecimal serviceMoney = getDecimalValue(jsonNode, "SeviceMoney"); // 第三方服务费
  596. // 修改订单状态并实时更新消费字段
  597. ChargeOrderInfo chargeOrderInfo = chargeOrderInfoService.getOne(new LambdaQueryWrapper<ChargeOrderInfo>()
  598. .eq(ChargeOrderInfo::getStartChargeSeq, startChargeSeq).last("limit 1"));
  599. if(ObjUtil.isNotEmpty(chargeOrderInfo)){
  600. Integer connectorStatus = getIntValue(jsonNode, "ConnectorStatus");
  601. // 计算平台实际收取金额(根据度数和平台价格策略计算)
  602. BigDecimal realCost = calculateRealCost(chargeOrderInfo, totalPower);
  603. if (Objects.equals(connectorStatus, SystemConstants.STATUS_THREE) && Objects.equals(chargeOrderInfo.getStatus(), SystemConstants.STATUS_ZERO)) {
  604. // 充电中
  605. log.info("充电中 - StartChargeSeq: {}", startChargeSeq);
  606. chargeOrderInfo.setStatus(SystemConstants.STATUS_ONE);
  607. chargeOrderInfoService.updateById(chargeOrderInfo);
  608. }
  609. if (Objects.equals(connectorStatus, SystemConstants.STATUS_FOUR) && Objects.equals(chargeOrderInfo.getStatus(),
  610. SystemConstants.STATUS_ONE)) {
  611. // 结算中
  612. log.info("结算中 - StartChargeSeq: {}", startChargeSeq);
  613. chargeOrderInfo.setStatus(SystemConstants.STATUS_TWO);
  614. chargeOrderInfoService.updateById(chargeOrderInfo);
  615. }
  616. log.info("实时更新订单消费 - startChargeSeq: {}, totalPower: {}, realCost: {}",
  617. startChargeSeq, totalPower, realCost);
  618. }
  619. // 查询是否已存在该充电状态记录
  620. ThirdPartyChargeStatus existing = chargeStatusMapper.selectOne(
  621. Wrappers.<ThirdPartyChargeStatus>lambdaQuery()
  622. .eq(ThirdPartyChargeStatus::getStartChargeSeq, startChargeSeq)
  623. );
  624. ThirdPartyChargeStatus chargeStatus = (existing != null) ? existing : new ThirdPartyChargeStatus();
  625. // 设置字段值
  626. chargeStatus.setStartChargeSeq(startChargeSeq);
  627. chargeStatus.setConnectorId(connectorId);
  628. chargeStatus.setConnectorStatus(getIntValue(jsonNode, "ConnectorStatus"));
  629. chargeStatus.setStartChargeSeqStat(getIntValue(jsonNode, "StartChargeSeqStat"));
  630. chargeStatus.setStartTime(parseDateTime(getTextValue(jsonNode, "StartTime")));
  631. chargeStatus.setEndTime(parseDateTime(getTextValue(jsonNode, "EndTime")));
  632. chargeStatus.setTotalPower(totalPower);
  633. chargeStatus.setTotalMoney(totalMoney);
  634. chargeStatus.setElecMoney(elecMoney);
  635. chargeStatus.setServiceMoney(serviceMoney);
  636. chargeStatus.setSoc(getIntValue(jsonNode, "Soc"));
  637. chargeStatus.setVoltageA(getDecimalValue(jsonNode, "VoltageA"));
  638. chargeStatus.setVoltageB(getDecimalValue(jsonNode, "VoltageB"));
  639. chargeStatus.setVoltageC(getDecimalValue(jsonNode, "VoltageC"));
  640. chargeStatus.setCurrentA(getDecimalValue(jsonNode, "CurrentA"));
  641. chargeStatus.setCurrentB(getDecimalValue(jsonNode, "CurrentB"));
  642. chargeStatus.setCurrentC(getDecimalValue(jsonNode, "CurrentC"));
  643. // 兼容处理:如果有ChargeDetails字段,则存储为JSON字符串
  644. if (jsonNode.has("ChargeDetails") && !jsonNode.get("ChargeDetails").isNull()) {
  645. chargeStatus.setChargeDetails(jsonNode.get("ChargeDetails").toString());
  646. }
  647. if (existing != null) {
  648. chargeStatus.setUpdateTime(LocalDateTime.now());
  649. chargeStatusMapper.updateById(chargeStatus);
  650. log.info("更新充电状态成功 - startChargeSeq: {}", startChargeSeq);
  651. } else {
  652. chargeStatus.setCreateTime(LocalDateTime.now());
  653. chargeStatusMapper.insert(chargeStatus);
  654. log.info("新增充电状态成功 - startChargeSeq: {}", startChargeSeq);
  655. }
  656. //异步推送渠道方充电状态
  657. pushChargeStatusTask(startChargeSeq, chargeStatus);
  658. // 熔断保护 - 余额不足判断
  659. isNeedBreak(chargeStatus, chargeOrderInfo);
  660. } catch (Exception e) {
  661. log.error("保存充电状态数据失败", e);
  662. }
  663. }
  664. @Autowired
  665. @Qualifier("businessTaskExecutor")
  666. private ThreadPoolTaskExecutor businessTaskExecutor;
  667. /**
  668. * 异步推送渠道方充电状态
  669. */
  670. public CompletableFuture<Void> pushChargeStatusTask(String startChargeSeq, ThirdPartyChargeStatus chargeStatus) {
  671. return CompletableFuture.runAsync(() -> {
  672. log.info("异步推送渠道方充电状态 - {}", startChargeSeq);
  673. //通过startChargeSeq查询订单详情
  674. ChargeOrderInfo chargeOrderInfo =
  675. chargeOrderInfoService.getOne(Wrappers.<ChargeOrderInfo>lambdaQuery().eq(ChargeOrderInfo::getStartChargeSeq, startChargeSeq).last("limit 1"));
  676. if (ObjUtil.isNotEmpty(chargeOrderInfo)) {
  677. if (Objects.equals(chargeOrderInfo.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_CHANNEL)) {
  678. Long firmId = chargeOrderInfo.getFirmId();
  679. FirmInfo firmInfo = firmInfoMapper.selectById(firmId);
  680. String channelUrl = firmInfo.getChannelUrl() + "/notification_equip_charge_status";
  681. // 推送充电状态
  682. okHttpUtil.doPostForm(channelUrl, BeanUtil.beanToMap(chargeStatus), null);
  683. }
  684. }
  685. }, businessTaskExecutor);
  686. }
  687. /**
  688. * 计算平台实际收取金额
  689. * 根据充电度数 * 当前时段的平台价格策略计算(支持跨时段计费)
  690. */
  691. private BigDecimal calculateRealCost(ChargeOrderInfo chargeOrderInfo, BigDecimal totalPower) {
  692. if (totalPower == null || totalPower.compareTo(BigDecimal.ZERO) <= 0) {
  693. return BigDecimal.ZERO;
  694. }
  695. try {
  696. // 获取用户企业信息
  697. UserFirm userFirm = userFirmMapper.selectOne(Wrappers.<UserFirm>lambdaQuery()
  698. .eq(UserFirm::getUserId, chargeOrderInfo.getUserId())
  699. .eq(UserFirm::getIsDeleted, 0));
  700. // 获取设备信息
  701. ThirdPartyEquipmentInfo equipmentInfo = thirdPartyEquipmentInfoMapper.selectOne(
  702. Wrappers.<ThirdPartyEquipmentInfo>lambdaQuery()
  703. .eq(ThirdPartyEquipmentInfo::getEquipmentId, chargeOrderInfo.getEquipmentId())
  704. .eq(ThirdPartyEquipmentInfo::getIsDeleted, 0));
  705. if (equipmentInfo == null) {
  706. log.warn("未找到设备信息 - equipmentId: {}", chargeOrderInfo.getEquipmentId());
  707. return BigDecimal.ZERO;
  708. }
  709. // 获取站点信息
  710. ThirdPartyStationInfo stationInfo = thirdPartyStationInfoMapper.selectOne(
  711. Wrappers.<ThirdPartyStationInfo>lambdaQuery()
  712. .eq(ThirdPartyStationInfo::getStationId, equipmentInfo.getStationId())
  713. .eq(ThirdPartyStationInfo::getIsDeleted, 0));
  714. if (stationInfo == null) {
  715. log.warn("未找到站点信息 - stationId: {}", equipmentInfo.getStationId());
  716. return BigDecimal.ZERO;
  717. }
  718. // 获取当前时间,格式为 HHmmss
  719. String currentTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HHmmss"));
  720. // 根据当前时间查询当前时段的价格策略(核心:支持跨时段计费)
  721. ThirdPartyPolicyInfo currentPeriodPolicy = thirdPartyPolicyInfoMapper
  722. .selectCurrentPeriodPolicyByStation(stationInfo.getId(), currentTime);
  723. if (currentPeriodPolicy == null || currentPeriodPolicy.getPeriodFlag() == null) {
  724. log.warn("未找到当前时段价格策略 - stationInfoId: {}, currentTime: {}",
  725. stationInfo.getId(), currentTime);
  726. return BigDecimal.ZERO;
  727. }
  728. Integer currentPeriodFlag = currentPeriodPolicy.getPeriodFlag();
  729. log.info("当前时段 - stationInfoId: {}, currentTime: {}, periodFlag: {}",
  730. stationInfo.getId(), currentTime, currentPeriodFlag);
  731. // 查询当前时段对应的价格策略
  732. Integer salesType = (userFirm != null) ? 1 : 0; // 1-企业 0-平台
  733. Long firmId = (userFirm != null) ? userFirm.getFirmId() : null;
  734. PolicyFee policyFee = policyFeeMapper.selectOne(Wrappers.<PolicyFee>lambdaQuery()
  735. .eq(PolicyFee::getStationInfoId, stationInfo.getId())
  736. .eq(PolicyFee::getPeriodFlag, currentPeriodFlag)
  737. .eq(PolicyFee::getSalesType, salesType)
  738. .eq(salesType == 1, PolicyFee::getFirmId, firmId)
  739. .eq(PolicyFee::getIsDeleted, 0)
  740. .last("LIMIT 1"));
  741. if (policyFee == null) {
  742. log.warn("未找到当前时段的价格策略 - stationInfoId: {}, periodFlag: {}, salesType: {}",
  743. stationInfo.getId(), currentPeriodFlag, salesType);
  744. return BigDecimal.ZERO;
  745. }
  746. // 获取综合销售费作为单价
  747. BigDecimal unitPrice = policyFee.getCompSalesFee() != null ? policyFee.getCompSalesFee() : BigDecimal.ZERO;
  748. // 实际收取金额 = 充电度数 * 当前时段单价
  749. BigDecimal realCost = totalPower.multiply(unitPrice).setScale(2, BigDecimal.ROUND_HALF_UP);
  750. log.info("计算实际收取金额 - totalPower: {}, periodFlag: {}, unitPrice: {}, realCost: {}",
  751. totalPower, currentPeriodFlag, unitPrice, realCost);
  752. return realCost;
  753. } catch (Exception e) {
  754. log.error("计算平台实际收取金额失败", e);
  755. return BigDecimal.ZERO;
  756. }
  757. }
  758. // ==================== JSON解析工具方法 ====================
  759. private String getTextValue(JsonNode node, String fieldName) {
  760. JsonNode field = node.get(fieldName);
  761. return (field != null && !field.isNull()) ? field.asText() : null;
  762. }
  763. private Integer getIntValue(JsonNode node, String fieldName) {
  764. JsonNode field = node.get(fieldName);
  765. return (field != null && !field.isNull()) ? field.asInt() : null;
  766. }
  767. private BigDecimal getDecimalValue(JsonNode node, String fieldName) {
  768. JsonNode field = node.get(fieldName);
  769. return (field != null && !field.isNull()) ? new BigDecimal(field.asText()) : null;
  770. }
  771. private LocalDateTime parseDateTime(String dateTimeStr) {
  772. if (dateTimeStr == null || dateTimeStr.isEmpty()) {
  773. return null;
  774. }
  775. try {
  776. return LocalDateTime.parse(dateTimeStr, DATE_TIME_FORMATTER);
  777. } catch (Exception e) {
  778. log.warn("解析时间失败: {}", dateTimeStr);
  779. return null;
  780. }
  781. }
  782. /**
  783. * 根据充电订单号StartChargeSeq 获取订单信息判断是否需要熔断,提前跳枪
  784. * 使用分布式锁防止并发重复检查
  785. *
  786. * @param chargeStatus 充电状态
  787. * @param chargeOrderInfo 订单信息
  788. */
  789. private void isNeedBreak(ThirdPartyChargeStatus chargeStatus, ChargeOrderInfo chargeOrderInfo) {
  790. // 订单为空或不是充电中状态,不需要熔断检查
  791. if (chargeOrderInfo == null || !Objects.equals(chargeOrderInfo.getStatus(), SystemConstants.STATUS_ONE)) {
  792. return;
  793. }
  794. String lockKey = BREAK_CHECK_LOCK_KEY + chargeStatus.getStartChargeSeq();
  795. RLock lock = redissonClient.getLock(lockKey);
  796. try {
  797. // 尝试获取锁,最多等待3秒,持有锁最多10秒
  798. if (lock.tryLock(LOCK_WAIT_TIME, LOCK_LEASE_TIME, TimeUnit.SECONDS)) {
  799. try {
  800. // 获取安全阈值配置
  801. DictItem dictItem = dictItemMapper.selectOne(
  802. new LambdaQueryWrapper<DictItem>()
  803. .eq(DictItem::getDictCode, "up_recharge")
  804. .eq(DictItem::getStatus, 1)
  805. .last("LIMIT 1"));
  806. BigDecimal safetyThreshold = BigDecimal.ZERO;
  807. if (dictItem != null && dictItem.getValue() != null) {
  808. safetyThreshold = new BigDecimal(dictItem.getValue());
  809. }
  810. // 获取用户余额
  811. UserAccount userAccount = userAccountMapper.selectOne(Wrappers.<UserAccount>lambdaQuery()
  812. .eq(UserAccount::getUserId, chargeOrderInfo.getUserId())
  813. .eq(UserAccount::getIsDeleted, 0));
  814. if (userAccount == null) {
  815. log.warn("未找到用户账户信息 - userId: {}", chargeOrderInfo.getUserId());
  816. return;
  817. }
  818. BigDecimal balance = userAccount.getBalance() != null ? userAccount.getBalance() : BigDecimal.ZERO;
  819. // 获取当前实时消费金额
  820. BigDecimal realCost = chargeOrderInfo.getRealCost() != null ? chargeOrderInfo.getRealCost() : BigDecimal.ZERO;
  821. // 剩余余额 = 账户余额 - 实时消费
  822. BigDecimal remainingBalance = balance.subtract(realCost);
  823. log.info("熔断检查 - startChargeSeq: {}, 用户余额: {}, 实时消费: {}, 安全阈值: {}, 剩余余额: {}",
  824. chargeStatus.getStartChargeSeq(), balance, realCost, safetyThreshold, remainingBalance);
  825. // 熔断条件:剩余余额 < 安全阈值 且 已产生实际消费(实时消费 > 0)
  826. // 这样可以避免刚开始充电(消费为0)时因余额小于阈值而被误熔断
  827. if (remainingBalance.compareTo(safetyThreshold) < 0 && realCost.compareTo(BigDecimal.ZERO) > 0) {
  828. log.warn("余额不足,触发熔断停止充电 - startChargeSeq: {}, 用户余额: {}, 实时消费: {}",
  829. chargeStatus.getStartChargeSeq(), balance, realCost);
  830. // 调用第三方停止充电接口
  831. try {
  832. chargingBusinessService.stopCharging(
  833. chargeStatus.getStartChargeSeq(),
  834. chargeStatus.getConnectorId());
  835. log.info("已发送停止充电请求 - startChargeSeq: {}, connectorId: {}",
  836. chargeStatus.getStartChargeSeq(), chargeStatus.getConnectorId());
  837. // 更新订单停止类型为余额不足停止
  838. chargeOrderInfo.setStopType(3); // 3-余额不足停止
  839. chargeOrderInfo.setStopReason("余额不足,系统自动停止充电");
  840. chargeOrderInfoService.updateById(chargeOrderInfo);
  841. } catch (Exception e) {
  842. log.error("调用第三方停止充电接口失败 - startChargeSeq: {}",
  843. chargeStatus.getStartChargeSeq(), e);
  844. }
  845. }
  846. } finally {
  847. lock.unlock();
  848. }
  849. } else {
  850. log.warn("获取熔断检查锁超时 - startChargeSeq: {}", chargeStatus.getStartChargeSeq());
  851. }
  852. } catch (InterruptedException e) {
  853. Thread.currentThread().interrupt();
  854. log.error("获取熔断检查锁被中断 - startChargeSeq: {}", chargeStatus.getStartChargeSeq(), e);
  855. } catch (Exception e) {
  856. log.error("熔断检查失败 - startChargeSeq: {}", chargeStatus.getStartChargeSeq(), e);
  857. }
  858. }
  859. }