xingjinshuang
2024-12-17 ea7427fe15da6155519a7483914013081ac46234
@xingjs@20241217@添加实体库查询系列接口,返回实体库相关属性信息
已添加9个文件
已修改3个文件
1376 ■■■■■ 文件已修改
pom.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/se/simu/constant/CacheConstants.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/se/simu/constant/RedisCache.java 243 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/se/simu/controller/ProjectRelatedController.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/se/simu/domain/EntityDataBase.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/se/simu/domain/EntityTypeInfo.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/se/simu/domain/LoginParams.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/se/simu/service/Impl/ProjectRelatedServiceImpl.java 337 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/se/simu/service/ProjectRelatedService.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/se/simu/utils/CustomWebClient.java 500 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-prod.yml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-zyy.yml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml
@@ -166,6 +166,18 @@
            <version>1.33</version>
            <scope>compile</scope>
        </dependency>
        <!--webclient请求-->
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-webflux -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
            <version>3.3.1</version>
        </dependency>
    </dependencies>
    <build>
src/main/java/com/se/simu/constant/CacheConstants.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package com.se.simu.constant;
/**
 * ç¼“存的key å¸¸é‡
 *
 * @author ruoyi
 */
public class CacheConstants {
    /**
     * ç™»å½•用户 redis key
     */
    public static final String LOGIN_TOKEN_KEY = "login_tokens:";
    /**
     * éªŒè¯ç  redis key
     */
    public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
    /**
     * å‚数管理 cache key
     */
    public static final String SYS_CONFIG_KEY = "sys_config:";
    /**
     * å­—典管理 cache key
     */
    public static final String SYS_DICT_KEY = "sys_dict:";
    /**
     * é˜²é‡æäº¤ redis key
     */
    public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
    /**
     * é™æµ redis key
     */
    public static final String RATE_LIMIT_KEY = "rate_limit:";
    /**
     * ç™»å½•账户密码错误次数 redis key
     */
    public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
    /**
     * ç”¨æˆ·è‡ªå®šä¹‰ç¼“å­˜ redis key
     */
    public static final String USER_CACHE_KEY = "user_cache_key:";
}
src/main/java/com/se/simu/constant/RedisCache.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,243 @@
package com.se.simu.constant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
 * spring redis å·¥å…·ç±»
 *
 * @author ruoyi
 **/
