How does Java call the interface API and return data (two methods)

How does Java call the interface API and return data (two methods)
Java processes the json data returned after requesting the interface - directly process the json string
Processing ideas:

Receive the returned data into a String object (sometimes you need to choose to accept it yourself)
and then convert the string into a JSONObject object.
Use the get() method to get the corresponding value
Note: Based on the special situation of the third point, if you return If there are multiple sets of data in the json, it needs to be converted into data (use JSONObject.

call interface, request data

1. post method

import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.IOException;
import java.net.URI;
import java.util.*;   
public static JSONObject postResponse(String url,JSONObject jsonParam){
    
    

       HttpClient client = HttpClients.createDefault();
//     要调用的接口方法
        HttpPost post = new HttpPost(url);
        JSONObject jsonObject = null;
        try {
    
    
        StringEntity s = new StringEntity(jsonParam.toString(),"UTF-8");
        s.setContentType("application/json");
        post.setEntity(s);
        post.setHeader("Content-Type","application/json");

            HttpResponse res = client.execute(post);
        if(res.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
    
    
            /*返回json格式*/
            jsonObject = JSONObject.parseObject(EntityUtils.toString(res.getEntity()));
        }
        } catch (IOException e) {
    
    
            System.out.println("接口调用出错!");
            e.printStackTrace();
            throw new RuntimeException(e);

        }
        return jsonObject;
    }

2. get method

import com.alibaba.fastjson.JSONObject;
import edu.zhku.fire_ant_project.config.WxConstant;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

public class HttpCallOtherInterfaceUtils {
    
    
    public static void main(String args[]) {
    
    
        HttpClient client = HttpClients.createDefault();
        // 要调用的接口方法
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+ WxConstant.appid +"&secret="+WxConstant.secret;
        HttpGet httpGet=new HttpGet(url);
        JSONObject jsonObject = null;
        try {
    
    
            HttpResponse res = client.execute(httpGet);
            if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
    
    
                // 返回json格式:
                jsonObject = JSONObject.parseObject(EntityUtils.toString(res.getEntity()));
                System.out.println(jsonObject);
            }
        } catch (Exception e) {
    
    
            System.out.println("服务间接口调用出错!");
            e.printStackTrace();
        }
    }
}

Process the returned json data

Reference Code:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

