diff --git a/jsowell-admin/src/test/java/SpringBootTestController.java b/jsowell-admin/src/test/java/SpringBootTestController.java index fcba4b536..dd2696d0f 100644 --- a/jsowell-admin/src/test/java/SpringBootTestController.java +++ b/jsowell-admin/src/test/java/SpringBootTestController.java @@ -71,6 +71,7 @@ import com.jsowell.service.OrderService; import com.jsowell.service.PileService; import com.jsowell.service.TempService; import com.jsowell.thirdparty.amap.service.AMapService; +import com.jsowell.thirdparty.huawei.HuaWeiService; import com.jsowell.thirdparty.lianlian.dto.CommonParamsDTO; import com.jsowell.thirdparty.lianlian.service.LianLianService; import com.jsowell.thirdparty.lianlian.util.Cryptos; @@ -244,6 +245,9 @@ public class SpringBootTestController { @Autowired private MemberGroupService memberGroupService; + @Autowired + private HuaWeiService huaWeiService; + @Test public void queryPaymentRefundTest() { String paymentId = "002212023122615542010585629628950949888"; @@ -277,6 +281,15 @@ public class SpringBootTestController { // System.out.println(future.toString()); } + @Test + public void testDeliverEquipBusinessPolicy() { + + String result = huaWeiService.deliverEquipBusinessPolicy("test123451694073123456", "8823000000073501"); + + System.out.println(result); + } + + @Test public void testDiscount() { OrderBasicInfo orderBasicInfo = new OrderBasicInfo(); diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/dto/huawei/DeliverEquipBusinessDTO.java b/jsowell-pile/src/main/java/com/jsowell/pile/dto/huawei/DeliverEquipBusinessDTO.java new file mode 100644 index 000000000..1031a61b4 --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/dto/huawei/DeliverEquipBusinessDTO.java @@ -0,0 +1,128 @@ +package com.jsowell.pile.dto.huawei; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +/** + * 下发计费策略DTO + * + * @author Lemon + * @Date 2024/1/14 10:00:04 + */ +@Data +public class DeliverEquipBusinessDTO { + /** + * 策略下发流水号 + */ + @JsonProperty(value = "EquipBizSeq") + private String equipBizSeq; + + /** + * 充电业务策略的数量 + * 大小与需要下发计费策略的充电枪数量一致。 + */ + @JsonProperty(value = "SumChargePolicyInfos") + private Integer sumChargePolicyInfos; + + /** + * 充电业务策略信息 + */ + @JsonProperty(value = "ChargePolicyInfos") + private List chargePolicyInfos; + + + /** + * 充电业务策略信息 + */ + @Data + public static class ChargePolicyInfo{ + /** + * 充电设备接口编码 + */ + @JsonProperty(value = "ConnectorID") + private String connectorID; + + /** + * 业务策略信息体 + * 数组大小为1,当前一把充电枪只支持一条充电计费策略。 + */ + @JsonProperty(value = "PricePolicyInfos") + private List pricePolicyInfos; + + + + @Data + public static class PricePolicyInfo { + /** + * 计费策略ID + */ + @JsonProperty(value = "EquipBizID") + private String equipBizID; + + /** + * 执行状态 + * 0:无效 + * 1:有效 + */ + @JsonProperty(value = "ValidStat") + private Integer validStat; + + /** + * 生效时间 + * 格式“yyyy-mm-dd HHmmss”。不能超过2100年。 + */ + @JsonProperty(value = "ValidStartTime") + private String validStartTime; + + /** + * 失效时间 + * 格式“yyyy-mm-dd HHmmss”。不能超过2100年 + */ + @JsonProperty(value = "ValidEndTime") + private String validEndTime; + + /** + * 时段数N + * 1~48 + */ + @JsonProperty(value = "SumPeriod") + private Integer sumPeriod; + + /** + * 计费信息 + * 单项业务策略信息体,数组大小与时段数N数量一致。 + */ + @JsonProperty(value = "PolicyInfos") + private List policyInfos; + + + @Data + public static class PolicyInfo { + + /** + * 时段起始时间点 + * 格式“HHmmss”,6字符。 + */ + @JsonProperty(value = "StartTime") + private String startTime; + + /** + * 时段电费 + * 小数点后4位 + */ + @JsonProperty(value = "ElecPrice") + private BigDecimal elecPrice; + + /** + * 时段服务费 + * 小数点后4位 + */ + @JsonProperty(value = "SevicePrice") + private BigDecimal sevicePrice; + } + } + } +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/dto/huawei/ReceiveDeliverDTO.java b/jsowell-pile/src/main/java/com/jsowell/pile/dto/huawei/ReceiveDeliverDTO.java new file mode 100644 index 000000000..77c628522 --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/dto/huawei/ReceiveDeliverDTO.java @@ -0,0 +1,78 @@ +package com.jsowell.pile.dto.huawei; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +/** + * 接收计费策略结果DTO + * + * @author Lemon + * @Date 2024/1/14 15:59:04 + */ +@Data +public class ReceiveDeliverDTO { + private String operatorId; + + /** + * 策略下发流水号 + */ + @JsonProperty(value = "EquipBizSeq") + private String equipBizSeq; + + /** + * 业务策略结果返回的数量 + * + * 一把枪当前下发后,响应业务策略信息执行结果的数量, + * 生效结果支持多次返回,一次可以返回1个或多个,一个主机下某个计费长时间无结果,不阻塞其他枪充电。 + */ + @JsonProperty(value = "SumChargePolicyInfoRets") + private Integer sumChargePolicyInfoRets; + + /** + * 充电业务策略返回信息 + * + * 批量不超过100把枪,每把枪返回对应的信息。 + */ + @JsonProperty(value = "ChargePolicyInfoRets") + private List chargePolicyInfoRets; + + @Data + public static class ChargePolicyInfoRet { + + /** + * 充电设备接口编码 + */ + @JsonProperty(value = "ConnectorID") + private String connectorID; + + /** + * 计费策略ID + * + * 格式“运营商ID+唯一编号” ,不超过(<=27字符)。 + */ + @JsonProperty(value = "EquipBizID") + private String equipBizID; + + /** + * 操作结果 + * 0:成功 + * 1:失败 + */ + @JsonProperty(value = "SuccStat") + private Integer succStat; + + /** + * 失败原因 + * 0:无 + * 1:设备离线 + * 2:设备响应超时 + * 3:策略下发设备失败 + * 4:服务异常 + * 5:此充电枪不存在 + */ + @JsonProperty(value = "FailReason") + private Integer failReason; + } +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/PileBillingTemplateServiceImpl.java b/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/PileBillingTemplateServiceImpl.java index 77f6dd9f5..efbdde168 100644 --- a/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/PileBillingTemplateServiceImpl.java +++ b/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/PileBillingTemplateServiceImpl.java @@ -566,6 +566,12 @@ public class PileBillingTemplateServiceImpl implements PileBillingTemplateServic vo.setElectricityPrice(billingTemplateVO.getValleyElectricityPrice().toString()); vo.setServicePrice(billingTemplateVO.getValleyServicePrice().toString()); } + // 计费模板id + vo.setTemplateId(billingTemplateVO.getTemplateId()); + // 计费模板编号 + vo.setTemplateCode(billingTemplateVO.getTemplateCode()); + // 发布时间 + vo.setPublishTime(billingTemplateVO.getPublishTime()); // 总费用 vo.setTotalPrice(new BigDecimal(vo.getElectricityPrice()).add(new BigDecimal(vo.getServicePrice())).toString()); // 开始时间 diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/BillingPriceVO.java b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/BillingPriceVO.java index bd1e818c2..fee58ec53 100644 --- a/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/BillingPriceVO.java +++ b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/BillingPriceVO.java @@ -47,4 +47,13 @@ public class BillingPriceVO { * @see BillingTimeEnum */ private String timeType; + + // 计费模板id + private String templateId; + + // 计费模板编号 + private String templateCode; + + // 发布时间 + private String publishTime; } diff --git a/jsowell-thirdparty/src/main/java/com/jsowell/thirdparty/huawei/HuaWeiService.java b/jsowell-thirdparty/src/main/java/com/jsowell/thirdparty/huawei/HuaWeiService.java index afd69c7f2..c499d2a24 100644 --- a/jsowell-thirdparty/src/main/java/com/jsowell/thirdparty/huawei/HuaWeiService.java +++ b/jsowell-thirdparty/src/main/java/com/jsowell/thirdparty/huawei/HuaWeiService.java @@ -5,6 +5,8 @@ import com.jsowell.pile.dto.QueryEquipChargeStatusDTO; import com.jsowell.pile.dto.QueryEquipmentDTO; import com.jsowell.pile.dto.QueryStartChargeDTO; import com.jsowell.pile.dto.QueryStationInfoDTO; +import com.jsowell.pile.dto.huawei.DeliverEquipBusinessDTO; +import com.jsowell.pile.dto.huawei.ReceiveDeliverDTO; import com.jsowell.thirdparty.lianlian.dto.CommonParamsDTO; import com.jsowell.thirdparty.zhongdianlian.dto.ZDLGetTokenDTO; @@ -148,4 +150,39 @@ public interface HuaWeiService { * @return */ String pushChargeOrderInfo(String orderCode); + + + /** + * 请求计费策略 + * 平台 <-- 华为 + * + * request_equip_business_policy + * + * @param dto + * @return + */ + Map requestEquipBusinessPolicy(QueryStartChargeDTO dto); + + /** + * 下发计费策略 + * 平台 --> 华为 + * + * deliver_equip_business_policy + * + * @param equipBizSeq 策略下发流水号 + * @param pileConnectorCode 枪口号 + * @return + */ + String deliverEquipBusinessPolicy(String equipBizSeq, String pileConnectorCode); + + + /** + * 下发计费策略响应 + * 平台 <-- 华为 + * + * notification_deliver_equip_business_policy_result + * + * @return + */ + Map receiveDeliverEquipBusinessPolicyResult(ReceiveDeliverDTO dto); } diff --git a/jsowell-thirdparty/src/main/java/com/jsowell/thirdparty/huawei/impl/HuaWeiServiceImpl.java b/jsowell-thirdparty/src/main/java/com/jsowell/thirdparty/huawei/impl/HuaWeiServiceImpl.java index e0de462a6..23b42da1f 100644 --- a/jsowell-thirdparty/src/main/java/com/jsowell/thirdparty/huawei/impl/HuaWeiServiceImpl.java +++ b/jsowell-thirdparty/src/main/java/com/jsowell/thirdparty/huawei/impl/HuaWeiServiceImpl.java @@ -1,19 +1,43 @@ package com.jsowell.thirdparty.huawei.impl; +import com.alibaba.fastjson2.JSONObject; +import com.google.common.collect.Maps; +import com.jsowell.common.constant.Constants; +import com.jsowell.common.util.BytesUtil; +import com.jsowell.common.util.StringUtils; +import com.jsowell.pile.domain.PileBasicInfo; +import com.jsowell.pile.domain.ThirdPartyPlatformConfig; +import com.jsowell.pile.domain.ThirdPartyStationRelation; import com.jsowell.pile.dto.QueryEquipChargeStatusDTO; import com.jsowell.pile.dto.QueryEquipmentDTO; import com.jsowell.pile.dto.QueryStartChargeDTO; import com.jsowell.pile.dto.QueryStationInfoDTO; +import com.jsowell.pile.dto.huawei.DeliverEquipBusinessDTO; +import com.jsowell.pile.dto.huawei.ReceiveDeliverDTO; +import com.jsowell.pile.service.*; +import com.jsowell.pile.vo.base.ThirdPartyStationRelationVO; +import com.jsowell.pile.vo.uniapp.BillingPriceVO; import com.jsowell.thirdparty.huawei.HuaWeiService; import com.jsowell.thirdparty.lianlian.dto.CommonParamsDTO; +import com.jsowell.thirdparty.lianlian.util.Cryptos; +import com.jsowell.thirdparty.lianlian.util.Encodes; +import com.jsowell.thirdparty.lianlian.util.GBSignUtils; +import com.jsowell.thirdparty.lianlian.util.HttpRequestUtil; import com.jsowell.thirdparty.zhongdianlian.dto.ZDLGetTokenDTO; import com.jsowell.thirdparty.zhongdianlian.service.ZDLService; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.io.UnsupportedEncodingException; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; /** * 华为 Service @@ -28,6 +52,21 @@ public class HuaWeiServiceImpl implements HuaWeiService { @Autowired private ZDLService zdlService; + @Autowired + private ThirdPartyPlatformConfigService thirdPartyPlatformConfigService; + + @Autowired + private PileBasicInfoService pileBasicInfoService; + + @Autowired + private PileBillingTemplateService pileBillingTemplateService; + + @Autowired + private ThirdPartyStationRelationService thirdPartyStationRelationService; + + @Autowired + private PileMerchantInfoService pileMerchantInfoService; + /** * 获取令牌 * @param dto @@ -199,4 +238,220 @@ public class HuaWeiServiceImpl implements HuaWeiService { public String pushChargeOrderInfo(String orderCode) { return zdlService.pushChargeOrderInfo(orderCode); } + + /** + * 请求计费策略 + * + * request_equip_business_policy + * + * @param dto + * @return + */ + @Override + public Map requestEquipBusinessPolicy(QueryStartChargeDTO dto) { + String pileConnectorCode = dto.getConnectorID(); + String equipBizSeq = dto.getEquipBizSeq(); + // 根据枪口号查询计费模板,并返回信息 + ThirdPartyPlatformConfig configInfo = thirdPartyPlatformConfigService.getInfoByOperatorId(dto.getOperatorId()); + if (configInfo == null) { + return null; + } + // 截取桩号 + String pileSn = StringUtils.substring(pileConnectorCode, 0, 14); + // 查询该桩的站点id + PileBasicInfo pileBasicInfo = pileBasicInfoService.selectPileBasicInfoBySN(pileSn); + // 根据桩号查询正在使用的计费模板 + List billingPriceVOList = pileBillingTemplateService.queryBillingPrice(String.valueOf(pileBasicInfo.getStationId())); + + JSONObject resultJson = new JSONObject(); + resultJson.put("EquipBizSeq", equipBizSeq); + resultJson.put("ConnectorID", pileConnectorCode); + if (CollectionUtils.isEmpty(billingPriceVOList)) { + // 为空说明未查到该枪口的计费模板 + resultJson.put("SuccStat", 1); + resultJson.put("FailReason", 1); + return null; + }else { + // 延时 500ms,异步调用 下发计费策略 接口 + CompletableFuture.runAsync(() -> { + try { + Thread.sleep(500); + } catch (Exception e) { + e.printStackTrace(); + } + String result = deliverEquipBusinessPolicy(equipBizSeq, pileConnectorCode); + log.info("华为 异步调用 下发计费策略 接口 result:{}", result); + }); + + resultJson.put("SuccStat", 0); + resultJson.put("FailReason", 0); + } + // 加密 + Map resultMap = Maps.newLinkedHashMap(); + // 加密数据 + byte[] encryptText = Cryptos.aesEncrypt(resultJson.toJSONString().getBytes(), + configInfo.getDataSecret().getBytes(), configInfo.getDataSecretIv().getBytes()); + String encryptData = Encodes.encodeBase64(encryptText); + + resultMap.put("Data", encryptData); + // 生成sig + String resultSign = GBSignUtils.sign(resultMap, configInfo.getSignSecret()); + resultMap.put("Sig", resultSign); + + return resultMap; + } + + /** + * 下发计费策略 + * @param equipBizSeq 策略下发流水号 + * @param pileConnectorCode 枪口号 + * @return + */ + @Override + public String deliverEquipBusinessPolicy(String equipBizSeq, String pileConnectorCode) { + DeliverEquipBusinessDTO params = new DeliverEquipBusinessDTO(); + + List chargePolicyInfos = new ArrayList<>(); + DeliverEquipBusinessDTO.ChargePolicyInfo chargePolicyInfo = new DeliverEquipBusinessDTO.ChargePolicyInfo(); + + List pricePolicyInfos = new ArrayList<>(); + DeliverEquipBusinessDTO.ChargePolicyInfo.PricePolicyInfo pricePolicyInfo = new DeliverEquipBusinessDTO.ChargePolicyInfo.PricePolicyInfo(); + + List policyInfoList = new ArrayList<>(); + DeliverEquipBusinessDTO.ChargePolicyInfo.PricePolicyInfo.PolicyInfo policyInfo = null; + + // 截取桩号 + String pileSn = StringUtils.substring(pileConnectorCode, 0, 14); + // 查询该桩的站点id + PileBasicInfo pileBasicInfo = pileBasicInfoService.selectPileBasicInfoBySN(pileSn); + String stationId = String.valueOf(pileBasicInfo.getStationId()); + // 获取运营商组织结构代码 + // MerchantInfoVO merchantInfoVO = pileMerchantInfoService.getMerchantInfoVO(String.valueOf(pileBasicInfo.getMerchantId())); + // String organizationCode = merchantInfoVO.getOrganizationCode(); + // 通过站点id查询相关配置信息 + ThirdPartyStationRelation relation = new ThirdPartyStationRelation(); + relation.setStationId(Long.parseLong(stationId)); + ThirdPartyStationRelationVO relationInfo = thirdPartyStationRelationService.selectRelationInfo(relation); + if (relationInfo == null) { + return null; + } + // 根据站点id查询正在使用的计费模板 + List billingPriceVOList = pileBillingTemplateService.queryBillingPrice(stationId); + if (CollectionUtils.isEmpty(billingPriceVOList)) { + return null; + } + for (BillingPriceVO billingPriceVO : billingPriceVOList) { + // 将时段开始时间、电费、服务费信息进行封装 + policyInfo = new DeliverEquipBusinessDTO.ChargePolicyInfo.PricePolicyInfo.PolicyInfo(); + String startTime = billingPriceVO.getStartTime() + ":00"; // 00:00:00 格式 + // 需要将中间的冒号去掉,改为 000000 格式 + String replace = StringUtils.replace(startTime, ":", ""); + policyInfo.setStartTime(replace); + policyInfo.setElecPrice(new BigDecimal(billingPriceVO.getElectricityPrice()).setScale(4, BigDecimal.ROUND_HALF_UP)); + policyInfo.setSevicePrice(new BigDecimal(billingPriceVO.getServicePrice()).setScale(4, BigDecimal.ROUND_HALF_UP)); + + policyInfoList.add(policyInfo); + } + + BillingPriceVO billingPriceVO = billingPriceVOList.get(0); + // String equipmentOwnerId = ""; + // if (StringUtils.isNotBlank(organizationCode) && organizationCode.length() == 18) { + // equipmentOwnerId = StringUtils.substring(organizationCode, organizationCode.length() - 10, organizationCode.length() - 1); + // }else { + // equipmentOwnerId = Constants.OPERATORID_JIANG_SU; + // } + + // 封装参数 + pricePolicyInfo.setEquipBizID(Constants.OPERATORID_JIANG_SU + billingPriceVO.getTemplateCode()); // 计费策略ID + pricePolicyInfo.setValidStat(1); // 执行状态 + pricePolicyInfo.setValidStartTime(billingPriceVO.getPublishTime()); // 生效时间 + pricePolicyInfo.setValidEndTime("2099-12-31 23:59:59"); // 失效时间 + pricePolicyInfo.setSumPeriod(policyInfoList.size()); // 时段数 + + pricePolicyInfo.setPolicyInfos(policyInfoList); + + pricePolicyInfos.add(pricePolicyInfo); + + chargePolicyInfo.setConnectorID(pileConnectorCode); + chargePolicyInfo.setPricePolicyInfos(pricePolicyInfos); + + chargePolicyInfos.add(chargePolicyInfo); + + params.setEquipBizSeq(equipBizSeq); + params.setSumChargePolicyInfos(pricePolicyInfos.size()); + params.setChargePolicyInfos(chargePolicyInfos); + + String jsonString = JSONObject.toJSONString(params); + + // 获取请求参数 + String operatorId = relationInfo.getOperatorId(); + String operatorSecret = relationInfo.getOperatorSecret(); + String signSecret = relationInfo.getSignSecret(); + String dataSecret = relationInfo.getDataSecret(); + String dataSecretIv = relationInfo.getDataSecretIv(); + String urlAddress = relationInfo.getUrlAddress(); + String thirdPartyType = relationInfo.getThirdPartyType(); + + String url = urlAddress + "deliver_equip_business_policy"; + // 获取令牌 + ZDLGetTokenDTO dto = new ZDLGetTokenDTO(); + dto.setOperatorId(operatorId); + dto.setDataSecret(dataSecret); + dto.setSignSecret(signSecret); + dto.setUrlAddress(urlAddress); + dto.setDataSecretIv(dataSecretIv); + dto.setOperatorSecret(operatorSecret); + String token = HWGetToken(dto); + // 发送请求 + String result = HttpRequestUtil.sendPost(token, jsonString, url, dataSecret, dataSecretIv, operatorId, signSecret); + return result; + } + + /** + * 下发计费策略响应 + * 平台 <-- 华为 + * @return + */ + @Override + public Map receiveDeliverEquipBusinessPolicyResult(ReceiveDeliverDTO dto) { + ThirdPartyPlatformConfig configInfo = thirdPartyPlatformConfigService.getInfoByOperatorId(dto.getOperatorId()); + if (configInfo == null) { + return null; + } + List chargePolicyInfoRets = dto.getChargePolicyInfoRets(); + // 将下发失败的进行筛选、收集 + List failedList = chargePolicyInfoRets.stream() + .filter(x -> x.getSuccStat() == Constants.one) // 1-失败 + .collect(Collectors.toList()); + for (ReceiveDeliverDTO.ChargePolicyInfoRet chargePolicyInfoRet : failedList) { + log.error("华为下发计费策略响应, 枪口:{} 下发计费策略失败, 失败原因:{}", chargePolicyInfoRet.getConnectorID(), chargePolicyInfoRet.getFailReason()); + // TODO 重新下发 + + } + // 返回参数 + Map map = new LinkedHashMap<>(); + map.put("SuccStat", 0); + map.put("FailReason", 0); + + // 加密 + Map resultMap = Maps.newLinkedHashMap(); + // 加密数据 + byte[] encryptText = Cryptos.aesEncrypt(JSONObject.toJSONString(map).getBytes(), + configInfo.getDataSecret().getBytes(), configInfo.getDataSecretIv().getBytes()); + String encryptData = Encodes.encodeBase64(encryptText); + + resultMap.put("Data", encryptData); + // 生成sig + String resultSign = GBSignUtils.sign(resultMap, configInfo.getSignSecret()); + resultMap.put("Sig", resultSign); + + return resultMap; + } + }