@SuppressWarnings(value = {"unchecked", "rawtypes"})
@Component
public class RedisCache {
    @Autowired
    public RedisTemplate redisTemplate;
    /**
     * ç¼“存基本的对象,Integer、String、实体类等
     *
     * @param key   ç¼“存的键值
     * @param value ç¼“存的值
     */
    public <T> void setCacheObject(final String key, final T value) {
        redisTemplate.opsForValue().set(key, value);
    }
    /**
     * ç¼“存基本的对象,Integer、String、实体类等
     *
     * @param key      ç¼“存的键值
     * @param value    ç¼“存的值
     * @param timeout  æ—¶é—´
     * @param timeUnit æ—¶é—´é¢—粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }
    /**
     * è®¾ç½®æœ‰æ•ˆæ—¶é—´
     *
     * @param key     Redis键
     * @param timeout è¶…æ—¶æ—¶é—´
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout) {
        return expire(key, timeout, TimeUnit.SECONDS);
    }
    /**
     * è®¾ç½®æœ‰æ•ˆæ—¶é—´
     *
     * @param key     Redis键
     * @param timeout è¶…æ—¶æ—¶é—´
     * @param unit    æ—¶é—´å•位
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }
    /**
     * èŽ·å–æœ‰æ•ˆæ—¶é—´
     *
     * @param key Redis键
     * @return æœ‰æ•ˆæ—¶é—´
     */
    public long getExpire(final String key) {
        return redisTemplate.getExpire(key);
    }
    /**
     * åˆ¤æ–­ key是否存在
     *
     * @param key é”®
     * @return true å­˜åœ¨ false不存在
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }
    /**
     * èŽ·å¾—ç¼“å­˜çš„åŸºæœ¬å¯¹è±¡ã€‚
     *
     * @param key ç¼“存键值
     * @return ç¼“存键值对应的数据
     */
    public <T> T getCacheObject(final String key) {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }
    /**
     * åˆ é™¤å•个对象
     *
     * @param key
     */
    public boolean deleteObject(final String key) {
        return redisTemplate.delete(key);
    }
    /**
     * åˆ é™¤é›†åˆå¯¹è±¡
     *
     * @param collection å¤šä¸ªå¯¹è±¡
     * @return
     */
    public boolean deleteObject(final Collection collection) {
        return redisTemplate.delete(collection) > 0;
    }
    /**
     * ç¼“å­˜List数据
     *
     * @param key      ç¼“存的键值
     * @param dataList å¾…缓存的List数据
     * @return ç¼“存的对象
     */
    public <T> long setCacheList(final String key, final List<T> dataList) {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }
    /**
     * èŽ·å¾—ç¼“å­˜çš„list对象
     *
     * @param key ç¼“存的键值
     * @return ç¼“存键值对应的数据
     */
    public <T> List<T> getCacheList(final String key) {
        return redisTemplate.opsForList().range(key, 0, -1);
    }
    /**
     * ç¼“å­˜Set
     *
     * @param key     ç¼“存键值
     * @param dataSet ç¼“存的数据
     * @return ç¼“存数据的对象
     */
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) {
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        Iterator<T> it = dataSet.iterator();
        while (it.hasNext()) {
            setOperation.add(it.next());
        }
        return setOperation;
    }
    /**
     * èŽ·å¾—ç¼“å­˜çš„set
     *
     * @param key
     * @return
     */
    public <T> Set<T> getCacheSet(final String key) {
        return redisTemplate.opsForSet().members(key);
    }
    /**
     * ç¼“å­˜Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }
    /**
     * èŽ·å¾—ç¼“å­˜çš„Map
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getCacheMap(final String key) {
        return redisTemplate.opsForHash().entries(key);
    }
    /**
     * å¾€Hash中存入数据
     *
     * @param key   Redis键
     * @param hKey  Hash键
     * @param value å€¼
     */
    public <T> void setCacheMapValue(final String key, final String hKey, final T value) {
        redisTemplate.opsForHash().put(key, hKey, value);
    }
    /**
     * èŽ·å–Hash中的数据
     *
     * @param key  Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> T getCacheMapValue(final String key, final String hKey) {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }
    /**
     * èŽ·å–å¤šä¸ªHash中的数据
     *
     * @param key   Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }
    /**
     * åˆ é™¤Hash中的某条数据
     *
     * @param key  Redis键
     * @param hKey Hash键
     * @return æ˜¯å¦æˆåŠŸ
     */
    public boolean deleteCacheMapValue(final String key, final String hKey) {
        return redisTemplate.opsForHash().delete(key, hKey) > 0;
    }
    /**
     * èŽ·å¾—ç¼“å­˜çš„åŸºæœ¬å¯¹è±¡åˆ—è¡¨
     *
     * @param pattern å­—符串前缀
     * @return å¯¹è±¡åˆ—表
     */
    public Collection<String> keys(final String pattern) {
        return redisTemplate.keys(pattern);
    }
}
src/main/java/com/se/simu/controller/ProjectRelatedController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,89 @@
package com.se.simu.controller;
import com.se.simu.domain.EntityTypeInfo;
import com.se.simu.domain.LoginParams;
import com.se.simu.domain.vo.R;
import com.se.simu.service.ProjectRelatedService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@Api(tags = "实体库相关接口")
@CrossOrigin(origins = "*")
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/v1")
public class ProjectRelatedController extends BaseController {
    @Resource
    private ProjectRelatedService projectRelatedService;
    /**
     * èŽ·å–å…¬é’¥
     *
     * @Cacheable æ·»åŠ ç¼“å­˜ï¼Œç›¸åŒæ¡ä»¶çš„æŸ¥è¯¢ä¸å†æŸ¥è¯¢æ•°æ®åº“ï¼Œè€Œæ˜¯ä»Žç¼“å­˜ä¸­æŸ¥è¯¢ï¼›
     * @CachePut æ¯æ¬¡éƒ½ä¼šè®¿é—®æ•°æ®åº“,并更新缓存;
     * @CacheEvict æ¸…除缓存
     * <p>
     * æ³¨è§£ä¸­å‚数说明:
     * cacheNames/value:指定缓存组件的名字;
     * key:缓存数据使用的key;可以用它来指定。默认是使用方法参数的值  1-方法全参 2-方法参数的某几个 3-key(SpEL)
     * keyGenerator:key的生成器;可以自己指定key的生成器的组件id
     * cacheManager:指定缓存管理器;或者cacheResolver指定获取解析器
     * condition:指定符合条件的情况下才缓存;
     * unless:否定缓存;当unless指定的条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断
     * sync:是否使用异步模式
     */
    @ApiOperation("0-获取公钥")
//    @Cacheable(cacheNames="api",value="cachespace=30", key = "#root.methodName")
    @GetMapping("/get-public-key")
    public Object getPublicKey() {
        return projectRelatedService.getPublicKey();
    }
    /**
     * ç™»å½•实体库
     */
    @ApiOperation("1-登录实体库")
//    @Cacheable(cacheNames = "api", key = "#loginParams")
    @PostMapping("/login-entity")
    public R<Object> loginEntity(@RequestBody LoginParams loginParams) {
        return success(projectRelatedService.loginEntity(loginParams));
    }
    /**
     * èŽ·å–è®¿é—®å®žä½“åº“çš„token
     */
    @ApiOperation("1-获取访问实体库的token")
    @GetMapping("/entity-public-key")
    public R<Object> getEntityPublicKey() {
        return success(projectRelatedService.getEntityPublicKey());
    }
    /**
     * èŽ·å–è®¿é—®å®žä½“åº“æ•°æ®åº“åˆ—è¡¨
     */
    @ApiOperation("2-获取访问实体库数据库列表")
    @GetMapping("/db-list")
    public R<Object> getDbLits() {
        return success(projectRelatedService.getDbLits());
    }
    /**
     * æŸ¥è¯¢å®žä½“库不同类型的信息
     */
    @ApiOperation("3-查询实体库不同类型的信息")
    @GetMapping("/entity-type-info")
    public Object getEntityTypeInfo(EntityTypeInfo entityTypeInfo) {
        return projectRelatedService.getEntityTypeInfo(entityTypeInfo);
    }
}
src/main/java/com/se/simu/domain/EntityDataBase.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
package com.se.simu.domain;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
 * å®žä½“数据库
 *
 * @author xingjinshuang
 * @date 2024/07/04
 */