public class JsonStr {
    
    
    public static void main(String[] args) {
    
    
        //1.比如从前端接收的是这个样子的json字符串,但是我们是不能直接获取到经度 纬度所对应的值的,所以必须要对这个字符串进行解析,
        String str = "{\"code\":\"0\"," +
                            "\"error\":null," +
                            "\"msg\":\"success\"," +
                            "\"detail\":[\n" +
                             "    {\"phoneNum\":\"013012401091\",\n" +
                             "    \"lat\":34.431864,\n" +
                             "    \"lon\":109.25992,\n" +
                             "    \"height\":360,\n" +
                             "    \"speed\":33,\n" +
                             "    \"direction\":10,\n" +
                             "    \"gpsTime\":1615420800000,\n" +
                             "    \"gpsDateTime\":1615420800000,\n" +
                             "    \"mileage\":253830800,\n" +
                             "    \"alarms\":[24],\n" +
                             "    \"status\":[1,2,21],\n" +
                             "    \"property\":{}\n" +
                             "    }" +
                            " ]" +
                         " }";
        //先转换成JSONObject类型
        JSONObject jsonObj = JSON.parseObject(str);
        //通过JSONObject中的getString("key")方法,得到对应的值  {"code":"0","error":null,"msg":"success"}这种类型
        System.out.println("code:"+jsonObj.getString("code"));
        
        //2.字符串中含有数组的,比如像detail中的数据
        JSONArray jsonInfo = JSONObject.parseArray(jsonObj.getString("detail"));//将jsonObj解析成json数组
        for (int i = 0; i < jsonInfo.size(); i++) {
    
    //遍历detail信息
            JSONObject jsonDetailInfo = jsonInfo.getJSONObject(i);//根据下标以此拿数据,每一个数据又是一个JSONObject对象,所以用JSONObject接收
            String lat = jsonDetailInfo.getString("lat");
            String lon = jsonDetailInfo.getString("lon");
            String gpsDateTime = jsonDetailInfo.getString("gpsDateTime");
            //然后进行其他处理
            System.out.println("lat:"+lat+";lon:"+lon+";gpsDateTime:"+gpsDateTime);
        }
    }
}
例子: 请求数据要求分页 所以采取循环
 返回值说明:
  {
    
    
  "code": "200",
  "data": {
    
    
      "endRow": 2,
      "firstPage": 1,
      "hasNextPage": true,
      "hasPreviousPage": false,
      "isFirstPage": true,
      "isLastPage": false,
      "lastPage": 8,
      "list": [
          {
    
    
              "packingUnit": "盒", //包装单位
              "factory": "河北百善药业有限公司", //生产企业
              "salePrice": 11.7, //销售价
              "fullSalePrice": 11.5, //整件销售价
              "inventoryQuantity": 172, //库存
              "pictureUrl": "/upload/product/pic/2021/11/20101013-2-424389.png", //商品图片
              "policyTitle": "10盒起积0.5分/盒,20盒起积1分/盒",//商品政策:比如10盒积1分意思是10盒每盒减1元,10s5:表示买10盒送5盒
              "policyStartDate": "2022-11-01",//商品政策开始日期
              "policyEndDate": "2022-11-30",//商品政策结束日期
              "validDateStr": "2024-09-07",//最近批号有效期
              "batchNumber": "2207012",    //最近批号
              "registerFileUrl": "https://img.zc511.com/data/uploadRegisterFileOne.shtml?productCode=20101013-2",    //    注册资料下载URL
              "authorizeNumber": "国药准字Z20063720",
              "rowId": 1,
              "barCode": "6909557000263",//商品条码
              "parseProductName": "接骨片",//商品名称
              "productCode": "20101013-2",//商品条码
              "countryCode": "ZG01AAJ0247010302685",//国家码 医保码
              "packingAmount": 200,//大包装
              "parseProductSpec": "60片",//规格
              "retailPrice": 196,//市场零售价
              "minPackingNumber": 0//最小购买包装
          }
      ],
      "navigatePages": 8,
      "navigatepageNums": [
          1,
          2,
          3,
          4,
          5,
          6,
          7,
          8
      ],
      "nextPage": 2,
      "pageNum": 1,
      "pageSize": 2,
      "pages": 5627,
      "prePage": 0,
      "size": 2,
      "startRow": 1,
      "total": 11254
  },
  "msg": "",
  "success": true
}

HttpCilentutil

request interface data

import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.IOException;
import java.net.URI;
import java.util.*;
public class HttpClientUtil 
{
    
    
    public static JSONObject postResponse(String url,JSONObject jsonParam){
    
    
       HttpClient client = HttpClients.createDefault();
//     要调用的接口方法
        HttpPost post = new HttpPost(url);
        JSONObject jsonObject = null;
        try {
    
    
        StringEntity s = new StringEntity(jsonParam.toString(),"UTF-8");
        s.setContentType("application/json");
        post.setEntity(s);
        post.setHeader("Content-Type","application/json");
            HttpResponse res = client.execute(post);
        if(res.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
    
    
            /*返回json格式*/
            jsonObject = JSONObject.parseObject(EntityUtils.toString(res.getEntity()));
        }
        } catch (IOException e) {
    
    
            System.out.println("接口调用出错!");
            e.printStackTrace();
            throw new RuntimeException(e);

        }
        return jsonObject;
    }
//请求一次接口 先获取分页后的总数据
    public static Map<String,Object> getOneRequest(){
    
    
        String url ="http://gateway.zc511.com/getway/v2/product_list_get.shtml";
        JSONObject params = new JSONObject();
        params.put("timestamp","1650865510301");
        params.put("appKey","ZBKJ");
        params.put("secret","123");
        params.put("sign","1186D2A92FC3AB4E5671EBF58B9896A6");
        params.put("buyerCode","3330020298-1");
        params.put("pageNum","1");
        params.put("pageSize","2000");
        JSONObject jsonObject = HttpClientUtil.postResponse(url,params);
//        System.out.println("第一次请求:"+jsonObject);
        Boolean success = (Boolean) jsonObject.get("success");
        JSONObject data = (JSONObject) jsonObject.get("data");
        Boolean isLastPage =(Boolean) data.get("isLastPage");//是否为最后一页
        Integer pages =(Integer) data.get("pages");//一共多少页  按每页取多少条进行计算的(共可以获取多少次)
        Integer total = (Integer)data.get("total");// 总数据
        Integer pageNum = (Integer) data.get("pageNum");//当前页数
        Map<String, Object> response = new HashMap<>();
        response.put("pages",pages);
        response.put("isLastPage",isLastPage);
        response.put("total",total);
        response.put("data",data);
        response.put("pageNum",pageNum);
        return response;
    }
}

