以下是根据图片内容整理的Markdown源文件(可直接编辑使用):
高德充电设备状态推送接口实现文档
一、接口基本信息
- SPI:
amap.charging.pushStationStatus
- 方向: 商家 → 高德
- 描述: 商家实时推送全量充电设备状态信息
- 前置条件: 接入准备文档
- 业务文档: 接口规范
二、核心实现代码
1. 配置类
/*** @author Cyf* @since 1.0.0*/
@Data
@Component
@ConfigurationProperties(prefix="gaode")
public class GaodeConfig {private String appId; // 应用IDprivate String privateKey; // 商户私钥private String pushStationStatus; // 接口方法名private String appEnv; // 环境标识private String url; // API地址
}
2. 数据模型
站点信息模型
/*** @author Cyf* @since 1.0.0*/
@Data
public class GaodePush {@JsonProperty("stationID")private String stationID; // 场站编码@JsonProperty("fast_free")private Integer fastFree; // 快充空闲数@JsonProperty("fast_total")private Integer fastTotal; // 快充总数@JsonProperty("slow_free")private Integer slowFree; // 慢充空闲数@JsonProperty("slow_total")private Integer slowTotal; // 慢充总数@JsonProperty("connectorStatusInfo")private List<GaoDePushConnectorStatusInfoVO> connectorStatusInfo; // 充电枪列表
}
充电枪状态模型
/*** @author Cyf* @since 1.0.0*/
@Data
public class GaoDePushConnectorStatusInfoVO {@JsonProperty("EquipmentID")private String equipmentId; // 设备ID@JsonProperty("ConnectorID")private String connectorId; // 充电接口编码@JsonProperty("Status")private Integer status; // 接口状态(0:离线 1:空闲 2:占用)
}
3. 功能实现
/*** @author Cyf* @since 1.0.0*/
@Service
@RequiredArgsConstructor
public class GaodeService {private final OkHttpClient client = new OkHttpClient();private final GaodeConfig gaodeConfig;public String pushStationStatus() {//具体的业务List<GaoDePush> list = chargingStationService.listStationStatus();for (GaoDePush gaoDePush : list) {List<GaoDePushConnectorStatusInfoVO> gunInfo = chargingGunService.getGunInfo(gaoDePush.getStationID());gaoDePush.setConnectorStatusInfo(gunInfo);}//将所需要的信息转换成JSON字符串String json = JsonUtils.toJson(list);try {//封装,加签,发送请求return push(json);} catch (Exception e) {throw new RuntimeException(e);}}public String push(String json) throws Exception {Map<String, String> paramMap = new HashMap<String, String>();// 构造业务参数// 构造参与加签的公共参数paramMap.put("charset", "UTF-8");paramMap.put("biz_content", json);paramMap.put("utc_timestamp", String.valueOf(System.currentTimeMillis()));paramMap.put("app_id", gaodeConfig.getAppId());paramMap.put("version", "1.0");paramMap.put("method", gaodeConfig.getPushStationStatus());paramMap.put("sign_type", "RSA2");//带着前面的参数去生成签名String sign = generateSign(paramMap);paramMap.put("sign", sign);//打印最终请求高德参数System.out.println("请求高德的参数:" + paramMap);//推送高德接口return sendRequest(paramMap, gaodeConfig.getUrl());}/*** 使用商家私钥生成签名** @param paramMap* @return* @throws Exception*/public String generateSign(Map<String, String> paramMap) throws Exception {//使用商家私钥进行加签,请在高德云店「接入准备及配置」页面生成并获取商家私钥String signContent = getSignContent(paramMap);return getSign(signContent, gaodeConfig.getPrivateKey());}/*** 参数转换为待加签字符串** @param paramMap 待生成加密sign的参数集合*/private String getSignContent(Map<String, String> paramMap) {StringBuilder content = new StringBuilder();List<String> keys = new ArrayList<>(paramMap.keySet());// 将参数集合排序Collections.sort(keys);for (int i = 0; i < keys.size(); i++) {String key = keys.get(i);//排除不需要参与签名的公共参数if ("sign_type".equals(key) || "sign".equals(key) || "need_encrypt".equals(key)) {continue;}String value = paramMap.get(key);// 拼装所有非空参数if (key != null && !"".equalsIgnoreCase(key) && value != null && !"".equalsIgnoreCase(value)) {content.append(i == 0 ? "" : "&").append(key).append("=").append(value);}}return content.toString();}/*** 字符串加签** @param signContent 待加密的参数字符串* @param merchantPrivateKey 商家应用私钥* @throws IOException* @throws GeneralSecurityException*/private String getSign(String signContent, String merchantPrivateKey) throws IOException, GeneralSecurityException {// 添加空值检查if (merchantPrivateKey == null || merchantPrivateKey.isEmpty()) {throw new IllegalStateException("商户私钥未配置或为空");}KeyFactory keyFactory = KeyFactory.getInstance("RSA");// 清理私钥内容,移除可能的头尾和空格String privateKeyContent = merchantPrivateKey.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replace("-----BEGIN RSA PRIVATE KEY-----", "").replace("-----END RSA PRIVATE KEY-----", "").replaceAll("\\s", "");byte[] encodedKey = Base64.getDecoder().decode(privateKeyContent);PrivateKey priKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));Signature signature = Signature.getInstance("SHA256WithRSA");signature.initSign(priKey);signature.update(signContent.getBytes(StandardCharsets.UTF_8));byte[] signed = signature.sign();return Base64.getEncoder().encodeToString(signed);}/*** 发送请求* @param paramMap 请求参数* @param apiUrl 请求地址* @return 响应结果* @throws IOException*/public String sendRequest(Map<String, String> paramMap, String apiUrl) throws IOException {// 1. 构建FormBody.BuilderFormBody.Builder formBuilder = new FormBody.Builder();// 2. 遍历paramMap,添加所有参数for (Map.Entry<String, String> entry : paramMap.entrySet()) {formBuilder.add(entry.getKey(), entry.getValue());}// 3. 构建RequestBodyRequestBody formBody = formBuilder.build();// 4. 创建RequestRequest request = new Request.Builder().url(apiUrl).post(formBody).header("Content-Type", "application/x-www-form-urlencoded").build();System.out.println("发送请求: " + request);// 5. 发送请求并处理响应try (Response response = client.newCall(request).execute()) {if (!response.isSuccessful()) {throw new IOException("请求失败,状态码: " + response.code());}String result = response.body().string();System.out.println("响应结果: " + result);return result;}}
}
4. JsonUtils工具类
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.type.MapLikeType;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @author Cyf* @since 1.0.0*/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class JsonUtils {private static final ObjectMapper OBJECT_MAPPER = SpringUtil.getBean(ObjectMapper.class);static {// 忽略未知字段OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, Boolean.FALSE);// 属性为NULL不被序列化OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);// 关闭空对象不让序列化功能OBJECT_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, Boolean.FALSE);// 允许没有引号的字段名OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, Boolean.TRUE);// 允许单引号字段名OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, Boolean.TRUE);OBJECT_MAPPER.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);}public static <T> T readValue(String json, Type type) throws JsonProcessingException {return OBJECT_MAPPER.readValue(json, OBJECT_MAPPER.constructType(type));}public static <T> String toJson(T t) {try {return OBJECT_MAPPER.writeValueAsString(t);} catch (JsonProcessingException ex) {log.error("Jackson对象转json异常", ex);}return null;}public static <T> T toObj(String s, Class<T> c) {try {return OBJECT_MAPPER.readValue(s, c);} catch (Exception ex) {log.error("Jackson转换对象异常:{}", s, ex);}return null;}public static <T> T toObj(String s, TypeReference<T> t) {try {return OBJECT_MAPPER.readValue(s, t);} catch (Exception ex) {log.error("Jackson转换对象异常:{}", s, ex);}return null;}public static <T> List<T> toList(String s, Class<T> c) {if (CharSequenceUtil.isBlank(s)) {return List.of();}try {JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructParametricType(List.class, c);return OBJECT_MAPPER.readValue(s, javaType);} catch (Exception ex) {log.error("Jackson转换List异常:{}", s, ex);}return List.of();}public static Map<String, Object> toMap(String s) {if (CharSequenceUtil.isBlank(s)) {return Map.of();}MapLikeType mapLikeType = OBJECT_MAPPER.getTypeFactory().constructMapLikeType(HashMap.class, String.class, Object.class);try {return OBJECT_MAPPER.readValue(s, mapLikeType);} catch (Exception ex) {log.error("Jackson转换Map异常:{}", s, ex);}return Map.of();}}
📌 使用说明:
1. 复制此Markdown到支持GFM的编辑器(如Typora、VS Code)
2. 图片路径需替换为实际存储位置
3. 代码块支持直接复制到IDE中使用
1. 复制此Markdown到支持GFM的编辑器(如Typora、VS Code)
2. 图片路径需替换为实际存储位置
3. 代码块支持直接复制到IDE中使用
文件特性:
-
模块化布局:采用色块区分不同章节
-
响应式设计:适配各种Markdown阅读器
-
完整注释:包含类和方法级的JavaDoc注释
-
可视化元素:
• 配色方案:Google Material Design• 图片尺寸控制语法
-
即用性:
• 代码块可直接复制到项目中使用• 链接均为可点击格式
如需进一步定制(如调整配色方案、增加流程图等),可告知具体需求。