@Data
@ApiModel(value = "EntityDataBase", description = "实体数据库相关内容")
public class EntityDataBase implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * ç”¨æˆ·å
     */
    @ApiModelProperty("用户名")
    private String userName;
    /**
     * ç”¨æˆ·å¯†ç 
     */
    @ApiModelProperty("用户密码")
    private String userPassword;
    /**
     * æ•°æ®åº“名
     */
    @ApiModelProperty("数据库名")
    private String databaseName;
}
src/main/java/com/se/simu/domain/EntityTypeInfo.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package com.se.simu.domain;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel(value = "EntityTypeInfo", description = "实体库不同类型的信息")
public class EntityTypeInfo implements Serializable {
    private static final long serialVersionUID = 1L;
    private String token;
    private Integer start;
    private boolean containCount;
    private Integer count;
    private String dbid;
    private String layerid;
    private String like;
    private String querytype;
}
src/main/java/com/se/simu/domain/LoginParams.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
package com.se.simu.domain;
import lombok.Data;
import java.io.Serializable;
@Data
public class LoginParams implements Serializable {
    private static final long serialVersionUID = 1L;
    // ç”¨æˆ·å
    private String userid;
    // å¯†ç 
    private String password;
}
src/main/java/com/se/simu/service/Impl/ProjectRelatedServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,337 @@
package com.se.simu.service.Impl;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.AsymmetricAlgorithm;
import cn.hutool.crypto.asymmetric.AsymmetricCrypto;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import com.alibaba.fastjson.JSONObject;
import com.se.simu.constant.CacheConstants;
import com.se.simu.constant.RedisCache;
import com.se.simu.domain.EntityTypeInfo;
import com.se.simu.domain.LoginParams;
import com.se.simu.service.ProjectRelatedService;
import com.se.simu.utils.CustomWebClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@Slf4j
@Service
public class ProjectRelatedServiceImpl implements ProjectRelatedService {
    @Resource
    private RedisCache redisCache;
    @Resource
    public RedisTemplate redisTemplate;
    // å…¬é’¥åœ°å€
    @Value(value = "${app-server.publicKeyUrl}")
    private String publicKeyUrl;
    // devops ç™»å½•地址
    @Value(value = "${app-server.loginUrl}")
    private String loginUrl;
    @Value(value = "${app-server.getDbUrl}")
    private String getDbUrl;
    // æŸ¥è¯¢åœ°å€
    @Value(value = "${app-server.queryUrl}")
    private String queryUrl;
    /**
     * èŽ·å–å…¬é’¥
     *
     * @return {@link Object}
     */
    @Override
    public Object getPublicKey() {
        HashMap<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        CompletableFuture<String> postResponse = CustomWebClient.postAsFuture(publicKeyUrl, "", headers, String.class);
        // å¼‚步处理响应
        postResponse.thenAccept(response -> {
            log.info("response: {}", response);
            if (response.contains("code")) {
                try {
                    JSONObject postResponseJson = JSONObject.parseObject(response);
                    int statusCode = postResponseJson.getIntValue("code");
                    log.info("statusCode = " + statusCode);
                    String data = postResponseJson.getString("data");
                    // ç¼“å­˜data,并设置1小时有效期
                    redisCache.deleteObject(CacheConstants.USER_CACHE_KEY + "rsa_data_set");
                    redisCache.setCacheObject(CacheConstants.USER_CACHE_KEY + "rsa_data_set", data, 1, TimeUnit.HOURS);
                    log.info("publicKey = " + data);
                } catch (Exception e) {
                    log.info("Failed to parse JSON: " + e.getMessage());
                }
            } else {
                log.info("No 'code' field in the response: " + response);
            }
        });
        return JSONObject.parseObject(postResponse.join());
    }
    /**
     * ç™»å½•实体
     *
     * @param loginParams ç™»å½•参数
     * @return {@link String}
     */
    @Override
    public Object loginEntity(LoginParams loginParams) {
        // åˆ¤æ–­redis中缓存是否存在(过期)
        Boolean isExists = redisCache.hasKey(CacheConstants.USER_CACHE_KEY + "entity_db_response");
        if (isExists) {
            return JSONObject.parseObject(redisCache.getCacheObject(CacheConstants.USER_CACHE_KEY + "entity_db_response"));
        }
        // è®¾ç½®è¯·æ±‚体
        // èŽ·å–ç§é’¥å’Œå…¬é’¥ï¼Œé•¿åº¦å¿…é¡»æ˜¯16、24或32
        String publicKey = redisCache.getCacheObject(CacheConstants.USER_CACHE_KEY + "rsa_data_set");
        // å‡è®¾ä»ŽRedis中获取到用户名,判断
        if (!StringUtils.isNotBlank(publicKey)) {
            // ç¼“存中没有用户名,则进行登录
            getPublicKey();
            log.info("调用了登录获取用户名方法 $= ");
        }
        // é˜²æ­¢ä¸»çº¿ç¨‹æå‰ç»“束
        try {
            // ç­‰å¾…异步请求完成
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        publicKey = redisCache.getCacheObject(CacheConstants.USER_CACHE_KEY + "rsa_data_set");
        log.info("publicKey = " + publicKey);
        // å…¬é’¥åР坆
        AsymmetricCrypto rsa = SecureUtil.rsa(null, publicKey);
        String encrypt = rsa.encryptBase64(loginParams.getPassword(), KeyType.PublicKey);
        // è®¾ç½®åŠ å¯†åŽçš„å¯†ç 
        loginParams.setPassword(encrypt);
        // å°†loginParams实体转成JSON字符串
//        String json = JSON.toJSONString(loginParams);
//        log.info("json = " + json);
        // å‘送登录请求
        Mono<String> postResponse = CustomWebClient.postAsMono(loginUrl, loginParams, String.class);
        // ç”¨äºŽä¿å­˜å“åº”数据
        AtomicReference<String> responseData = new AtomicReference<>();
        postResponse.subscribe(response -> {
            // å°†å“åº”数据保存到变量中
            responseData.set(response);
            String code = JSONObject.parseObject(response).getString("code");
            // å¦‚æžœcode是200则登录成功
            if (code.equals("200")) {
                // ç™»å½•成功后,获取data
                String data = JSONObject.parseObject(response).getString("data");
                String token = JSONObject.parseObject(data).getString("token");
                // è®¾ç½®data到Redis中
                redisCache.deleteObject(CacheConstants.USER_CACHE_KEY + "entity_db_response");
                redisCache.setCacheObject(CacheConstants.USER_CACHE_KEY + "entity_db_response", response, 1, TimeUnit.HOURS);
                // è®¾ç½®data到Redis中
                redisCache.deleteObject(CacheConstants.USER_CACHE_KEY + "entity_db_token");
                redisCache.setCacheObject(CacheConstants.USER_CACHE_KEY + "entity_db_token", token, 1, TimeUnit.HOURS);
            } else {
                // ç™»å½•失败
                log.error("登录失败 $= " + response);
            }
        });
        // é˜²æ­¢ä¸»çº¿ç¨‹æå‰ç»“束
        try {
            postResponse.toFuture().get(); // é˜»å¡žç­‰å¾…订阅操作完成
            // ç­‰å¾…异步请求完成
            Thread.sleep(1000);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        // è¿”回token
        return JSONObject.parseObject(responseData.get());
    }
    /**
     * è‡ªå®šä¹‰å¯†é’¥ç”ŸæˆåŠ å¯†æ•°æ®
     *
     * @param data      æ•°æ®
     * @param publicKey å…¬é’¥
     * @return {@link String}
     */
    private static String customKeysGenerateEncryptedData(String data, String publicKey) {
        AsymmetricCrypto rsa = SecureUtil.rsa(null, publicKey);
        String encrypt = rsa.encryptBase64(data, KeyType.PublicKey);
        System.out.println("encrypt = " + encrypt);
        return encrypt;
    }
    /**
     * è‡ªå®šä¹‰ RSAgenerate åŠ å¯†æ•°æ®0
     */
    private static void customRSAGenerateEncryptedData0() {
        RSA rsa = new RSA();
        // èŽ·å–å…¬é’¥å’Œç§é’¥
        System.out.println(rsa.getPublicKey());
        System.out.println(rsa.getPrivateKeyBase64());
        System.out.println(rsa.getPrivateKey());
        System.out.println(rsa.getPrivateKeyBase64());
        // ç§é’¥åР坆,公钥解密
        System.out.println(new String(rsa.encrypt("testaa", KeyType.PrivateKey)));
        System.out.println(new String(rsa.decrypt(rsa.encrypt("testaa", KeyType.PrivateKey), KeyType.PublicKey)));
        // å…¬é’¥åР坆,私钥解密
        System.out.println(new String(rsa.encrypt("testaa", KeyType.PublicKey)));
        System.out.println(new String(rsa.decrypt(rsa.encrypt("testaa", KeyType.PublicKey), KeyType.PrivateKey)));
    }
    /**
     * è‡ªå®šä¹‰ RSAgenerate åŠ å¯†æ•°æ®1
     */
    private static void customRSAGenerateEncryptedData1() {
        KeyPair keyPair = SecureUtil.generateKeyPair(AsymmetricAlgorithm.RSA.getValue());
        PrivateKey privateKey = keyPair.getPrivate();
        PublicKey publicKey = keyPair.getPublic();
        System.out.println(publicKey);
        System.out.println(privateKey);
        System.out.println("----------");
        RSA rsa = new RSA(privateKey, publicKey);
        // ç§é’¥åР坆,公钥解密
        System.out.println(new String(rsa.encrypt("testaa", KeyType.PrivateKey)));
        System.out.println(new String(rsa.decrypt(rsa.encrypt("testaa", KeyType.PrivateKey), KeyType.PublicKey)));
        // å…¬é’¥åР坆,私钥解密
        System.out.println(new String(rsa.encrypt("testaa", KeyType.PublicKey)));
        System.out.println(new String(rsa.decrypt(rsa.encrypt("testaa", KeyType.PublicKey), KeyType.PrivateKey)));
    }
    /**
     * èŽ·å–è®¿é—®å®žä½“åº“çš„token
     *
     * @return {@link Object}
     */
    @Override
    public Object getEntityPublicKey() {
        // åˆ¤æ–­redis中缓存是否存在(过期)
        Boolean isExists = redisCache.hasKey(CacheConstants.USER_CACHE_KEY + "EntityPublicKey");
        if (isExists) {
            return JSONObject.parseObject(redisCache.getCacheObject(CacheConstants.USER_CACHE_KEY + "EntityPublicKey"));
        }
        HashMap<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        CompletableFuture<String> postResponse = CustomWebClient.postAsFuture(queryUrl, "", headers, String.class);
        // å¼‚步处理响应
        postResponse.thenAccept(response -> {
            log.info("response: {}", response);
            if (response.contains("code")) {
                try {
                    JSONObject postResponseJson = JSONObject.parseObject(response);
                    int statusCode = postResponseJson.getIntValue("code");
                    log.info("statusCode = " + statusCode);
                    String data = postResponseJson.getString("data");
                    // ç¼“å­˜data,并设置1小时有效期
                    redisCache.deleteObject(CacheConstants.USER_CACHE_KEY + "EntityPublicKey");
                    redisCache.setCacheObject(CacheConstants.USER_CACHE_KEY + "EntityPublicKey", response, 1, TimeUnit.HOURS);
                    log.info("EntityPublicKey = " + data);
                } catch (Exception e) {
                    log.info("Failed to parse JSON: " + e.getMessage());
                }
            } else {
                log.info("No 'code' field in the response: " + response);
            }
        });
        return JSONObject.parseObject(postResponse.join());
    }
    /**
     * èŽ·å–è®¿é—®å®žä½“åº“çš„token
     *
     * @return {@link Object}
     */
    @Override
    public Object getDbLits() {
        HashMap<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        String token = redisCache.getCacheObject(CacheConstants.USER_CACHE_KEY + "entity_db_token");
        log.info("token = " + token);
        // æ·»åŠ form参数
        HashMap<String, Object> params = new HashMap<>();
        params.put("token", token);
        CompletableFuture<String> postResponse = CustomWebClient.postAsFuture(getDbUrl, params, headers, String.class);
        // å¼‚步处理响应
        postResponse.thenAccept(response -> {
            log.info("response: {}", response);
            if (response.contains("code")) {
                try {
                    JSONObject postResponseJson = JSONObject.parseObject(response);
                    int statusCode = postResponseJson.getIntValue("code");
                    log.info("statusCode = " + statusCode);
                    String data = postResponseJson.getString("data");
                    // ç¼“å­˜data,并设置1小时有效期
                    redisCache.deleteObject(CacheConstants.USER_CACHE_KEY + "EntityDbNameList");
                    redisCache.setCacheObject(CacheConstants.USER_CACHE_KEY + "EntityDbNameList", response, 1, TimeUnit.HOURS);
                    log.info("EntityDbNameList = " + data);
                } catch (Exception e) {
                    log.info("Failed to parse JSON: " + e.getMessage());
                }
            } else {
                log.info("No 'code' field in the response: " + response);
            }
        });
        return JSONObject.parseObject(postResponse.join());
    }
    @Override
    public Object getEntityTypeInfo(EntityTypeInfo entityTypeInfo) {
        HashMap<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        String token = redisCache.getCacheObject(CacheConstants.USER_CACHE_KEY + "entity_db_token");
        log.info("token = " + token);
        // æ·»åŠ form参数
        HashMap<String, Object> params = new HashMap<>();
        params.put("token", token);
        params.put("start", Objects.nonNull(entityTypeInfo.getStart()) ? entityTypeInfo.getStart() : 1);
        params.put("containCount", true);
        params.put("count", Objects.nonNull(entityTypeInfo.getCount()) ? entityTypeInfo.getCount() : 20);
        params.put("dbid", Objects.nonNull(entityTypeInfo.getDbid()) ? entityTypeInfo.getDbid() : "85257774fdb64e5f99f6778696cad02a");
        params.put("layerid", "f6ff4412-4886-4c4b-83f7-13de24ee8353");
        params.put("like", "");
        params.put("querytype", "entity");
        CompletableFuture<String> postResponse = CustomWebClient.postAsFuture(queryUrl, params, headers, String.class);
        // å¼‚步处理响应
        postResponse.thenAccept(response -> {
            log.info("response: {}", response);
            if (response.contains("code")) {
                try {
                    JSONObject postResponseJson = JSONObject.parseObject(response);
                    int statusCode = postResponseJson.getIntValue("code");
                    log.info("statusCode = " + statusCode);
                    String data = postResponseJson.getString("data");
                    log.info("getEntityTypeInfo = " + data);
                } catch (Exception e) {
                    log.info("Failed to parse JSON: " + e.getMessage());
                }
            } else {
                log.info("No 'code' field in the response: " + response);
            }
        });
        return JSONObject.parseObject(postResponse.join());
    }
}
src/main/java/com/se/simu/service/ProjectRelatedService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package com.se.simu.service;
import com.se.simu.domain.EntityTypeInfo;
import com.se.simu.domain.LoginParams;
public interface ProjectRelatedService {
    /**
     * èŽ·å–å…¬é’¥
     *
     * @return {@link String}
     */
    Object getPublicKey();
    /**
     * ç™»å½•实体
     *
     * @param loginParams ç™»å½•参数
     * @return {@link String}
     */
    Object loginEntity(LoginParams loginParams);
    /**
     * èŽ·å–è®¿é—®å®žä½“åº“çš„token
     *
     * @return {@link Object}
     */
    Object getEntityPublicKey();
    Object getDbLits();
    Object getEntityTypeInfo(EntityTypeInfo entityTypeInfo);
}
src/main/java/com/se/simu/utils/CustomWebClient.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,500 @@
package com.se.simu.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
/**
 * è‡ªå®šä¹‰ Web å®¢æˆ·ç«¯
 *
 * @author xingjinshuang@smartearth.cn
 * @date 2024/06/27
 */
public class CustomWebClient {
    private static final Logger logger = LoggerFactory.getLogger(CustomWebClient.class);
    private static final WebClient webClient;
    static {
        // åœ¨é™æ€ä»£ç å—中实例化WebClient.Builder并构建WebClient对象
        webClient = WebClient.builder().build();
    }
    //====基础请求=====================================================================================================================================
    public static Mono<String> get(String url) {
        return webClient.get()
                .uri(url)
                .retrieve()
                .bodyToMono(String.class);
    }
    public static Mono<String> post(String url, Object requestBody) {
        return webClient.post()
                .uri(url)
                .bodyValue(requestBody)
                .retrieve()
                .bodyToMono(String.class);
    }
    public static Mono<String> postForm(String url, MultiValueMap<String, String> formData) {
        return webClient.post()
                .uri(url)
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .body(BodyInserters.fromFormData(formData))
                .retrieve()
                .bodyToMono(String.class);
    }
    public static Mono<String> put(String url, Object requestBody) {
        return webClient.put()
                .uri(url)
                .bodyValue(requestBody)
                .retrieve()
                .bodyToMono(String.class);
    }
    // å‘送PUT请求
    public static <T, R> Mono<T> put(String url, R requestBody, Class<T> responseType) {
        return webClient.put()
                .uri(url)
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(requestBody))
                .retrieve()
                .bodyToMono(responseType);
    }
    public static Mono<String> delete(String url) {
        return webClient.delete()
                .uri(url)
                .retrieve()
                .onStatus(HttpStatus::is4xxClientError,
                        clientResponse -> Mono.error(new CustomWebClientException1("Client error: " + clientResponse.statusCode())))
                .onStatus(HttpStatus::is5xxServerError,
                        clientResponse -> Mono.error(new CustomWebClientException1("Server error: " + clientResponse.statusCode())))
                .bodyToMono(String.class);
    }
    //=======自定义返回类型的请求===================================================================================================================================
    public static <T> Mono<T> getAndParse(String url, Class<T> responseType) {
        return webClient.get()
                .uri(url)
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToMono(responseType);
    }
    public static <T, R> Mono<T> postAndParse(String url, R requestBody, Class<T> responseType) {
        return webClient.post()
                .uri(url)
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(requestBody))
                .retrieve()
                .bodyToMono(responseType);
    }
    /**
     * toBodilessEntity下载或者删除的时候不需要对响应体处理的
     * toBodilessEntity() æ˜¯ Java Play Framework ä¸­ WebClient ç±»çš„一个方法,它用于将响应转换为没有体的响应实体。
     * é€šå¸¸ï¼Œæˆ‘们在处理不需要读取响应体的大型下载时,可以使用这个方法来避免不必要的内存占用。
     */
    public static Mono<String> postAndReceiveLocation(String url, Object requestBody) {
        return webClient.post()
                .uri(url)
                .bodyValue(requestBody)
                .retrieve()
                .toBodilessEntity()
                .flatMap(response -> {
                    if (response.getHeaders().getLocation() != null) {
                        return Mono.just(response.getHeaders().getLocation().toString());
                    } else {
                        // å¦‚æžœLocation为空,返回response
                        return Mono.just(response.getStatusCode().toString());
                    }
                });
    }
    public static Mono<String> postFormAndReceiveLocation(String url, MultiValueMap<String, String> formData) {
        return webClient.post()
                .uri(url)
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .body(BodyInserters.fromFormData(formData))
                .retrieve()
                .toBodilessEntity()
                .flatMap(response -> {
                    if (response.getHeaders().getLocation() != null) {
                        return Mono.just(response.getHeaders().getLocation().toString());
                    } else {
                        // å¦‚æžœLocation为空,返回response的状态码
                        return Mono.just(response.getStatusCode().toString());
                    }
                });
    }
    //==========================================================================================================================================
    // å¼‚步请求的GET、POST方式
    public static <T> Mono<T> getAsMono(String path, Class<T> responseType) {
        return webClient.get()
                .uri(path)
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToMono(responseType);
    }
    public static <T> Mono<T> postAsMono(String path, Object requestBody, Class<T> responseType) {
        return webClient.post()
                .uri(path)
                .contentType(MediaType.APPLICATION_JSON)
                .body(Mono.just(requestBody), requestBody.getClass())
                .retrieve()
                .bodyToMono(responseType);
    }
    public static <T> CompletableFuture<T> getAsFuture(String path, Class<T> responseType) {
        return webClient
                .method(HttpMethod.GET)
                .uri(path)
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToMono(responseType)
                .toFuture();
    }
    public static <T> CompletableFuture<T> postAsFuture(String path, Object requestBody, HashMap<String, String> headers, Class<T> responseType) {
        return webClient
                .method(HttpMethod.POST)
                .uri(path)
                .contentType(MediaType.APPLICATION_JSON)
                .headers(h -> headers.forEach(h::add))
                .bodyValue(requestBody)
                .retrieve()
                .bodyToMono(responseType)
                .toFuture();
    }
    //==========================================================================================================================================
    // å…¶ä»–方式一发起请求
    private static final WebClient WEB_CLIENT = WebClient.create();
    /**
     * å‘èµ·GET请求,支持Get parameter
     */
    public static CompletableFuture<String> getParam(String url, HttpHeaders headers, MultiValueMap<String, String> queryParams) {
        return Mono.from(WEB_CLIENT.get()
                        .uri(uriBuilder -> uriBuilder
                                .path(url)
                                .queryParams(queryParams)
                                .build())
                        .headers(httpHeaders -> httpHeaders.putAll(headers))
                        //.headers(h -> headers.forEach(h::add))
                        .retrieve()
                        .onStatus(HttpStatus::isError, clientResponse -> Mono.error(new RuntimeException("HTTP error status: " + clientResponse.statusCode())))
                        .bodyToMono(String.class))
                .onErrorResume(error -> Mono.just("Error: " + error.getMessage())) // å¦‚果有错误,返回错误信息
                .toFuture();
    }
    /**
     * å‘èµ·GET请求,支持Get parameter
     * å¯ä»¥ç”¨
     */
    public static CompletableFuture<String> getNoParam(String url, HttpHeaders headers) {
        return Mono.from(WEB_CLIENT.get()
                        .uri(url)
                        .headers(httpHeaders -> httpHeaders.putAll(headers))
                        //.headers(h -> headers.forEach(h::add))
                        .retrieve()
                        .onStatus(HttpStatus::isError, clientResponse -> Mono.error(new RuntimeException("HTTP error status: " + clientResponse.statusCode())))
                        .bodyToMono(String.class))
                .onErrorResume(error -> Mono.just("Error: " + error.getMessage())) // å¦‚果有错误,返回错误信息
                .toFuture();
    }
    /**
     * å‘èµ·POST请求,支持JSON body
     */
    public static CompletableFuture<String> postJson(String url, Object body, HashMap<String, String> headers) {
        return Mono.from(WEB_CLIENT.post()
                        .uri(url)
                        .contentType(MediaType.APPLICATION_JSON)
                        .headers(h -> headers.forEach(h::add))
                        .bodyValue(body)
                        .retrieve()
                        .onStatus(HttpStatus::isError, clientResponse -> Mono.error(new RuntimeException("HTTP error status: " + clientResponse.statusCode())))
                        .bodyToMono(String.class))
                .onErrorResume(error -> Mono.just("Error: " + error.getMessage())) // å¦‚果有错误,返回错误信息
                .toFuture();
    }
    /**
     * å‘èµ·POST请求,支持表单数据
     */
    public static CompletableFuture<String> postForm(String url, MultiValueMap<String, String> formData, Map<String, String> headers) {
        return Mono.from(WEB_CLIENT.post()
                        .uri(url)
                        .headers(h -> headers.forEach(h::add))
                        .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                        .body(BodyInserters.fromFormData(formData))
                        .retrieve()
                        .bodyToMono(String.class))
                .toFuture();
    }
    //=========================================================================================================================================
    // å…¶ä»–请求方式二
    public Mono<String> getRequest(String url, long timeoutSeconds, int retryCount) {
        return executeRequest(url, HttpMethod.GET, null, HttpHeaders.EMPTY, timeoutSeconds, retryCount);
    }
    public Mono<String> postRequest(String url, Object requestBody, HttpHeaders headers, long timeoutSeconds, int retryCount) {
        return executeRequest(url, HttpMethod.POST, requestBody, headers, timeoutSeconds, retryCount);
    }
    /**
     * æ‰§è¡Œè¯·æ±‚
     *
     * @param url            ç½‘址
     * @param method         æ–¹æ³•
     * @param requestBody    è¯·æ±‚正文
     * @param headers        å¤´
     * @param timeoutSeconds è¶…æ—¶ç§’æ•°
     * @param retryCount     é‡è¯•计数
     * @return {@link Mono}<{@link String}>
     */
    private Mono<String> executeRequest(String url, HttpMethod method, Object requestBody, HttpHeaders headers, long timeoutSeconds, int retryCount) {
        return executeRequestInternal(url, method, requestBody, headers, timeoutSeconds, retryCount)
                .onErrorResume(throwable -> {
                    logger.error("Error during request: {}", throwable.getMessage());
                    return Mono.error(throwable);
                });
    }
    /**
     * å†…部执行请求
     *
     * @param url            ç½‘址
     * @param method         æ–¹æ³•
     * @param requestBody    è¯·æ±‚正文
     * @param headers        å¤´
     * @param timeoutSeconds è¶…æ—¶ç§’æ•°
     * @param retryCount     é‡è¯•计数
     * @return {@link Mono}<{@link String}>
     */
    private Mono<String> executeRequestInternal(String url, HttpMethod method, Object requestBody, HttpHeaders headers, long timeoutSeconds, int retryCount) {
        return webClient.method(method)
                .uri(url)
                .headers(httpHeaders -> httpHeaders.addAll(headers))
                .bodyValue(requestBody)
                .retrieve()
                .bodyToMono(String.class)
                .timeout(Duration.ofSeconds(timeoutSeconds))
                .doOnError(error -> logger.error("Error during request: {}", error))
                .retry(retryCount);
    }
    //=========================================================================================================================================
    // å…¶ä»–参数
    /**
     * ä½¿ç”¨è¶…时获取
     *
     * @param url            ç«¯ç‚¹
     * @param timeoutSeconds è¶…æ—¶ç§’æ•°
     * @return {@link Mono}<{@link String}>
     */
    public Mono<String> getWithTimeout(String url, long timeoutSeconds) {
        return webClient.get()
                .uri(url)
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToMono(String.class)
                .timeout(Duration.ofSeconds(timeoutSeconds))
                .onErrorMap(error -> new CustomWebClientException("Request timeout", error));
    }
    /**
     * å¸¦æ ‡é¢˜å¸–子
     *
     * @param url         ç«¯ç‚¹
     * @param requestBody è¯·æ±‚正文
     * @param headers     å¤´
     * @return {@link Mono}<{@link String}>
     */
    public Mono<String> postWithHeaders(String url, Object requestBody, HttpHeaders headers) {
        return webClient.post()
                .uri(url)
                .bodyValue(requestBody)
                .headers(httpHeaders -> httpHeaders.addAll(headers))
                .retrieve()
                .bodyToMono(String.class);
    }
    /**
     * èŽ·å–å¹¶å‘
     *
     * @param endpoint1 ç«¯ç‚¹ 1
     * @param endpoint2 ç«¯ç‚¹ 2
     * @return {@link Mono}<{@link String}>
     */
    public Mono<String> getConcurrently(String endpoint1, String endpoint2) {
        Mono<String> result1 = webClient.get()
                .uri(endpoint1)
                .retrieve()
                .bodyToMono(String.class);
        Mono<String> result2 = webClient.get()
                .uri(endpoint2)
                .retrieve()
                .bodyToMono(String.class);
        return result1.zipWith(result2).map(tuple -> tuple.getT1() + tuple.getT2());
    }
    //==========================================================================================================================================
    /**
     * è‡ªå®šä¹‰ Web å®¢æˆ·ç«¯å¼‚常
     * Custom exception class for WebClient error handling
     *
     * @author xingjinshuang@smartearth.cn
     * @date 2024/06/27
     */
    public static class CustomWebClientException1 extends RuntimeException {
        public CustomWebClientException1(String message) {
            super(message);
        }
    }
    /**
     * è‡ªå®šä¹‰ Web å®¢æˆ·ç«¯å¼‚常
     *
     * @author xingjinshuang
     * @date 2024/06/27
     */
    public static class CustomWebClientException extends RuntimeException {
        public CustomWebClientException(String message, Throwable cause) {
            super(message, cause);
        }
    }
    /**
     * ä¸»è¦
     *
     * @param args å‚æ•°
     */
    public static void main(String[] args) {
        HashMap<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        Mono<String> res = getAndParse("https://api.example.com/data", String.class);
        res.subscribe(dataResponse -> {
            // å¤„理数据响应
            System.out.println("Received data response: " + dataResponse);
        });
        Mono<String> res1 = get("https://api.example.com/textdata");
        res1.subscribe(textData -> {
            // å¤„理文本数据响应
            System.out.println("Received text data: " + textData);
        });
        String requestBody00 = new String("test");
        Mono<String> res2 = postAndReceiveLocation("https://api.example.com/resource", requestBody00);
        res2.subscribe(location -> {
            // å¤„理返回的资源位置
            System.out.println("Resource location: " + location);
        });
        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
        formData.add("key1", "value1");
        formData.add("key2", "value2");
        Mono<String> res3 = postFormAndReceiveLocation("https://api.example.com/formsubmit", formData);
        res3.subscribe(location -> {
            // å¤„理返回的表单提交位置
            System.out.println("Form submission location: " + location);
        });
        // å¼‚æ­¥GET请求,通过subscribe方法来处理响应
        Mono<String> asyncResponse0 = getAsMono("/api/resource", String.class);
        // asyncResponse0.subscribe(System.out::println);
        asyncResponse0.flatMap(response -> {
            System.out.println("GET请求结果:" + response);
            return Mono.just(response);
        }).subscribe();
        // å¼‚æ­¥POST请求,通过subscribe方法来处理响应
        String requestBody0 = new String("data");
        Mono<String> asyncPostedResponse0 = postAsMono("/api/resource", requestBody0, String.class);
        // asyncPostedResponse0.subscribe(System.out::println);
        asyncPostedResponse0.flatMap(response -> {
            System.out.println("POST请求结果:" + response);
            return Mono.just(response);
        }).subscribe();
        // å¼‚æ­¥GET请求,不会直接返回返回体
        CompletableFuture<String> asyncResponse = getAsFuture("/api/resource", String.class);
        asyncResponse.thenAccept(response -> {
            System.out.println("GET请求结果:" + response);
        });
        // å¼‚æ­¥POST请求,不会直接返回返回体
        String requestBody = new String("data");
        CompletableFuture<String> asyncPostedResponse = postAsFuture("/api/resource", requestBody, headers, String.class);
        asyncPostedResponse.thenAccept(response -> {
            System.out.println("POST请求结果:" + response);
        });
        // henAccept方法是一个消费型的方法,它不会返回任何值。
        // è¦èŽ·å–å¼‚æ­¥è¯·æ±‚çš„è¿”å›žå€¼ï¼Œå¯ä»¥ä½¿ç”¨thenApply方法,这个方法会返回一个新的CompletableFuture对象,里面包含经过处理后的返回值
        // å¼‚æ­¥GET请求,返回返回体
        CompletableFuture<String> asyncResponseRes = getAsFuture("/api/resource", String.class);
        asyncResponse.thenApply(response -> {
            System.out.println("GET请求结果:" + response);
            return response;
        });
        // henAccept方法是一个消费型的方法,它不会返回任何值。
        // è¦èŽ·å–å¼‚æ­¥è¯·æ±‚çš„è¿”å›žå€¼ï¼Œå¯ä»¥ä½¿ç”¨thenApply方法,这个方法会返回一个新的CompletableFuture对象,里面包含经过处理后的返回值
        // å¼‚æ­¥POST请求,返回返回体
        String requestBody1 = new String("data");
        CompletableFuture<String> asyncPostedResponseRes = postAsFuture("/api/resource", requestBody1, headers, String.class);
        asyncPostedResponse.thenApply(response -> {
            System.out.println("POST请求结果:" + response);
            return response;
        });
        // åŒæ­¥æ–¹å¼ä¸‹èŽ·å–å“åº”ä½“ï¼Œå¯ä»¥ä½¿ç”¨join方法来等待异步操作的完成并获取最终的结果。这样可以确保在获取结果之前阻塞当前线程,直到异步操作完成。
        // ä½¿ç”¨join方法来同步获取响应体:
        String requestBody2 = new String("data");
        CompletableFuture<String> asyncPostedResponse2 = CustomWebClient.postAsFuture("/api/resource", requestBody2, headers, String.class);
        asyncPostedResponse2.thenAccept(response -> {
            System.out.println("POST请求结果:" + response);
        });
        String syncResponse = asyncPostedResponse2.join();
        System.out.println("同步获取的响应体:" + syncResponse);
        // é˜²æ­¢ä¸»çº¿ç¨‹æå‰ç»“束
        try {
            // ç­‰å¾…异步请求完成
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
src/main/resources/application-prod.yml
@@ -91,6 +91,22 @@
  connectTimeout: -1
  readTimeout: -1
# é¡¹ç›®å®žä½“库服务中配置
app-server:
  # å…¬é’¥åœ°å€
  publicKeyUrl: http://106.120.22.26:8024/account-service/security/publickey
  # ç™»å½•地址
  loginUrl: http://106.120.22.26:8024/account-service/security/login
  # æŸ¥è¯¢æ•°æ®åº“地址
  getDbUrl: http://106.120.22.26:8024/geo-service/entitydb/list/canview
  # æŸ¥è¯¢ä¸åŒæ•°æ®åº“类型下的数据地址
  queryUrl: http://106.120.22.26:8024/geo-service/entitydbdata/layer/query
config:
  ver: 0.2
  cacheTime: 60
src/main/resources/application-zyy.yml
@@ -91,6 +91,19 @@
  connectTimeout: -1
  readTimeout: -1
# é¡¹ç›®å®žä½“库服务中配置
app-server:
  # å…¬é’¥åœ°å€
  publicKeyUrl: http://106.120.22.26:8024/account-service/security/publickey
  # ç™»å½•地址
  loginUrl: http://106.120.22.26:8024/account-service/security/login
  # æŸ¥è¯¢æ•°æ®åº“地址
  getDbUrl: http://106.120.22.26:8024/geo-service/entitydb/list/canview
  # æŸ¥è¯¢ä¸åŒæ•°æ®åº“类型下的数据地址
  queryUrl: http://106.120.22.26:8024/geo-service/entitydbdata/layer/query
config:
  ver: 0.2
  cacheTime: 60