controller

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.sun.org.apache.xpath.internal.operations.Bool;
import com.zc.zcdj.domain.LhkjHzhnData;
import com.zc.zcdj.domain.ResponseEntity;
import com.zc.zcdj.mapper.LhkjHzhnDataMapper;
import com.zc.zcdj.service.ILhkjHzhnDataService;
import com.zc.zcdj.utils.HttpClientUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collector;
import java.util.stream.Collectors;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author guoshanshan
 * @since 2023-03-21
 */

@Component
public class LhkjHzhnDataController {
    
    

    @Autowired
    private ILhkjHzhnDataService lhkjHzhnDataService;
    @Autowired
    private LhkjHzhnDataMapper lhkjHzhnDataMapper;
    @Autowired
    private JdbcOperations jdbcTemplate;

    private static final Logger log = LoggerFactory.getLogger("获取珍诚全量数据");

    /**
     * 获取珍诚全量数据
     */
//    @Scheduled(cron = "0 14 13 ? * *")
//    public void getAll(){
    
    
//        System.out.println("111111111111111111111111111111");
//        lhkjHzhnDataService.getProductList();
//    }
    @Scheduled(cron = "0 49 19 ? * *")
    public void getProductList() {
    
    
        Map<String, String> AllTybm = lhkjHzhnDataMapper.getTybm().stream().filter(s -> s.get统一商品编码() != null).filter(s -> s.get规则() != null).collect(Collectors.toMap(s -> s.get规则(), s -> s.get统一商品编码()));

        log.info("第一次请求珍诚接口...");
//        请求第一次获取数据总条数 以及分页后循环次数
        Map<String, Object> response = HttpClientUtil.getOneRequest();
        Integer pages = (Integer) response.get("pages");//
        Integer total = (Integer) response.get("total");
        Integer pageNum = (Integer) response.get("pageNum");//当前页数
//        Boolean isLastPage = (Boolean) response.get("isLastPage");
//        JSONObject data = (JSONObject) response.get("data");
        log.info("获取珍诚商品列表全量数据共有" + total + "条,共有" + pages + "页数据");
        log.info("珍诚全量开始执行...");
        for (int i = 1; i <= pages; i++) {
    
    
            String url = "http://gateway.zc511.com/getway/v2/product_list_get.shtml";
            JSONObject params = new JSONObject();
            params.put("timestamp", "1650865510301");
            params.put("appKey", "ZBKJ");
            params.put("secret", "123");
            params.put("sign", "1186D2A92FC3AB4E5671EBF58B9896A6");
            params.put("buyerCode", "3330020298-1");
            params.put("pageNum", pageNum);
            params.put("pageSize", "2000");
            JSONObject jsonObject = HttpClientUtil.postResponse(url, params);
            log.info("开始获取第" + i + "页数据");
            JSONObject data = (JSONObject) jsonObject.get("data");
            Boolean isLastPage = (Boolean) data.get("isLastPage");//是否为最后一页
            //        获取list中的商品数据 JSONArray
            JSONArray list = data.getJSONArray("list");
            for (int j = 0; j < list.size(); j++) {
    
    
                LhkjHzhnData hzhnData = new LhkjHzhnData();
                String packingUnit = list.getJSONObject(j).getString("packingUnit");//包装单位
                Double salePrice = list.getJSONObject(j).getDouble("salePrice");//销售价
                String factory = list.getJSONObject(j).getString("factory");//生产企业
                Integer inventoryQuantity = list.getJSONObject(j).getInteger("inventoryQuantity");//库存
                Date validDateStr = list.getJSONObject(j).getDate("validDateStr");//有效期
                String authorizeNumber = list.getJSONObject(j).getString("authorizeNumber");//国药准字
                String barCode = list.getJSONObject(j).getString("barCode");//商品条码
                String parseProductName = list.getJSONObject(j).getString("parseProductName");//商品名称
                String packingAmount = list.getJSONObject(j).getString("packingAmount");//大包装
                String parseProductSpec = list.getJSONObject(j).getString("parseProductSpec");//规格
                String minPackingNumber = list.getJSONObject(j).getString("minPackingNumber");//最小购买包装
                if (authorizeNumber != null && parseProductSpec != null) {
    
    
                    String authorizeNumberNew = authorizeNumber.replaceAll("[^a-zA-Z/0-9]", "");
                    String parseProductSpecNew = parseProductSpec.replaceAll("[^0-9]", "");
                    String tj = authorizeNumberNew.toUpperCase() + parseProductSpecNew;
                    hzhnData.set统一编码("0");
                    String tybm = (String) AllTybm.get(tj);
                    if (tybm != null && tybm != "0") {
    
    
                        hzhnData.set统一编码(tybm);
                    }
                }
                hzhnData.set药品名称(parseProductName);
                hzhnData.set规格(parseProductSpec);
                hzhnData.set产地(factory);
                hzhnData.set单位(packingUnit);
                hzhnData.set大包装(packingAmount);
                hzhnData.set批准文号(authorizeNumber);
                hzhnData.set库存数量(inventoryQuantity);
                hzhnData.set价格(salePrice);
                hzhnData.setTxm(barCode);
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                if (validDateStr != null) {
    
    
                    hzhnData.set有效期(sdf.format(validDateStr));
                } else {
    
    
                    hzhnData.set有效期(null);
                }
                hzhnData.set库区("珍诚");
                hzhnData.set供应商("珍诚");
                hzhnData.set中包装(minPackingNumber);
                Date date = new Date();
                SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                hzhnData.setStoragedate(sdf1.format(date));
//            hzhnData.set备注();
//            hzhnData.setHyid();
                System.out.println("第" + (j + 1) + "条" + hzhnData);
            lhkjHzhnDataMapper.insert(hzhnData);
            }
            pageNum += 1;

        }

     log.info("获取珍诚全量数据运行完毕...");
    }

}

