医保移动支付和接口对接开发
1 · 医保移动支付开发注意事项。
2 · HTTP 基础知识: 回顾 HTTP 请求的基本结构和工作原理,包括请求方法、请求头、请求体等。
3 · 数据格式处理: 探讨如何正确处理不同数据格式(如 JSON、XML)的请求和响应。
4 · 错误处理策略: 研究异常情况下的最佳实践,包括重试策略、超时处理和错误状态码处理。
5 · 性能优化: 提供性能优化技巧,减少请求延迟和提高吞吐量。
6 · 第三方服务认证: 学习如何进行认证和授权,以确保只有授权的请求可以访问第三方服务。(服务接口提供方)
7 · 安全性: 讲解如何保护 HTTP 请求和响应,防止潜在的安全漏洞。(服务接口提供方)
8 · 日志和监控: 了解如何记录请求和响应信息,以及如何设置监控来追踪系统的健康状况
1医保移动支付开发注意事项:
1.1微信医保移动支付关注文档:
https://docs.qq.com/doc/DV1J6ZVB6eHZ3amxK
1.2用户授权接入文档见链接:
用户授权接入文档(医保信息授权)(payAuthNo版本) https://docs.qq.com/doc/DV3JYRG1xelhKTWdz
1.3对接移动医疗平台接口文档见链接:
对接移动医疗平台接口文档_国家局v4.0 https://docs.qq.com/doc/DV3lxV3hSbXFudVBE
2 · HTTP基础知识: 回顾 HTTP1.1 请求的基本结构和工作原理,包括请求方法、请求头、请求体等。
2.1 请求例子:
curl --location --request POST ‘https://hlwyy.songjianghealth.com/zsyy/insuranceWx/refundInsurance?refund_key=8e6458f38c2ca66215c94a42c517ddfd’
–header ‘Content-Type: application/json’
–data-raw ’ {
“appRefdSn”: “ORD310100202309221504550007718”,
“appRefdTime”: “20230529210946”,
“cashRefdAmt”: “1.00”,
“ecToken”: “”,
“expContent”: “”,
“fundRefdAmt”: “0”,
“outTradeNo”: “0”,
“payAuthNo”: “ORD310100202309221504550007718”,
“payOrdId”: “ORD310100202309221504550007718”,
“payWay”: “01”,
“psnAcctRefdAmt”: “1.00”,
“refdType”: “HI”,
“totlRefdAmt”: “15.54”
}’
2.2 请求客户端 httpClient
https://hc.apache.org/httpcomponents-client-4.5.x/current/tutorial/html/connmgmt.html
package cn.google.util;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.*;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.UnsupportedEncodingException;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
-
HttpClient工具类
-
@author
-
@date 2023-09-18 17:12
/
@Slf4j
public class HttpClientUtil {
/*- 超时时间
/
private static final int TIMEOUT = 30 * 1000;
/* - 最大连接数
/
private static final int MAX_TOTAL = 200;
/* - 每个路由的默认最大连接数
/
private static final int MAX_PER_ROUTE = 40;
/* - 目标主机的最大连接数
/
private static final int MAX_ROUTE = 100;
/* - 访问失败时最大重试次数
*/
private static final int MAX_RETRY_TIME = 5;
private static CloseableHttpClient httpClient = null;
private static final Object SYNC_LOCK = new Object();
private static final String DEFAULT_CHARSET = “UTF-8”;private static void config(HttpRequestBase httpRequestBase) {
//配置请求的超时时间
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(TIMEOUT)
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.build();
httpRequestBase.setConfig(requestConfig);
}/**
- 获取HttpClient对象
/
private static CloseableHttpClient getHttpClient(String url) throws NoSuchAlgorithmException, KeyManagementException {
String hostName = url.split(“/”)[2];
int port = 80;
if (hostName.contains(“:”)) {
String[] attr = hostName.split(“:”);
hostName = attr[0];
port = Integer.parseInt(attr[1]);
}
if (httpClient == null) {
synchronized (SYNC_LOCK) {
if (httpClient == null) {
httpClient = createHttpClient(MAX_TOTAL, MAX_PER_ROUTE, MAX_ROUTE, hostName, port);
}
}
}
return httpClient;
}
/* - 创建HttpClient对象
*/
private static CloseableHttpClient createHttpClient(int maxTotal, int maxPerRoute, int maxRoute,
String hostName, int port) throws KeyManagementException, NoSuchAlgorithmException {
PlainConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(createIgnoreVerifySSL());
Registry registry = RegistryBuilder.create()
.register(“http”, plainsf)
.register(“https”, sslsf)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
//增加最大连接数
cm.setMaxTotal(maxTotal);
//增加每个路由的默认最大连接
cm.setDefaultMaxPerRoute(maxPerRoute);
//增加目标主机的最大连接数
cm.setMaxPerRoute(new HttpRoute(new HttpHost(hostName, port)), maxRoute);
//请求重试
HttpRequestRetryHandler httpRequestRetryHandler = (exception, executionCount, context) -> {
//若重试5次,放弃
if (executionCount >= MAX_RETRY_TIME) {
return false;
}
//若服务器丢掉了连接,那就重试
if (exception instanceof NoHttpResponseException) {
return true;
}
//不重试SSL握手异常
if (exception instanceof SSLHandshakeException) {
return false;
}
//超时
if (exception instanceof InterruptedIOException) {
return false;
}
//目标服务器不可达
if (exception instanceof UnknownHostException) {
return false;
}
//SSL握手异常
if (exception instanceof SSLException) {
return false;
}
HttpClientContext clientContext = HttpClientContext.adapt(context);
HttpRequest request = clientContext.getRequest();
//若请求时幂等的,就再次尝试
return !(request instanceof HttpEntityEnclosingRequest);
};
return HttpClients.custom().setConnectionManager(cm)
.setRetryHandler(httpRequestRetryHandler)
.build();
}
/**
-
HttpClient配置SSL绕过https证书(因为我的网站是有https证书的,所以在访问https网站时,会自动读取我的证书,
-
和目标网站不符,会报错),所以这里需要绕过https证书
*/
private static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
SSLContext sslContext = SSLContext.getInstance(“SSLv3”);
// 实现一个X509TrustManager接口,用于绕过验证,不用修改里面的方法
X509TrustManager trustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {} @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
};
sslContext.init(null, new TrustManager[] {trustManager}, null);
return sslContext;
}
private static void setPostParams(HttpPost httpPost, Map<String, Object> params) {
List nameValuePairs = new ArrayList<>();
params.forEach((key, value) -> nameValuePairs.add(new BasicNameValuePair(key, value.toString())));
try {
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, DEFAULT_CHARSET));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
/**- post请求,默认编码格式为UTF-8
- @param url 请求地址
- @param params 请求参数
- @return 响应正文
/
public static String doPost(String url, Map<String, Object> params) {
return doPost(url, params, DEFAULT_CHARSET);
}
/* - post请求
- @param url 请求地址
- @param params 请求参数
- @param charset 字符编码
- @return 响应正文
*/
public static String doPost(String url, Map<String, Object> params, String charset) {
HttpPost httpPost = new HttpPost(url);
config(httpPost);
setPostParams(httpPost, params);
return getResponse(url, httpPost, charset);
}
/**
- get请求,默认编码UTF-8
- @param url 请求地址
- @return 响应正文
/
public static String doGet(String url) {
return doGet(url, DEFAULT_CHARSET);
}
/* - get请求
- @param url 请求地址
- @param charset 字符编码
- @return 响应正文
*/
public static String doGet(String url, String charset) {
HttpGet httpGet = new HttpGet(url);
config(httpGet);
return getResponse(url, httpGet, charset);
}
/**
- 发起请求,获取响应
- @param url 请求地址
- @param httpRequest 请求对象
- @param charset 字符编码
- @return 响应正文
*/
private static String getResponse(String url, HttpRequestBase httpRequest, String charset) {
CloseableHttpResponse response = null;
try {
response = getHttpClient(url).execute(httpRequest, HttpClientContext.create());
HttpEntity httpEntity = response.getEntity();
String result = EntityUtils.toString(httpEntity, charset);
EntityUtils.consume(httpEntity);
return result;
} catch (IOException | NoSuchAlgorithmException | KeyManagementException e) {
log.error(“网络访问异常!”, e);
} finally {
try {
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
- 超时时间
}
2.3 请求客户端restTemplate.
package cn.ucmed.zsyy.common.config.restTemplate;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.*;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
-
@author ucmed
-
@since 2022/1/4 16:13
*/
@Slf4j
@Configuration
public class RestTemplateConfig {@Value(“ h t t p . c o n n e c t i o n . m a x " ) p r i v a t e i n t c o n n e c t i o n M a x ; @ V a l u e ( " {http.connection.max}") private int connectionMax; @Value(" http.connection.max")privateintconnectionMax;@Value("{http.connection.route.max}”)
private int connectionRouteMax;
@Value(“ h t t p . c o n n e c t . t i m e o u t " ) p r i v a t e i n t c o n n e c t T i m e o u t ; @ V a l u e ( " {http.connect.timeout}") private int connectTimeout; @Value(" http.connect.timeout")privateintconnectTimeout;@Value("{http.socket.timeout}”)
private int socketTimeout;
@Value(“${http.request.timeout}”)
private int requestTimeout;@Bean
public RestTemplate getRestTemplate() {
RestTemplate restTemplate = new RestTemplate(httpComponentsClientHttpRequestFactory());
List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters(); Iterator
3 · 接口数据格式处理: 探讨如何正确处理不同数据格式(如 JSON、XML)的请求和响应。
3.1 利用excel表格生成javaBean代码
代码及文档: 使用文档拷贝,入参 出参处理。 代码注释包含 属性描述 说明备注 是否必填等
3.2 出入参数业务异常统一处理.
String xmlReturnStr = centerHospitalHttpService.postRequestHis(ShanghaiSongCenterHospitalHttpService.HIS_URL,
xmlString);
HisBaseBeanResult hisResult = new HisBaseBeanResult<>();
//异常统一处理
hisResult.builderBeanData(xmlReturnStr, ClinicDetailHisResult.class);
3.3 禁止未知异常,推荐返回his接口的业务his异常信息
String xmlString = objToxml(xmlBody);
log.warn(“xmlString ::{}”, xmlString);
String xmlReturnStr = centerHospitalHttpService.postRequestHis(ShanghaiSongCenterHospitalHttpService.HIS_URL,
xmlString);
HisBaseBeanResult hisResult = new HisBaseBeanResult<>();
hisResult.builderBeanData(xmlReturnStr, ClinicDetailHisResult.class);
ClinicDetailHisResult items = hisResult.getBeanData();
4 · 熔断错误处理策略: 研究异常情况下的最佳实践,包括重试策略、超时处理和错误状态码处理。
4.1 什么是接口熔断.(容错)
系统A调⽤B,⽽B调⽤C,这时如果C出现故障,则此时调⽤B的⼤量线程资源阻塞,慢慢的B的线程数量持续增加直到CPU耗尽到100%,整体微服务不可⽤,这时就需要对不可⽤的服务进⾏隔离.
系统所依赖的服务的稳定性对系统的影响⾮常⼤,⽽且还有很多不确定因素引起雪崩,如⽹络连接中断,服务宕机等
https://blog.csdn.net/ywtech/article/details/132626613
4.2如何熔断的原理
4.3熔断保护-社区生态有哪些方案
Sentinel 与 Hystrix、Resilience4j
4.4 熔断保护系统实践.( 熔断 降级 并发限流,重试)
5 性能优化: 提供性能优化技巧,减少请求延迟和提高吞吐量。
5.1 禁止使用 new RestTemplate()
5.2 推荐使用连接连接池 forest http 客户端
6 · 第三方服务认证: 学习如何进行认证和授权,以确保只有授权的请求可以访问第三方服务。(服务接口提供方)
7 · 安全性: 讲解如何保护 HTTP 请求和响应,防止潜在的安全漏洞。(服务接口提供方)
8 · 日志和监控: 了解如何记录请求和响应信息,以及如何设置监控来追踪系统的健康状况
Skywalking