From db0b7644a9a5a62ac2da3cf571fee41bb8b6974f Mon Sep 17 00:00:00 2001 From: Pu Zhibing <393733352@qq.com> Date: 星期四, 25 九月 2025 15:54:15 +0800 Subject: [PATCH] 添加E路通推送数据功能 --- ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/StationInfoReq.java | 40 + ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/model/Site.java | 6 ruoyi-service/ruoyi-chargingPile/pom.xml | 4 ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/ConnectorStatusInfo.java | 30 ruoyi-api/ruoyi-api-jianguan/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports | 3 pom.xml | 7 ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/config/AppServerConfig.java | 26 ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/CommonResponse.java | 12 ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/feignClient/ELuTongClient.java | 43 + ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/SignAlgorithm.java | 64 ++ ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/ConnectorStatusReq.java | 36 + ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/config/HttpsClientRequestFactory.java | 130 ++++ ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/util/TaskUtil.java | 97 +++ ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/StationInfo.java | 145 ++++ ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/CommonRequest.java | 16 ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/ConnectorInfo.java | 51 + ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/factory/ELuTongClientFallbackFactory.java | 32 + ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/EquipmentInfo.java | 61 + ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/StationsStatusReq.java | 36 + ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/SignatureTools.java | 135 ++++ ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TChargingPileController.java | 79 ++ ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/SiteServiceImpl.java | 201 ++++++ ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/config/RestConfigBean.java | 20 ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/OperatorInfo.java | 32 + ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/AESTools.java | 69 ++ ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/StationStausInfo.java | 18 ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/controller/SampleController.java | 341 +++++++++++ ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/Base64Tools.java | 111 +++ 28 files changed, 1,837 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index dba8ee3..3f8b860 100644 --- a/pom.xml +++ b/pom.xml @@ -247,6 +247,13 @@ <version>${ruoyi.version}</version> </dependency> + <!-- 监管接口 --> + <dependency> + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi-api-jianguan</artifactId> + <version>${ruoyi.version}</version> + </dependency> + </dependencies> </dependencyManagement> diff --git a/ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/model/Site.java b/ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/model/Site.java index 5b58570..9ca22ef 100644 --- a/ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/model/Site.java +++ b/ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/model/Site.java @@ -447,6 +447,12 @@ @TableField("supportOrder") @ApiModelProperty(value = "2.0修改字段-是否支持预约 0不支持1支持") private Integer supportOrder; + /** + * 高速路服务区编号 + */ + @TableField("serAreaCode") + @ApiModelProperty(value = "高速路服务区编号") + private String serAreaCode; // /** // * 换电设备信息 // */ diff --git a/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/ConnectorInfo.java b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/ConnectorInfo.java new file mode 100644 index 0000000..0fc7224 --- /dev/null +++ b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/ConnectorInfo.java @@ -0,0 +1,51 @@ +package com.ruoyi.integration.api.elutong.model; + +import lombok.Data; + +@Data +public class ConnectorInfo { + /** + * 充电设备接口编码 + * 充电设备接口编码,同一运营商内唯一 + */ + private String connectorId; + /** + * 充电设备接口名称 + */ + private String connectorName; + /** + * 充电设备接口类型 + * 1:家用插座(模式2); + * 2:交流接口插座(模式3,连接方式B ); + * 3:交流接口插头(带枪线,模式3,连接方式C); + * 4:直流接口枪头(带枪线,模式4); + * 5:无线充电座; + * 6:其他 + */ + private Integer connectorType; + /** + * 额定电压上限 + */ + private Integer voltageUpperLimits; + /** + * 额定电压下限 + */ + private Integer voltageLowerLimits; + /** + * 额定电流 + */ + private Integer current; + /** + * 充电设备总功率 + */ + private Double power; + /** + * 车位号 + */ + private String parkNo; + /** + * 国家标准 + * 1:2011;2:2015 + */ + private Integer nationalStandard; +} diff --git a/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/ConnectorStatusInfo.java b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/ConnectorStatusInfo.java new file mode 100644 index 0000000..510fcc3 --- /dev/null +++ b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/ConnectorStatusInfo.java @@ -0,0 +1,30 @@ +package com.ruoyi.integration.api.elutong.model; + +import lombok.Data; + +@Data +public class ConnectorStatusInfo { + /** + * 充电设备接口编码 + * 充电设备接口编码,同一运营商内唯一 + */ + private String connectorId; + /** + * 充电设备接口状态 + * 0:离网; + * 1:空闲; + * 2:占用(未充电); + * 3:占用(充电中); + * 4:占用(预约锁定); + * 255:故障 + */ + private Integer status; + /** + * 电池剩余电量 + */ + private Double soc; + /** + * 估算剩余充电时间(min) + */ + private Integer remainingTime; +} diff --git a/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/ConnectorStatusReq.java b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/ConnectorStatusReq.java new file mode 100644 index 0000000..64fd0ec --- /dev/null +++ b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/ConnectorStatusReq.java @@ -0,0 +1,36 @@ +package com.ruoyi.integration.api.elutong.model; + +import lombok.Data; + +import java.util.List; + +@Data +public class ConnectorStatusReq { + /** + * 充电运营商ID + * 使用运营商组织机构代码 + */ + private String operatorId; + /** + * 服务区编码 + * 如上送具体的服务区信息时,该字段必填,否则可为空 + */ + private String serAreaCode; + /** + * 充电站ID + * 充电站唯一编码 + * 运营商未提供:使用服务区编码+充电站(CD)/换电站(HD)+顺序码 + * 顺序码规则:两位字符,取值范围01~99 + * 运营商提供:使用运营商提供的充电站编码。同一运营商内唯一 + */ + private String stationId; + /** + * 总记录条数 + * 符合条件的充电站总数,记录数不超过50 + */ + private Integer itemSize; + /** + * 充电设备接口状态列表 + */ + private List<ConnectorStatusInfo> connectorStatusInfos; +} diff --git a/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/EquipmentInfo.java b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/EquipmentInfo.java new file mode 100644 index 0000000..f821eb1 --- /dev/null +++ b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/EquipmentInfo.java @@ -0,0 +1,61 @@ +package com.ruoyi.integration.api.elutong.model; + +import lombok.Data; + +import java.util.List; + +@Data +public class EquipmentInfo { + /** + * 设备编码 + * 设备唯一编码,对同一运营商,保证唯一 + */ + private String equipmentId; + /** + * 充电设备名称 + * 运营商名称简写+设备名称 + */ + private String equipmentName; + /** + * 设备生产商组织机构代码 + */ + private String manufacturerId; + /** + * 设备生产商名称 + */ + private String manufacturerName; + /** + * 设备型号 + */ + private String equipmentModel; + /** + * 设备生产日期 + * YYYY-MM-DD + */ + private String productionDate; + /** + * 设备类型 + * 1:直流设备; + * 2:交流设备; + * 3:交直流一体设备; + * 4:无线设备; + * 5:其他 + */ + private Integer equipmentType; + /** + * 充电设备经度 + */ + private Double equipmentLng; + /** + * 充电设备维度 + */ + private Double equipmentLat; + /** + * 充电设备总功率 + */ + private Double power; + /** + * 充电设备接口信息列表 + */ + private List<ConnectorInfo> connectorInfos; +} diff --git a/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/OperatorInfo.java b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/OperatorInfo.java new file mode 100644 index 0000000..3cde73e --- /dev/null +++ b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/OperatorInfo.java @@ -0,0 +1,32 @@ +package com.ruoyi.integration.api.elutong.model; + +import lombok.Data; + +@Data +public class OperatorInfo { + /** + * 运营商ID + * 组织机构代码 + */ + private String operatorId; + /** + * 运营商名称 + */ + private String operatorName; + /** + * 运营商客服电话1 + */ + private String operatorTel1; + /** + * 运营商客服电话2 + */ + private String operatorTel2; + /** + * 运营商注册地址 + */ + private String operatorRegAddress; + /** + * 备注 + */ + private String operatorNote; +} diff --git a/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/StationInfo.java b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/StationInfo.java new file mode 100644 index 0000000..8f5db62 --- /dev/null +++ b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/StationInfo.java @@ -0,0 +1,145 @@ +package com.ruoyi.integration.api.elutong.model; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Data +public class StationInfo { + /** + * 服务区编码 + * 由部级管理平台统一管理和分配 + */ + private String serAreaCode; + /** + * 运营商ID + * 组织机构代码 + */ + private String operatorId; + /** + * 充/换电站ID + * 充电站唯一编码 + * 运营商未提供:使用服务区编码+充电站(CD)/换电站(HD)+顺序码 + * 顺序码规则:两位字符,取值范围01~99 + * 运营商提供:使用运营商提供的充电站编码。同一运营商内唯一 + */ + private String stationId; + /** + * 充电站名称 + */ + private String stationName; + /** + * 站点模式 + * CD:充电站 + * HD:换电站 + */ + private String stationMode; + /** + * 充电站省市辖区编码 + */ + private String areaCode; + /** + * 服务电话 + */ + private String serviceTel; + /** + * 站点类型 + * 1:公共; + * 50:个人; + * 100:公交(专用); + * 101:环卫(专用); + * 102:物流(专用); + * 103:出租车(专用); + * 255:其他 + */ + private Integer stationType; + /** + * 站点状态 + * 0:未知; + * 1:建设中; + * 5:关闭下线; + * 6:维护中; + * 50:正常使用 + */ + private Integer stationStatus; + /** + * 车位数量 + */ + private Integer parkNums; + /** + * 经度 + */ + private Double stationLng; + /** + * 纬度 + */ + private Double stationLat; + /** + * 站点引导 + */ + private String siteGuide; + /** + * 建设场所 + * 1:居民区; + * 2:公共机构; + * 3:企事业单位; + * 4:写字楼; + * 5:工业园区; + * 6:交通枢纽; + * 7:大型文体设施; + * 8:城市绿地; + * 9:大型建筑配建停车场; + * 10:路边停车位; + * 11:城际高速服务区; + * 255:其他 + */ + private Integer construction; + /** + * 站点照片 + */ + private List<String> pictures; + /** + * 使用车型描述 + */ + private String matchCars; + /** + * 营业时间 + */ + private String busineHours; + /** + * 充电电费率 + */ + private BigDecimal electricityFee; + /** + * 服务费率 + */ + private BigDecimal serviceFee; + /** + * 停车费 + */ + private BigDecimal parkFee; + /** + * 支付方式 + * 1:刷卡 + * 2:线上 + * 3:现金 + * 101:ETC支付 + * 说明:其中电子钱包类卡为刷卡,身份鉴权卡、微信/支付宝、APP为线上 + */ + private String payment; + /** + * 是否支持预约 + * 0:否 + * 1:是 + */ + private Integer supportOrder; + /** + * 备注 + */ + private String remark; + /** + * 充电设备信息列表 + */ + private List<EquipmentInfo> equipmentInfos; +} diff --git a/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/StationInfoReq.java b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/StationInfoReq.java new file mode 100644 index 0000000..04a314a --- /dev/null +++ b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/StationInfoReq.java @@ -0,0 +1,40 @@ +package com.ruoyi.integration.api.elutong.model; + +import lombok.Data; + +import java.util.List; + +@Data +public class StationInfoReq { + /** + * 充电运营商ID + * 使用运营商组织机构代码 + */ + private String operatorId; + /** + * 服务区编码 + * 如上送具体的服务区信息时,该字段必填,否则可为空 + */ + private String serAreaCode; + /** + * 充电站ID + * 充电站唯一编码 + * 运营商未提供:使用服务区编码+充电站(CD)/换电站(HD)+顺序码 + * 顺序码规则:两位字符,取值范围01~99 + * 运营商提供:使用运营商提供的充电站编码。同一运营商内唯一 + */ + private String stationId; + /** + * 基础设施运营商信息 + */ + private OperatorInfo operatorInfo; + /** + * 总记录条数 + * 符合条件的充电站总数,记录数不超过50 + */ + private Integer itemSize; + /** + * 充电站信息列表 + */ + private List<StationInfo> stationInfos; +} diff --git a/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/StationStausInfo.java b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/StationStausInfo.java new file mode 100644 index 0000000..8711fc2 --- /dev/null +++ b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/StationStausInfo.java @@ -0,0 +1,18 @@ +package com.ruoyi.integration.api.elutong.model; + +import lombok.Data; + +import java.util.List; + +@Data +public class StationStausInfo { + /** + * 充/换电站ID + * 使用服务区编码+充电站(CD)/换电站(HD)+顺序码 + */ + private String stationId; + /** + * 充电设备接口状态列表 + */ + private List<ConnectorStatusInfo> connectorStatusInfos; +} diff --git a/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/StationsStatusReq.java b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/StationsStatusReq.java new file mode 100644 index 0000000..38de087 --- /dev/null +++ b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/elutong/model/StationsStatusReq.java @@ -0,0 +1,36 @@ +package com.ruoyi.integration.api.elutong.model; + +import lombok.Data; + +import java.util.List; + +@Data +public class StationsStatusReq { + /** + * 充电运营商ID + * 使用运营商组织机构代码 + */ + private String operatorId; + /** + * 服务区编码 + * 如上送具体的服务区信息时,该字段必填,否则可为空 + */ + private String serAreaCode; + /** + * 充电站ID + * 充电站唯一编码 + * 运营商未提供:使用服务区编码+充电站(CD)/换电站(HD)+顺序码 + * 顺序码规则:两位字符,取值范围01~99 + * 运营商提供:使用运营商提供的充电站编码。同一运营商内唯一 + */ + private String stationId; + /** + * 总记录条数 + * 符合条件的充电站总数,记录数不超过50 + */ + private Integer itemSize; + /** + * 充电站状态信息列表 + */ + private List<StationStausInfo> stationStausInfos; +} diff --git a/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/factory/ELuTongClientFallbackFactory.java b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/factory/ELuTongClientFallbackFactory.java new file mode 100644 index 0000000..96cc0ed --- /dev/null +++ b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/factory/ELuTongClientFallbackFactory.java @@ -0,0 +1,32 @@ +package com.ruoyi.integration.api.factory; + +import com.ruoyi.common.core.domain.R; +import com.ruoyi.integration.api.elutong.model.ConnectorStatusReq; +import com.ruoyi.integration.api.elutong.model.StationInfoReq; +import com.ruoyi.integration.api.elutong.model.StationsStatusReq; +import com.ruoyi.integration.api.feignClient.ELuTongClient; +import org.springframework.cloud.openfeign.FallbackFactory; + +public class ELuTongClientFallbackFactory implements FallbackFactory<ELuTongClient> { + @Override + public ELuTongClient create(Throwable cause) { + return new ELuTongClient() { + + + @Override + public R pushStationInfo(StationInfoReq stationInfoReq) { + return R.fail("推送充电站静态信息失败:" + cause.getMessage()); + } + + @Override + public R pushStationsStatus(StationsStatusReq stationsStatusReq) { + return R.fail("推送充电站状态信息失败:" + cause.getMessage()); + } + + @Override + public R pushConnectorStatus(ConnectorStatusReq connectorStatusReq) { + return R.fail("推送设备接口状态信息失败:" + cause.getMessage()); + } + }; + } +} diff --git a/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/feignClient/ELuTongClient.java b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/feignClient/ELuTongClient.java new file mode 100644 index 0000000..d119974 --- /dev/null +++ b/ruoyi-api/ruoyi-api-jianguan/src/main/java/com/ruoyi/integration/api/feignClient/ELuTongClient.java @@ -0,0 +1,43 @@ +package com.ruoyi.integration.api.feignClient; + +import com.ruoyi.common.core.constant.ServiceNameConstants; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.integration.api.elutong.model.ConnectorStatusReq; +import com.ruoyi.integration.api.elutong.model.StationInfoReq; +import com.ruoyi.integration.api.elutong.model.StationsStatusReq; +import com.ruoyi.integration.api.factory.ELuTongClientFallbackFactory; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + + +@FeignClient(contextId = "ELuTongClient", value = ServiceNameConstants.JIANGUAN_SERVICE, fallbackFactory = ELuTongClientFallbackFactory.class) +public interface ELuTongClient { + + + /** + * 推送充电站静态信息 + * @param stationInfoReq + * @return + */ + @PostMapping("/elutong/pushStationInfo") + R pushStationInfo(@RequestBody StationInfoReq stationInfoReq); + + + /** + * 推送充电站状态信息 + * @param stationsStatusReq + * @return + */ + @PostMapping("/elutong/pushStationsStatus") + R pushStationsStatus(@RequestBody StationsStatusReq stationsStatusReq); + + + /** + * 推送设备接口状态信息 + * @param connectorStatusReq + * @return + */ + @PostMapping("/elutong/pushConnectorStatus") + R pushConnectorStatus(@RequestBody ConnectorStatusReq connectorStatusReq); +} diff --git a/ruoyi-api/ruoyi-api-jianguan/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-api/ruoyi-api-jianguan/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 3538350..ffd095b 100644 --- a/ruoyi-api/ruoyi-api-jianguan/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/ruoyi-api/ruoyi-api-jianguan/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1 +1,2 @@ -com.ruoyi.integration.api.factory.ChargingMessageClientFallbackFactory \ No newline at end of file +com.ruoyi.integration.api.factory.ChargingMessageClientFallbackFactory +com.ruoyi.integration.api.factory.ELuTongClientFallbackFactory \ No newline at end of file diff --git a/ruoyi-service/ruoyi-chargingPile/pom.xml b/ruoyi-service/ruoyi-chargingPile/pom.xml index 07f0e31..adabae8 100644 --- a/ruoyi-service/ruoyi-chargingPile/pom.xml +++ b/ruoyi-service/ruoyi-chargingPile/pom.xml @@ -158,6 +158,10 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency> + <dependency> + <groupId>com.ruoyi</groupId> + <artifactId>ruoyi-api-jianguan</artifactId> + </dependency> </dependencies> <build> diff --git a/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TChargingPileController.java b/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TChargingPileController.java index 90ec317..2df41df 100644 --- a/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TChargingPileController.java +++ b/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TChargingPileController.java @@ -3,9 +3,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; -import com.ruoyi.chargingPile.api.model.TChargingGun; -import com.ruoyi.chargingPile.api.model.TChargingPile; -import com.ruoyi.chargingPile.api.model.TFaultMessage; +import com.ruoyi.chargingPile.api.model.*; import com.ruoyi.chargingPile.api.query.TChargingGunQuery; import com.ruoyi.chargingPile.api.vo.TChargingGunVO; import com.ruoyi.chargingPile.api.vo.UpdateChargingPileStatusVo; @@ -15,6 +13,7 @@ import com.ruoyi.chargingPile.dto.GetChargingGunMonitoring; import com.ruoyi.chargingPile.service.*; import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.web.domain.AjaxResult; import com.ruoyi.common.core.web.page.PageInfo; import com.ruoyi.chargingPile.api.dto.PageChargingPileListDTO; @@ -30,6 +29,11 @@ import com.ruoyi.common.redis.service.RedisService; import com.ruoyi.common.security.annotation.Logical; import com.ruoyi.common.security.annotation.RequiresPermissions; +import com.ruoyi.integration.api.elutong.model.ConnectorStatusInfo; +import com.ruoyi.integration.api.elutong.model.ConnectorStatusReq; +import com.ruoyi.integration.api.elutong.model.StationStausInfo; +import com.ruoyi.integration.api.elutong.model.StationsStatusReq; +import com.ruoyi.integration.api.feignClient.ELuTongClient; import com.ruoyi.integration.api.feignClient.TCECClient; import com.ruoyi.order.api.feignClient.ChargingOrderClient; import com.ruoyi.order.api.model.TChargingOrder; @@ -39,6 +43,7 @@ import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.*; @@ -60,6 +65,7 @@ * @author xiaochen * @since 2024-08-06 */ +@Slf4j @Api(tags = "充电桩") @RestController @RequestMapping("/t-charging-pile") @@ -79,6 +85,14 @@ @Resource private TCECClient tcecClient; + + @Resource + private ELuTongClient eLuTongClient; + + @Resource + private ISiteService siteService; + + @Autowired @@ -268,6 +282,7 @@ public void run() { //推送状态给三方平台 tcecClient.pushChargingGunStatus(tChargingGun.getFullNumber(), tChargingGun1.getStatus()); + pushConnectorStatus(tChargingGun); } }).start(); @@ -296,6 +311,7 @@ public void run() { //推送状态给三方平台 tcecClient.pushChargingGunStatus(tChargingGun.getFullNumber(), tChargingGun1.getStatus()); + pushConnectorStatus(tChargingGun); } }).start(); @@ -349,6 +365,7 @@ public void run() { //推送状态给三方平台 tcecClient.pushChargingGunStatus(tChargingGun.getFullNumber(), tChargingGun1.getStatus()); + pushConnectorStatus(tChargingGun); } }).start(); }else{ @@ -361,6 +378,7 @@ public void run() { //推送状态给三方平台 tcecClient.pushChargingGunStatus(tChargingGun.getFullNumber(), tChargingGun1.getStatus()); + pushConnectorStatus(tChargingGun); } }).start(); } @@ -374,5 +392,60 @@ } } } + + + /** + * 推送设备接口状态信息 + * @param chargingGun + */ + private void pushConnectorStatus(TChargingGun chargingGun){ + Site site = siteService.getById(chargingGun.getSiteId()); + if(StringUtils.isNotEmpty(site.getSerAreaCode())){ + ConnectorStatusReq connectorStatusReq = new ConnectorStatusReq(); + connectorStatusReq.setOperatorId("91510903906171535D"); + connectorStatusReq.setSerAreaCode(site.getSerAreaCode()); + connectorStatusReq.setStationId(site.getId().toString()); + List<ConnectorStatusInfo> connectorStatusInfos = new ArrayList<>(); + connectorStatusInfos.add(buildConnectorStatus(chargingGun)); + connectorStatusReq.setItemSize(connectorStatusInfos.size()); + connectorStatusReq.setConnectorStatusInfos(connectorStatusInfos); + R r = eLuTongClient.pushConnectorStatus(connectorStatusReq); + if(200 != r.getCode()){ + log.error(r.getMsg()); + } + } + } + + + private ConnectorStatusInfo buildConnectorStatus(TChargingGun chargingGun){ + ConnectorStatusInfo connectorStatusInfo = new ConnectorStatusInfo(); + connectorStatusInfo.setConnectorId(chargingGun.getId().toString()); + switch (chargingGun.getStatus()){ + case 1: + connectorStatusInfo.setStatus(0); + break; + case 2: + connectorStatusInfo.setStatus(1); + break; + case 3: + connectorStatusInfo.setStatus(2); + break; + case 4: + connectorStatusInfo.setStatus(3); + break; + case 5: + connectorStatusInfo.setStatus(3); + break; + case 6: + connectorStatusInfo.setStatus(4); + break; + case 7: + connectorStatusInfo.setStatus(255); + break; + } + connectorStatusInfo.setSoc(0D); + connectorStatusInfo.setRemainingTime(0); + return connectorStatusInfo; + } } diff --git a/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/SiteServiceImpl.java b/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/SiteServiceImpl.java index ed2cadc..4347bdb 100644 --- a/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/SiteServiceImpl.java +++ b/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/SiteServiceImpl.java @@ -24,6 +24,8 @@ import com.ruoyi.common.core.web.page.PageInfo; import com.ruoyi.common.security.service.TokenService; import com.ruoyi.common.security.utils.SecurityUtils; +import com.ruoyi.integration.api.elutong.model.*; +import com.ruoyi.integration.api.feignClient.ELuTongClient; import com.ruoyi.integration.api.feignClient.IntegrationClient; import com.ruoyi.integration.api.feignClient.TCECClient; import com.ruoyi.other.api.domain.TVip; @@ -41,7 +43,11 @@ import javax.annotation.Resource; import java.math.BigDecimal; +import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** @@ -90,6 +96,8 @@ @Resource private TCECClient tcecClient; + + @Resource ELuTongClient eLuTongClient; /** @@ -196,9 +204,176 @@ } site.setMark(0); this.save(site); + + if(StringUtils.isNotEmpty(site.getSerAreaCode())){ + ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); + threadPoolExecutor.execute(()->{ + StationInfoReq stationInfoReq = new StationInfoReq(); + stationInfoReq.setOperatorId("91510903906171535D"); + stationInfoReq.setStationId(site.getId().toString()); + OperatorInfo operatorInfo = new OperatorInfo(); + operatorInfo.setOperatorId("91510903906171535D"); + operatorInfo.setOperatorName("四川明星新能源科技有限公司"); + operatorInfo.setOperatorTel1("18683346252"); + operatorInfo.setOperatorTel2("13982508784"); + operatorInfo.setOperatorRegAddress("遂宁市船山区渠河南路18号"); + stationInfoReq.setOperatorInfo(operatorInfo); + List<StationInfo> stationInfos = new ArrayList<>(); + stationInfos.add(buildStationInfo(site)); + stationInfoReq.setItemSize(stationInfos.size()); + stationInfoReq.setStationInfos(stationInfos); + R r = eLuTongClient.pushStationInfo(stationInfoReq); + if(200 != r.getCode()){ + System.out.println(r.getMsg()); + } + }); + } return AjaxResult.success(); } - + + + private StationInfo buildStationInfo(Site site){ + StationInfo stationInfo = new StationInfo(); + stationInfo.setSerAreaCode(site.getSerAreaCode()); + stationInfo.setOperatorId("91510903906171535D"); + stationInfo.setStationId(site.getId().toString()); + stationInfo.setStationName(site.getName()); + stationInfo.setStationMode("CD"); + stationInfo.setAreaCode(site.getDistrictsCode()); + stationInfo.setServiceTel(site.getServicePhone()); + switch (site.getSiteType()){ + case 0: + stationInfo.setStationType(255); + break; + case 1: + stationInfo.setStationType(1); + break; + case 2: + stationInfo.setStationType(50); + break; + case 3: + stationInfo.setStationType(100); + break; + case 4: + stationInfo.setStationType(101); + break; + case 5: + stationInfo.setStationType(102); + break; + case 6: + stationInfo.setStationType(103); + break; + } + switch (site.getStatus()){ + case 3: + stationInfo.setStationStatus(5); + break; + case 2: + stationInfo.setStationStatus(6); + break; + case 1: + stationInfo.setStationStatus(50); + break; + } + stationInfo.setParkNums(site.getParkingSpace()); + stationInfo.setStationLng(Double.parseDouble(site.getLon())); + stationInfo.setStationLat(Double.parseDouble(site.getLat())); + stationInfo.setSiteGuide(site.getGuide()); + switch (site.getConstructionSite()){ + case 1: + stationInfo.setConstruction(1); + break; + case 2: + stationInfo.setConstruction(2); + break; + case 3: + stationInfo.setConstruction(3); + break; + case 4: + stationInfo.setConstruction(4); + break; + case 5: + stationInfo.setConstruction(5); + break; + case 6: + stationInfo.setConstruction(6); + break; + case 7: + stationInfo.setConstruction(7); + break; + case 8: + stationInfo.setConstruction(8); + break; + case 9: + stationInfo.setConstruction(9); + break; + case 10: + stationInfo.setConstruction(10); + break; + case 11: + stationInfo.setConstruction(11); + break; + case 0: + stationInfo.setConstruction(255); + break; + } + if(StringUtils.isNotEmpty(site.getImgUrl())){ + stationInfo.setPictures(Arrays.asList(site.getImgUrl().split(","))); + } + stationInfo.setMatchCars(site.getVehicleDescription()); + stationInfo.setBusineHours(site.getStartServiceTime() + " - " + site.getEndServiceTime()); + stationInfo.setEquipmentInfos(buildEquipmentInfo(site)); + return stationInfo; + } + + private List<EquipmentInfo> buildEquipmentInfo(Site site){ + List<TChargingPile> list = chargingPileService.list(new LambdaQueryWrapper<TChargingPile>() + .eq(TChargingPile::getSiteId, site.getId()).eq(TChargingPile::getDelFlag, 0)); + return new ArrayList<EquipmentInfo>(){{ + for (TChargingPile chargingPile : list) { + EquipmentInfo equipmentInfo = new EquipmentInfo(); + equipmentInfo.setEquipmentId(chargingPile.getId().toString()); + equipmentInfo.setEquipmentName(chargingPile.getName()); + equipmentInfo.setManufacturerId(chargingPile.getManufacturerCode()); + equipmentInfo.setManufacturerName(chargingPile.getManufacturer()); + equipmentInfo.setEquipmentModel(chargingPile.getEquipmentType()); + if(null != chargingPile.getProductionDate()){ + equipmentInfo.setProductionDate(chargingPile.getProductionDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + } + equipmentInfo.setEquipmentType(0 == chargingPile.getType() ? 5 : chargingPile.getType()); + if(StringUtils.isNotEmpty(chargingPile.getEquipmentLng())){ + equipmentInfo.setEquipmentLng(Double.parseDouble(chargingPile.getEquipmentLng())); + } + if(StringUtils.isNotEmpty(chargingPile.getEquipmentLat())){ + equipmentInfo.setEquipmentLat(Double.parseDouble(chargingPile.getEquipmentLat())); + } + equipmentInfo.setPower(chargingPile.getRatedPower().doubleValue()); + equipmentInfo.setConnectorInfos(buildConnectorInfo(chargingPile)); + add(equipmentInfo); + } + }}; + } + + + private List<ConnectorInfo> buildConnectorInfo(TChargingPile chargingPile){ + List<TChargingGun> list = chargingGunService.list(new LambdaQueryWrapper<TChargingGun>() + .eq(TChargingGun::getChargingPileId, chargingPile.getId()).eq(TChargingGun::getDelFlag, 0)); + return new ArrayList<ConnectorInfo>(){{ + for (TChargingGun chargingGun : list) { + ConnectorInfo connectorInfo = new ConnectorInfo(); + connectorInfo.setConnectorId(chargingGun.getId().toString()); + connectorInfo.setConnectorName(chargingGun.getName()); + connectorInfo.setConnectorType(0 == chargingGun.getType() ? 6 : chargingGun.getType()); + connectorInfo.setVoltageUpperLimits(chargingGun.getUpperRatedVoltage().intValue()); + connectorInfo.setVoltageLowerLimits(chargingGun.getLowerLimitOfRatedVoltage().intValue()); + connectorInfo.setCurrent(chargingGun.getRatedCurrent().intValue()); + connectorInfo.setPower(chargingGun.getRatedPower().doubleValue()); + connectorInfo.setParkNo(chargingGun.getParkingNumber()); + connectorInfo.setNationalStandard(Integer.parseInt(chargingGun.getNationalStandard())); + add(connectorInfo); + } + }}; + } /** * 编辑站点 @@ -227,7 +402,29 @@ this.updateById(site); tcecClient.superviseNotificationStationInfo(site.getId()); - + if(StringUtils.isNotEmpty(site.getSerAreaCode())){ + ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); + threadPoolExecutor.execute(()->{ + StationInfoReq stationInfoReq = new StationInfoReq(); + stationInfoReq.setOperatorId("91510903906171535D"); + stationInfoReq.setStationId(site.getId().toString()); + OperatorInfo operatorInfo = new OperatorInfo(); + operatorInfo.setOperatorId("91510903906171535D"); + operatorInfo.setOperatorName("四川明星新能源科技有限公司"); + operatorInfo.setOperatorTel1("18683346252"); + operatorInfo.setOperatorTel2("13982508784"); + operatorInfo.setOperatorRegAddress("遂宁市船山区渠河南路18号"); + stationInfoReq.setOperatorInfo(operatorInfo); + List<StationInfo> stationInfos = new ArrayList<>(); + stationInfos.add(buildStationInfo(site)); + stationInfoReq.setItemSize(stationInfos.size()); + stationInfoReq.setStationInfos(stationInfos); + R r = eLuTongClient.pushStationInfo(stationInfoReq); + if(200 != r.getCode()){ + System.out.println(r.getMsg()); + } + }); + } return AjaxResult.success(); } diff --git a/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/util/TaskUtil.java b/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/util/TaskUtil.java index a463b33..af9be75 100644 --- a/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/util/TaskUtil.java +++ b/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/util/TaskUtil.java @@ -1,18 +1,32 @@ package com.ruoyi.chargingPile.util; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.ruoyi.chargingPile.api.model.Site; +import com.ruoyi.chargingPile.api.model.TChargingGun; +import com.ruoyi.chargingPile.service.ISiteService; +import com.ruoyi.chargingPile.service.TChargingGunService; import com.ruoyi.chargingPile.service.TChargingPileService; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.core.utils.StringUtils; +import com.ruoyi.integration.api.elutong.model.*; +import com.ruoyi.integration.api.feignClient.ELuTongClient; +import lombok.extern.slf4j.Slf4j; import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.context.ApplicationListener; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.List; /** * 定时任务工具类 */ +@Slf4j @Component public class TaskUtil implements ApplicationListener<WebServerInitializedEvent> { @@ -20,6 +34,15 @@ private TChargingPileService chargingPileService; private Integer port = null; + + @Resource + private ELuTongClient eLuTongClient; + + @Resource + private ISiteService siteService; + + @Resource + private TChargingGunService chargingGunService; @Override @@ -35,8 +58,78 @@ public void taskMinute(){ if(null != port && port == 5300){ chargingPileService.updateStatus(); + pushStationsStatus(); } } - - + + + /** + * 1分钟定时推送设备状态E路通 + */ + private void pushStationsStatus(){ + List<Site> list = siteService.list(new LambdaQueryWrapper<Site>().eq(Site::getDelFlag, 0).isNotNull(Site::getSerAreaCode)); + for (Site site : list) { + if(StringUtils.isEmpty(site.getSerAreaCode())){ + continue; + } + StationsStatusReq stationsStatusReq = new StationsStatusReq(); + stationsStatusReq.setOperatorId("91510903906171535D"); + stationsStatusReq.setSerAreaCode(site.getSerAreaCode()); + stationsStatusReq.setStationId(site.getId().toString()); + List<StationStausInfo> stationStausInfos = new ArrayList<>(); + stationStausInfos.add(buildStationStaus(site)); + stationsStatusReq.setItemSize(stationStausInfos.size()); + stationsStatusReq.setStationStausInfos(stationStausInfos); + R r = eLuTongClient.pushStationsStatus(stationsStatusReq); + if(200 != r.getCode()){ + log.error(r.getMsg()); + } + } + + } + + + private StationStausInfo buildStationStaus(Site site){ + StationStausInfo stationStausInfo = new StationStausInfo(); + stationStausInfo.setStationId(site.getId().toString()); + stationStausInfo.setConnectorStatusInfos(buildConnectorStatus(site)); + return stationStausInfo; + } + + private List<ConnectorStatusInfo> buildConnectorStatus(Site site){ + List<TChargingGun> list = chargingGunService.list(new LambdaQueryWrapper<TChargingGun>().eq(TChargingGun::getDelFlag, 0) + .eq(TChargingGun::getSiteId, site.getId())); + return new ArrayList<ConnectorStatusInfo>(){{ + for (TChargingGun chargingGun : list) { + ConnectorStatusInfo connectorStatusInfo = new ConnectorStatusInfo(); + connectorStatusInfo.setConnectorId(chargingGun.getId().toString()); + switch (chargingGun.getStatus()){ + case 1: + connectorStatusInfo.setStatus(0); + break; + case 2: + connectorStatusInfo.setStatus(1); + break; + case 3: + connectorStatusInfo.setStatus(2); + break; + case 4: + connectorStatusInfo.setStatus(3); + break; + case 5: + connectorStatusInfo.setStatus(3); + break; + case 6: + connectorStatusInfo.setStatus(4); + break; + case 7: + connectorStatusInfo.setStatus(255); + break; + } + connectorStatusInfo.setSoc(0D); + connectorStatusInfo.setRemainingTime(0); + add(connectorStatusInfo); + } + }}; + } } diff --git a/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/config/AppServerConfig.java b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/config/AppServerConfig.java new file mode 100644 index 0000000..af337a4 --- /dev/null +++ b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/config/AppServerConfig.java @@ -0,0 +1,26 @@ +package com.ruoyi.jianguan.elutong.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "app.server.config") +public class AppServerConfig { + + //应用ID + private String appId; + //应用密码 + private String appSecret; + //应用公钥 + private String pubKey; + //应用私钥 + private String priKey; + //AES秘钥 + private String aesKey; + //接口地址 + private String serverUrl; + //验签公钥 + private String signPub; +} diff --git a/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/config/HttpsClientRequestFactory.java b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/config/HttpsClientRequestFactory.java new file mode 100644 index 0000000..b1438a5 --- /dev/null +++ b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/config/HttpsClientRequestFactory.java @@ -0,0 +1,130 @@ +package com.ruoyi.jianguan.elutong.config; + +import org.springframework.http.client.SimpleClientHttpRequestFactory; + +import javax.net.ssl.*; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.Socket; +import java.security.cert.X509Certificate; + +/** + * TLS的三个作用: + * (1)身份认证 + * 通过证书认证来确认对方的身份,防止中间人攻击 + * (2)数据私密性 + * 使用对称性密钥加密传输的数据,由于密钥只有客户端/服务端有,其他人无法窥探。 + * (3)数据完整性 + * 使用摘要算法对报文进行计算,收到消息后校验该值防止数据被篡改或丢失。 + * <p> + * 使用RestTemplate进行HTTPS请求访问: + * private static RestTemplate restTemplate = new RestTemplate(new HttpsClientRequestFactory()); + */ +public class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory { + @Override + protected void prepareConnection(HttpURLConnection connection, String httpMethod) { + try { + if (!(connection instanceof HttpsURLConnection)) { + throw new RuntimeException("An instance of HttpsURLConnection is expected"); + } + + HttpsURLConnection httpsConnection = (HttpsURLConnection) connection; + TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + @Override + public void checkClientTrusted(X509Certificate[] certs, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] certs, String authType) { + } + } + }; + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); + httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory())); + + httpsConnection.setHostnameVerifier(new HostnameVerifier() { + @Override + public boolean verify(String s, SSLSession sslSession) { + return true; + } + }); + + super.prepareConnection(httpsConnection, httpMethod); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static class MyCustomSSLSocketFactory extends SSLSocketFactory { + private final SSLSocketFactory delegate; + + public MyCustomSSLSocketFactory(SSLSocketFactory delegate) { + this.delegate = delegate; + } + + // 返回默认启用的密码套件。除非一个列表启用,对SSL连接的握手会使用这些密码套件。 + // 这些默认的服务的最低质量要求保密保护和服务器身份验证 + @Override + public String[] getDefaultCipherSuites() { + return delegate.getDefaultCipherSuites(); + } + + // 返回的密码套件可用于SSL连接启用的名字 + @Override + public String[] getSupportedCipherSuites() { + return delegate.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket(final Socket socket, final String host, final int port, + final boolean autoClose) throws IOException { + final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose); + return overrideProtocol(underlyingSocket); + } + + @Override + public Socket createSocket(final String host, final int port) throws IOException { + final Socket underlyingSocket = delegate.createSocket(host, port); + return overrideProtocol(underlyingSocket); + } + + @Override + public Socket createSocket(final String host, final int port, final InetAddress localAddress, + final int localPort) throws + IOException { + final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort); + return overrideProtocol(underlyingSocket); + } + + @Override + public Socket createSocket(final InetAddress host, final int port) throws IOException { + final Socket underlyingSocket = delegate.createSocket(host, port); + return overrideProtocol(underlyingSocket); + } + + @Override + public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress, + final int localPort) throws + IOException { + final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort); + return overrideProtocol(underlyingSocket); + } + + private Socket overrideProtocol(final Socket socket) { + if (!(socket instanceof SSLSocket)) { + throw new RuntimeException("An instance of SSLSocket is expected"); + } + //((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1.2"}); + ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1", "TLSv1.1", "TLSv1.2"}); + return socket; + } + } +} diff --git a/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/config/RestConfigBean.java b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/config/RestConfigBean.java new file mode 100644 index 0000000..affa36a --- /dev/null +++ b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/config/RestConfigBean.java @@ -0,0 +1,20 @@ +package com.ruoyi.jianguan.elutong.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestConfigBean { + +// @Bean +// public RestTemplate getRestTemplate() { +// return new RestTemplate(new HttpsClientRequestFactory()); +// } + + @Bean + public RestTemplate getRestTemplate() { + return new RestTemplate(); + } + +} diff --git a/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/controller/SampleController.java b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/controller/SampleController.java new file mode 100644 index 0000000..6e613f0 --- /dev/null +++ b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/controller/SampleController.java @@ -0,0 +1,341 @@ +package com.ruoyi.jianguan.elutong.controller; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.jianguan.elutong.config.AppServerConfig; +import com.ruoyi.jianguan.elutong.core.AESTools; +import com.ruoyi.jianguan.elutong.core.CommonRequest; +import com.ruoyi.jianguan.elutong.core.CommonResponse; +import com.ruoyi.jianguan.elutong.core.SignatureTools; +import com.ruoyi.integration.api.elutong.model.ConnectorStatusReq; +import com.ruoyi.integration.api.elutong.model.StationInfoReq; +import com.ruoyi.integration.api.elutong.model.StationsStatusReq; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.client.RestTemplate; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +@Slf4j +@RestController +@RequestMapping("/elutong") +public class SampleController { + + @Autowired + private AppServerConfig appServerConfig; + @Autowired + private RestTemplate restTemplate; + + /** + * 签名 + * + * @param request + * @param privateKey + * @return + */ + public static CommonRequest sign(CommonRequest request, String privateKey) { + String signStr = SignatureTools.buildSignStr(request); + signStr = signStr + "&filename=" + request.getFilename(); + log.info("【E路通】signStr : {}", signStr); + String sign = SignatureTools.rsa256Sign(signStr, privateKey); + request.setSign(sign); + return request; + } + + /** + * 验签 + * + * @param response + * @return + */ + public boolean verifySign(CommonResponse response) { + String signStr = SignatureTools.buildSignStr(response); + log.info("【E路通】signStr : {}", signStr); + log.info("【E路通】sign : {}", response.getSign()); + return SignatureTools.rsa256Verify(signStr, appServerConfig.getSignPub(), response.getSign()); + } + + + + @GetMapping("/test") + public String test() { + Date date = new Date(); + DateFormat format = new SimpleDateFormat("yyyyMMddHHmmssSSS"); + String filename = "CHARGE_DATA_STATIONINFO_REQ"+"_"+appServerConfig.getAppId()+"_"+format.format(date)+".json"; + //String filename = "SAIS_SERVICEAREA_LPAGE_REQ" + "_" + appServerConfig.getAppId() + "_" + format.format(date) + ".json"; + + + CommonRequest req = new CommonRequest(); + req.setEncryptType("AES"); + req.setSignType("RSA256"); + req.setFilename(filename); + format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + req.setTimestamp(format.format(date)); + + String requestJson = "{\n" + + "\"operatorId\":\"91530xxxxK3LK15Y\",\n" + + "\"operatorInfo\":{\n" + + "\"operatorId\":\"91530xxxxK3LK15Y\",\n" + + "\"operatorName\":\"xxx产业发展有限公司\",\n" + + "\"operatorTel1\":\"0871-61408801\",\n" + + "\"operatorTel2\":\"0871-61408801\",\n" + + "\"operatorRegAddress\":\"xxx西山区前卫街道前福路231号\",\n" + + "\"operatorNote\":null\n" + + "},\n" + + "\"stationId\":\"G56-111111-220-2-02819-0-CD-01025001\",\n" + + "\"serAreaCode\":\"G56-111111-220-2-02819-0\",\n" + + "\"itemSize\":1,\n" + + "\"stationInfos\":[\n" + + "{\n" + + "\"serAreaCode\":\"G56-111111-220-2-02819-0\",\n" + + "\"stationId\":\"G56-111111-220-2-02819-0-CD-01025001\",\n" + + "\"stationMode\":\"CD\",\n" + + "\"operatorId\":\"915300016K3LK15Y\",\n" + + "\"stationName\":\"黄草xxx车区上行\",\n" + + "\"areaCode\":\"510100\",\n" + + "\"serviceTel\":\"0871-68408801\",\n" + + "\"stationType\":1,\n" + + "\"stationStatus\":50,\n" + + "\"parkNums\":2,\n" + + "\"stationLng\":98.7116251,\n" + + "\"stationLat\":24.6117291,\n" + + "\"siteGuide\":null,\n" + + "\"construction\":11,\n" + + "\"pictures\":null,\n" + + "\"matchCars\":null,\n" + + "\"businessHours\":\"周一至周日00:00-24:00\",\n" + + "\"electricityFee\":null,\n" + + "\"serviceFee\":null,\n" + + "\"parkFee\":null,\n" + + "\"payment\":null,\n" + + "\"supportOrder\":null,\n" + + "\"equipmentInfos\":[\n" + + "{\n" + + "\"equipmentId\":\"02025001\",\n" + + "\"manufacturerId\":\"11\",\n" + + "\"manufacturerName\":null,\n" + + "\"equipmentModel\":null,\n" + + "\"productionDate\":null,\n" + + "\"equipmentType\":1,\n" + + "\"connectorInfos\":[\n" + + "{\n" + + "\"connectorId\":\"0202500101\",\n" + + "\"connectorName\":null,\n" + + "\"connectorType\":1,\n" + + "\"voltageUpperLimits\":750,\n" + + "\"voltageLowerLimits\":150,\n" + + "\"current\":40,\n" + + "\"power\":30.0,\n" + + "\"parkNo\":null,\n" + + "\"nationalStandard\":2\n" + + "}\n" + + "],\n" + + "\"equipmentLng\":null,\n" + + "\"equipmentLat\":null,\n" + + "\"power\":37.0,\n" + + "\"equipmentName\":null\n" + + "}\n" + + "],\n" + + "\"remark\":null\n" + + "}\n" + + "]\n" + + "}\n"; + //String requestJson = "{\"pageNum\":1,\"pageSize\":700}"; + System.out.println("queryParam : " + requestJson); + String bizContentJson = AESTools.encrypt(requestJson, appServerConfig.getAesKey()); + + req.setBizContent(bizContentJson); + System.out.println("encrypt CommonRequest toJson : " + JSONObject.toJSONString(req)); + req = sign(req, appServerConfig.getPriKey()); + System.out.println("sign : " + req.getSign()); + System.out.println("request body : " + JSONObject.toJSONString(req)); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON_UTF8); + String bodyStr = JSONObject.toJSONString(req); + HttpEntity<String> request = new HttpEntity<String>(bodyStr, headers); + ResponseEntity<String> response = restTemplate.postForEntity(appServerConfig.getServerUrl(), request, String.class); + String result = ""; + if (response.getStatusCodeValue() == 200) { + result = response.getBody(); + System.out.println("response result : " + result); + CommonResponse commonResponse = JSONObject.parseObject(result, CommonResponse.class); + if ("200".equals(commonResponse.getStatusCode())) { + boolean rsa256Verify = verifySign(commonResponse); + System.out.println("verify sign result : " + rsa256Verify); + } + } + return result; + } + + + /** + * 推送充电站静态信息 + * @param stationInfoReq + * @return + */ + @ResponseBody + @PostMapping("/pushStationInfo") + public R pushStationInfo(@RequestBody StationInfoReq stationInfoReq){ + Date date = new Date(); + DateFormat format = new SimpleDateFormat("yyyyMMddHHmmssSSS"); + String filename = "CHARGE_DATA_STATIONINFO_REQ_" + appServerConfig.getAppId() + "_" + format.format(date) + ".json"; + + CommonRequest req = new CommonRequest(); + req.setEncryptType("AES"); + req.setSignType("RSA256"); + req.setFilename(filename); + format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + req.setTimestamp(format.format(date)); + + String requestJson = JSON.toJSONString(stationInfoReq); + log.info("【E路通-推送充电站静态信息】queryParam : {}", requestJson); + String bizContentJson = AESTools.encrypt(requestJson, appServerConfig.getAesKey()); + + req.setBizContent(bizContentJson); + log.info("【E路通-推送充电站静态信息】encrypt CommonRequest toJson : {}", JSONObject.toJSONString(req)); + req = sign(req, appServerConfig.getPriKey()); + log.info("【E路通-推送充电站静态信息】sign : {}", req.getSign()); + log.info("【E路通-推送充电站静态信息】request body : {}", JSONObject.toJSONString(req)); + + HttpRequest post = HttpUtil.createPost(appServerConfig.getServerUrl()); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON_UTF8); + post.header(headers); + String bodyStr = JSONObject.toJSONString(req); + post.body(bodyStr); + HttpResponse execute = post.execute(); + String result = ""; + if (execute.getStatus() == 200) { + result = execute.body(); + log.info("【E路通-推送充电站静态信息】response result : {}", result); + CommonResponse commonResponse = JSONObject.parseObject(result, CommonResponse.class); + if ("200".equals(commonResponse.getStatusCode())) { + boolean rsa256Verify = verifySign(commonResponse); + log.info("【E路通-推送充电站静态信息】verify sign result : {}", rsa256Verify); + return R.ok(); + }else{ + return R.fail(commonResponse.getErrorMsg()); + } + } + return R.fail("推送接口请求失败"); + } + + + /** + * 推送充电站状态信息 + * @param stationsStatusReq + * @return + */ + @ResponseBody + @PostMapping("/pushStationsStatus") + public R pushStationsStatus(@RequestBody StationsStatusReq stationsStatusReq){ + Date date = new Date(); + DateFormat format = new SimpleDateFormat("yyyyMMddHHmmssSSS"); + String filename = "CHARGE_DATA_STATIONSTATUS_REQ_" + appServerConfig.getAppId() + "_" + format.format(date) + ".json"; + + CommonRequest req = new CommonRequest(); + req.setEncryptType("AES"); + req.setSignType("RSA256"); + req.setFilename(filename); + format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + req.setTimestamp(format.format(date)); + + String requestJson = JSON.toJSONString(stationsStatusReq); + log.info("【E路通-推送充电站状态信息】queryParam : {}", requestJson); + String bizContentJson = AESTools.encrypt(requestJson, appServerConfig.getAesKey()); + + req.setBizContent(bizContentJson); + log.info("【E路通-推送充电站状态信息】encrypt CommonRequest toJson : {}", JSONObject.toJSONString(req)); + req = sign(req, appServerConfig.getPriKey()); + log.info("【E路通-推送充电站状态信息】sign : {}", req.getSign()); + log.info("【E路通-推送充电站状态信息】request body : {}", JSONObject.toJSONString(req)); + + HttpRequest post = HttpUtil.createPost(appServerConfig.getServerUrl()); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON_UTF8); + post.header(headers); + String bodyStr = JSONObject.toJSONString(req); + post.body(bodyStr); + HttpResponse execute = post.execute(); + String result = ""; + if (execute.getStatus() == 200) { + result = execute.body(); + log.info("【E路通-推送充电站状态信息】response result : {}", result); + CommonResponse commonResponse = JSONObject.parseObject(result, CommonResponse.class); + if ("200".equals(commonResponse.getStatusCode())) { + boolean rsa256Verify = verifySign(commonResponse); + log.info("【E路通-推送充电站状态信息】verify sign result : {}", rsa256Verify); + return R.ok(); + }else{ + return R.fail(commonResponse.getErrorMsg()); + } + } + return R.fail("推送接口请求失败"); + } + + + /** + * 推送设备接口状态信息 + * @param connectorStatusReq + * @return + */ + @ResponseBody + @PostMapping("/pushConnectorStatus") + public R pushConnectorStatus(@RequestBody ConnectorStatusReq connectorStatusReq){ + Date date = new Date(); + DateFormat format = new SimpleDateFormat("yyyyMMddHHmmssSSS"); + String filename = "CHARGE_NOTIFICATION_CONNECTORSTAUS_REQ_" + appServerConfig.getAppId() + "_" + format.format(date) + ".json"; + + CommonRequest req = new CommonRequest(); + req.setEncryptType("AES"); + req.setSignType("RSA256"); + req.setFilename(filename); + format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + req.setTimestamp(format.format(date)); + + String requestJson = JSON.toJSONString(connectorStatusReq); + log.info("【E路通-推送设备接口状态信息】queryParam : {}", requestJson); + String bizContentJson = AESTools.encrypt(requestJson, appServerConfig.getAesKey()); + + req.setBizContent(bizContentJson); + log.info("【E路通-推送设备接口状态信息】encrypt CommonRequest toJson : {}", JSONObject.toJSONString(req)); + req = sign(req, appServerConfig.getPriKey()); + log.info("【E路通-推送设备接口状态信息】sign : {}", req.getSign()); + log.info("【E路通-推送设备接口状态信息】request body : {}", JSONObject.toJSONString(req)); + + HttpRequest post = HttpUtil.createPost(appServerConfig.getServerUrl()); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON_UTF8); + post.header(headers); + String bodyStr = JSONObject.toJSONString(req); + post.body(bodyStr); + HttpResponse execute = post.execute(); + String result = ""; + if (execute.getStatus() == 200) { + result = execute.body(); + log.info("【E路通-推送设备接口状态信息】response result : {}", result); + CommonResponse commonResponse = JSONObject.parseObject(result, CommonResponse.class); + if ("200".equals(commonResponse.getStatusCode())) { + boolean rsa256Verify = verifySign(commonResponse); + log.info("【E路通-推送设备接口状态信息】verify sign result : {}", rsa256Verify); + return R.ok(); + }else{ + return R.fail(commonResponse.getErrorMsg()); + } + } + return R.fail("推送接口请求失败"); + } + +} diff --git a/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/AESTools.java b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/AESTools.java new file mode 100644 index 0000000..80ec58f --- /dev/null +++ b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/AESTools.java @@ -0,0 +1,69 @@ +package com.ruoyi.jianguan.elutong.core; + +import org.slf4j.LoggerFactory; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +public class AESTools { + + private static final String KEY_ALGORITHM = "AES"; + private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";// 默认的加密算法 + + /** + * AES 加密操作 + * + * @param content 待加密内容 + * @param password 加密密码 + * @return 返回Base64转码后的加密数据 + */ + public static String encrypt(String content, String password) { + try { + Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);// 创建密码器 + byte[] byteContent = content.getBytes("utf-8"); + cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(password));// 初始化为加密模式的密码器 + byte[] result = cipher.doFinal(byteContent);// 加密 + return Base64Tools.encodeToString(result);// 通过Base64转码返回 + } catch (Exception ex) { + LoggerFactory.getLogger(AESTools.class).error("", ex); + throw new RuntimeException("AES加密失败:" + content); + } + } + + /** + * AES 解密操作 + * + * @param content + * @param password + * @return + */ + public static String decrypt(String content, String password) { + try { + // 实例化 + Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM); + // 使用密钥初始化,设置为解密模式 + cipher.init(Cipher.DECRYPT_MODE, getSecretKey(password)); + // 执行操作 + byte[] result = cipher.doFinal(Base64Tools.decodeFromString(content)); + return new String(result, "utf-8"); + } catch (Exception ex) { + LoggerFactory.getLogger(AESTools.class).error("", ex); + throw new RuntimeException("AES解密失败:" + content); + } + } + + /** + * 生成加密秘钥 + * + * @return + */ + private static SecretKeySpec getSecretKey(final String password) { + try { + return new SecretKeySpec(password.getBytes("UTF-8"), KEY_ALGORITHM);// 转换为AES专用密钥 + } catch (Exception ex) { + LoggerFactory.getLogger(AESTools.class).error("", ex); + throw new RuntimeException("生成加密秘钥"); + } + } + +} diff --git a/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/Base64Tools.java b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/Base64Tools.java new file mode 100644 index 0000000..3e81600 --- /dev/null +++ b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/Base64Tools.java @@ -0,0 +1,111 @@ +package com.ruoyi.jianguan.elutong.core; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +public abstract class Base64Tools { + + private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; + + /** + * Base64-encode the given byte array. + * + * @param src the original byte array + * @return the encoded byte array + */ + public static byte[] encode(byte[] src) { + if (src.length == 0) { + return src; + } + return Base64.getEncoder().encode(src); + } + + /** + * Base64-decode the given byte array. + * + * @param src the encoded byte array + * @return the original byte array + */ + public static byte[] decode(byte[] src) { + if (src.length == 0) { + return src; + } + return Base64.getDecoder().decode(src); + } + + /** + * Base64-encode the given byte array using the RFC 4648 "URL and Filename Safe Alphabet". + * + * @param src the original byte array + * @return the encoded byte array + * @since 4.2.4 + */ + public static byte[] encodeUrlSafe(byte[] src) { + if (src.length == 0) { + return src; + } + return Base64.getUrlEncoder().encode(src); + } + + /** + * Base64-decode the given byte array using the RFC 4648 "URL and Filename Safe Alphabet". + * + * @param src the encoded byte array + * @return the original byte array + * @since 4.2.4 + */ + public static byte[] decodeUrlSafe(byte[] src) { + if (src.length == 0) { + return src; + } + return Base64.getUrlDecoder().decode(src); + } + + /** + * Base64-encode the given byte array to a String. + * + * @param src the original byte array (may be {@code null}) + * @return the encoded byte array as a UTF-8 String + */ + public static String encodeToString(byte[] src) { + if (src.length == 0) { + return ""; + } + return new String(encode(src), DEFAULT_CHARSET); + } + + /** + * Base64-decode the given byte array from an UTF-8 String. + * + * @param src the encoded UTF-8 String + * @return the original byte array + */ + public static byte[] decodeFromString(String src) { + if (src.isEmpty()) { + return new byte[0]; + } + return decode(src.getBytes(DEFAULT_CHARSET)); + } + + /** + * Base64-encode the given byte array to a String using the RFC 4648 "URL and Filename Safe Alphabet". + * + * @param src the original byte array + * @return the encoded byte array as a UTF-8 String + */ + public static String encodeToUrlSafeString(byte[] src) { + return new String(encodeUrlSafe(src), DEFAULT_CHARSET); + } + + /** + * Base64-decode the given byte array from an UTF-8 String using the RFC 4648 "URL and Filename Safe Alphabet". + * + * @param src the encoded UTF-8 String + * @return the original byte array + */ + public static byte[] decodeFromUrlSafeString(String src) { + return decodeUrlSafe(src.getBytes(DEFAULT_CHARSET)); + } + +} diff --git a/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/CommonRequest.java b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/CommonRequest.java new file mode 100644 index 0000000..9541b9f --- /dev/null +++ b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/CommonRequest.java @@ -0,0 +1,16 @@ +package com.ruoyi.jianguan.elutong.core; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class CommonRequest { + private String encryptType; + private String signType; + private String filename; + private String bizContent; + private String sign; + private String timestamp; + private String version = "1.0"; +} diff --git a/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/CommonResponse.java b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/CommonResponse.java new file mode 100644 index 0000000..8895a3a --- /dev/null +++ b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/CommonResponse.java @@ -0,0 +1,12 @@ +package com.ruoyi.jianguan.elutong.core; + + +import lombok.Data; + +@Data +public class CommonResponse { + private String bizContent; + private String errorMsg; + private String sign; + private String statusCode; +} diff --git a/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/SignAlgorithm.java b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/SignAlgorithm.java new file mode 100644 index 0000000..edca3b1 --- /dev/null +++ b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/SignAlgorithm.java @@ -0,0 +1,64 @@ +package com.ruoyi.jianguan.elutong.core; + +/** + * 签名算法类型 + */ +public enum SignAlgorithm { + + // The RSA signature algorithm + NONEwithRSA("NONEwithRSA", "RSA"), + + // The MD2/MD5 with RSA Encryption signature algorithm + MD2withRSA("MD2withRSA", "RSA"), + MD5withRSA("MD5withRSA", "RSA"), + + // The signature algorithm with SHA-* and the RSA + SHA1withRSA("SHA1withRSA", "RSA"), + SHA256withRSA("SHA256withRSA", "RSA"), + SHA384withRSA("SHA384withRSA", "RSA"), + SHA512withRSA("SHA512withRSA", "RSA"), + + // The Digital Signature Algorithm + NONEwithDSA("NONEwithDSA", "DSA"), + // The DSA with SHA-1 signature algorithm + SHA1withDSA("SHA1withDSA", "DSA"), + + // The ECDSA signature algorithms + NONEwithECDSA("NONEwithECDSA", "EC"), + SHA1withECDSA("SHA1withECDSA", "EC"), + SHA256withECDSA("SHA256withECDSA", "EC"), + SHA384withECDSA("SHA384withECDSA", "EC"), + SHA512withECDSA("SHA512withECDSA", "EC"); + + private String value; + private String type; + + /** + * 构造 + * + * @param value 算法字符表示,区分大小写 + * @param type XXXwithXXX算法的后半部分算法,如果为ECDSA,返回算法为EC + */ + private SignAlgorithm(String value, String type) { + this.value = value; + this.type = type; + } + + /** + * 获取算法字符串表示,区分大小写 + * + * @return 算法字符串表示 + */ + public String getValue() { + return this.value; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + +} diff --git a/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/SignatureTools.java b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/SignatureTools.java new file mode 100644 index 0000000..8619067 --- /dev/null +++ b/ruoyi-service/ruoyi-jianguan/src/main/java/com/ruoyi/jianguan/elutong/core/SignatureTools.java @@ -0,0 +1,135 @@ +package com.ruoyi.jianguan.elutong.core; + +import org.apache.commons.lang3.StringUtils; + +import java.lang.reflect.Field; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +public class SignatureTools { + + public static final String PUBLIC_KEY = "RSAPublicKey"; + public static final String PRIVATE_KEY = "RSAPrivateKey"; + private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; + + /** + * sha256WithRsa 加签 + * + * @param content 待签名的字符串 + * @param priKeyBase64 base64编码的私钥 + * @return base64编码的签名 + */ + public static String rsa256Sign(String content, String priKeyBase64) { + byte[] signed = sign(SignAlgorithm.SHA256withRSA, content.getBytes(DEFAULT_CHARSET), Base64Tools.decodeFromString(priKeyBase64)); + return Base64Tools.encodeToString(signed); + } + + /** + * sha256WithRsa 验签 + * + * @param content 待验签的字符串 + * @param pubKeyBase64 base64编码的公钥 + * @param signBase64 base64编码签名 + * @return 签名是否正确 + */ + public static boolean rsa256Verify(String content, String pubKeyBase64, String signBase64) { + return verify(SignAlgorithm.SHA256withRSA, content.getBytes(DEFAULT_CHARSET), + Base64Tools.decodeFromString(signBase64), Base64Tools.decodeFromString(pubKeyBase64)); + } + + public static byte[] sign(SignAlgorithm algorithm, byte[] content, byte[] key) { + try { + PrivateKey priKey = generatePrivateKey(algorithm, key); + Signature signature = Signature.getInstance(algorithm.getValue()); + signature.initSign(priKey); + signature.update(content); + byte[] signed = signature.sign(); + return signed; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static boolean verify(SignAlgorithm algorithm, byte[] content, byte[] sign, byte[] key) { + try { + Signature signature = Signature.getInstance(algorithm.getValue()); + PublicKey publicKey = generatePublicKey(algorithm, key); + signature.initVerify(publicKey); + signature.update(content); + return signature.verify(sign); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + public static PrivateKey generatePrivateKey(SignAlgorithm algorithmType, byte[] key) + throws InvalidKeySpecException, NoSuchAlgorithmException { + return generatePrivateKey(algorithmType, new PKCS8EncodedKeySpec(key)); + } + + public static PrivateKey generatePrivateKey(SignAlgorithm algorithmType, KeySpec keySpec) + throws InvalidKeySpecException, NoSuchAlgorithmException { + return KeyFactory.getInstance(algorithmType.getType()).generatePrivate(keySpec); + } + + public static PublicKey generatePublicKey(SignAlgorithm algorithm, byte[] key) + throws InvalidKeySpecException, NoSuchAlgorithmException { + return generatePublicKey(algorithm, new X509EncodedKeySpec(key)); + } + + public static PublicKey generatePublicKey(SignAlgorithm algorithm, KeySpec keySpec) + throws InvalidKeySpecException, NoSuchAlgorithmException { + return KeyFactory.getInstance(algorithm.getType()).generatePublic(keySpec); + } + + public static String buildSignStr(Object object) { + if (object == null) { + return null; + } + Map map = new HashMap(); + + for (Field field : object.getClass().getDeclaredFields()) { + field.setAccessible(true); + try { + if ("sign".equals(field.getName())) { + continue; + } + if ("filename".equals(field.getName())) { + continue; + } + + map.put(field.getName(), field.get(object)); + } catch (IllegalArgumentException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalAccessException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + TreeMap<String, String> treeMap = new TreeMap<>(map); + StringBuffer strBuffer = new StringBuffer(); + treeMap.entrySet().forEach(i -> { + if (i.getValue() == null) { + return; + } + if (StringUtils.isBlank(i.getValue())) { + return; + } + strBuffer.append(i.getKey()).append("=").append(String.valueOf(i.getValue())).append("&"); + }); + String signStr = strBuffer.substring(0, strBuffer.length() - 1); + return signStr; + } + + +} -- Gitblit v1.7.1