If the frequency of use is more, it is recommended to use the form of object processing. Use
Ali's fastjson, please note the previous reference
insert image description here
about the processing of json strings in Java, which can be obtained directly; but when the frequency of use is more, it is not very convenient. Since it is object-oriented programming, convert the data to json into objects!

Processing ideas:

An object needs to be constructed based on the received json object.
Encapsulate all required data into objects!
Then use Ali (fastjson)/google (Gson)'s json-to-object method to
directly convert to an object, and then directly manipulate the object.
Suppose the following is the json we received, and what we need is the data in the detail;

You need to build entity dataItems and entities containing dataItems

Then convert directly:
insert image description here
sample code: entities (assuming they are all String types)

dataItems

@Data
public class DataIteams {
    
    
        private  String  phoneNum;
        private  String  aramDateTime;
        private  String  aramType;
        private  String  lon;
        private  String  lat;
        private  String  devNo;
        private  String  descripton;
        private  String  since;
        private  String  amc;
        }

entity of detail

import java.util.List;
@Data
public class Detail {
    
    
    private String pageCount;
    private String totalCount;
    private String pageIndex;
    private String pageSize;
    /**
     *这个实体就是DataItems里面的数据体
     */
    private List<DataItems> dataItems;
    }

When the entity is constructed, the method conversion can be used!

Sample code:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import java.util.HashMap;

public class Main {
    
    

    public static void main(String[] args) {
    
    
       String str = "{\n" +
               "    \"code\": \"0\",\n" +
               "    \"error\": \"\",\n" +
               "    \"msg\": \"success\",\n" +
               "    \"detail\": {\n" +
               "        \"pageCount\": 1,\n" +
               "        \"totalCount\": 9,\n" +
               "        \"pageIndex\": 1,\n" +
               "        \"pageSize\": 1000,\n" +
               "        \"dataItems\": [\n" +
               "            {\n" +
               "                \"phoneNum\": \"027044039989\",\n" +
               "                \"aramDateTime\": 1616990185000,\n" +
               "                \"aramType\": 1,\n" +
               "                \"lon\": 114.224438,\n" +
               "                \"lat\": 30.55706,\n" +
               "                \"devNo\": \"\",\n" +
               "                \"descripton\": \"\",\n" +
               "                \"since\": \"\",\n" +
               "                \"amc\": \"\"\n" +
               "            },\n" +
               "            {\n" +
               "                \"phoneNum\": \"027044039989\",\n" +
               "                \"aramDateTime\": 1616990186000,\n" +
               "                \"aramType\": 1,\n" +
               "                \"lon\": 114.224457,\n" +
               "                \"lat\": 30.557187,\n" +
               "                \"devNo\": \"\",\n" +
               "                \"descripton\": \"\",\n" +
               "                \"since\": \"\",\n" +
               "                \"amc\": \"\"\n" +
               "            },\n" +
               "            {\n" +
               "                \"phoneNum\": \"027044039989\",\n" +
               "                \"aramDateTime\": 1616990190000,\n" +
               "                \"aramType\": 1,\n" +
               "                \"lon\": 114.224484,\n" +
               "                \"lat\": 30.557565,\n" +
               "                \"devNo\": \"\",\n" +
               "                \"descripton\": \"\",\n" +
               "                \"since\": \"\",\n" +
               "                \"amc\": \"\"\n" +
               "            },\n" +
               "            {\n" +
               "                \"phoneNum\": \"027044039989\",\n" +
               "                \"aramDateTime\": 1616990195000,\n" +
               "                \"aramType\": 1,\n" +
               "                \"lon\": 114.224434,\n" +
               "                \"lat\": 30.557971,\n" +
               "                \"devNo\": \"\",\n" +
               "                \"descripton\": \"\",\n" +
               "                \"since\": \"\",\n" +
               "                \"amc\": \"\"\n" +
               "            },\n" +
               "            {\n" +
               "                \"phoneNum\": \"027044039989\",\n" +
               "                \"aramDateTime\": 1616990196000,\n" +
               "                \"aramType\": 1,\n" +
               "                \"lon\": 114.224388,\n" +
               "                \"lat\": 30.558073,\n" +
               "                \"devNo\": \"\",\n" +
               "                \"descripton\": \"\",\n" +
               "                \"since\": \"\",\n" +
               "                \"amc\": \"\"\n" +
               "            },\n" +
               "            {\n" +
               "                \"phoneNum\": \"027044039989\",\n" +
               "                \"aramDateTime\": 1616990200000,\n" +
               "                \"aramType\": 1,\n" +
               "                \"lon\": 114.224297,\n" +
               "                \"lat\": 30.558436,\n" +
               "                \"devNo\": \"\",\n" +
               "                \"descripton\": \"\",\n" +
               "                \"since\": \"\",\n" +
               "                \"amc\": \"\"\n" +
               "            },\n" +
               "            {\n" +
               "                \"phoneNum\": \"027044039989\",\n" +
               "                \"aramDateTime\": 1614736039000,\n" +
               "                \"aramType\": 27,\n" +
               "                \"lon\": 114.204813,\n" +
               "                \"lat\": 30.553443,\n" +
               "                \"devNo\": \"\",\n" +
               "                \"descripton\": \"\",\n" +
               "                \"since\": \"\",\n" +
               "                \"amc\": \"\"\n" +
               "            },\n" +
               "            {\n" +
               "                \"phoneNum\": \"027044039989\",\n" +
               "                \"aramDateTime\": 1615342530000,\n" +
               "                \"aramType\": 27,\n" +
               "                \"lon\": 114.216378,\n" +
               "                \"lat\": 30.561051,\n" +
               "                \"devNo\": \"\",\n" +
               "                \"descripton\": \"\",\n" +
               "                \"since\": \"\",\n" +
               "                \"amc\": \"\"\n" +
               "            },\n" +
               "            {\n" +
               "                \"phoneNum\": \"027044039989\",\n" +
               "                \"aramDateTime\": 1616990178000,\n" +
               "                \"aramType\": 27,\n" +
               "                \"lon\": 114.222609,\n" +
               "                \"lat\": 30.554516,\n" +
               "                \"devNo\": \"\",\n" +
               "                \"descripton\": \"\",\n" +
               "                \"since\": \"\",\n" +
               "                \"amc\": \"\"\n" +
               "            }\n" +
               "        ]\n" +
               "    },\n" +
               "}";


        //先转换成JSONObject类型
        String json = JSON.parseObject(str).getString("detail");
        Detail detail = JSONObject.parseObject(json, Detail.class);
        System.out.println(detail);
    }

}

insert image description here
The obtained object can directly use the get and set methods to obtain data

method:

//Javabean object is converted into a JSON string of type String
JSONObject.toJSONString(Javabean object)

//String type JSON string is converted into Javabean object
JSONObject.toJavaObject(JSON string, Javabean.class)

//Json string is converted into JSONObject object
JSONObject.parseObject(JSON string)

//JSON string is converted into a Javabean object
JSONObject.parseObject(JSON string, Javabean.class)

Guess you like

Origin blog.csdn.net/weixin_45817985/article/details/132459637