From aacca16683a188295345ff0470857abbd1154c1d Mon Sep 17 00:00:00 2001 From: sqlicong <2301640570@qq.com> Date: Thu, 7 Aug 2025 15:25:08 +0800 Subject: [PATCH] =?UTF-8?q?=E9=98=BF=E5=A5=87=E7=B4=A2=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=8F=91=E8=B4=A7=E6=8E=A5=E5=85=A5=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../enums/GlobalErrorCodeConstants.java | 2 +- .../yudao/module/kfc/config/AgisoConfig.java | 15 ++ .../yudao/module/kfc/config/OkHttpConfig.java | 28 +++ .../AgisoAuthCallbackController.java | 1 - .../{AccessTokenData.java => AgisoData.java} | 2 +- ...sTokenResponse.java => AgisoResponse.java} | 4 +- .../module/kfc/enums/ErrorCodeConstants.java | 3 + .../kfc/service/agiso/AgisoAuthService.java | 17 ++ .../service/agiso/AgisoAuthServiceImpl.java | 175 ++++++++++++++---- .../yudao/module/kfc/utils/HttpUtils.java | 131 +++++++++++++ .../src/main/resources/application-local.yaml | 1 + 11 files changed, 338 insertions(+), 41 deletions(-) create mode 100644 yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/config/AgisoConfig.java create mode 100644 yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/config/OkHttpConfig.java rename yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/controller/admin/agisoproxy/vo/{AccessTokenData.java => AgisoData.java} (96%) rename yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/controller/admin/agisoproxy/vo/{AccessTokenResponse.java => AgisoResponse.java} (89%) create mode 100644 yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/utils/HttpUtils.java diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/GlobalErrorCodeConstants.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/GlobalErrorCodeConstants.java index edf31f24aa..228e844367 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/GlobalErrorCodeConstants.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/exception/enums/GlobalErrorCodeConstants.java @@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode; * 全局错误码枚举 * 0-999 系统异常编码保留 * - * 一般情况下,使用 HTTP 响应状态码 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status + * 一般情况下,使用 HTTP 响应状态码 ... * 虽然说,HTTP 响应状态码作为业务使用表达能力偏弱,但是使用在系统层面还是非常不错的 * 比较特殊的是,因为之前一直使用 0 作为成功,就不使用 200 啦。 * diff --git a/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/config/AgisoConfig.java b/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/config/AgisoConfig.java new file mode 100644 index 0000000000..d8627209b0 --- /dev/null +++ b/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/config/AgisoConfig.java @@ -0,0 +1,15 @@ +package cn.iocoder.yudao.module.kfc.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@ConfigurationProperties(prefix = "agiso") +@Configuration +public class AgisoConfig { + + private String appid; + + private String appsecret; +} diff --git a/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/config/OkHttpConfig.java b/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/config/OkHttpConfig.java new file mode 100644 index 0000000000..81342dec1d --- /dev/null +++ b/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/config/OkHttpConfig.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.kfc.config; + +import okhttp3.ConnectionPool; +import okhttp3.OkHttpClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import java.util.concurrent.TimeUnit; +/** +OkHttp 配置类,用于创建并注册 OkHttpClient 实例到 Spring 容器 +*/ +@Configuration +public class OkHttpConfig { + /** + 定义 OkHttpClient 的 bean,设置超时时间和连接池等参数 + */ + @Bean + public OkHttpClient okHttpClient () { + // 连接池配置:最大空闲连接数 5 个,连接空闲超时 5 分钟 + ConnectionPool connectionPool = new ConnectionPool (5, 5, TimeUnit.MINUTES); + return new OkHttpClient.Builder () + .connectTimeout (30, TimeUnit.SECONDS) + .readTimeout (30, TimeUnit.SECONDS) + .writeTimeout (30, TimeUnit.SECONDS) + .connectionPool (connectionPool) + .retryOnConnectionFailure (true) + .build (); + } +} \ No newline at end of file diff --git a/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/controller/admin/agisoproxy/AgisoAuthCallbackController.java b/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/controller/admin/agisoproxy/AgisoAuthCallbackController.java index 3546bbb7be..32a059ca1d 100644 --- a/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/controller/admin/agisoproxy/AgisoAuthCallbackController.java +++ b/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/controller/admin/agisoproxy/AgisoAuthCallbackController.java @@ -44,7 +44,6 @@ public class AgisoAuthCallbackController { @GetMapping("/get/code-url") @Operation(summary = "生成阿奇索code认证url") - @PermitAll public CommonResult getCodeUrl() { return success(agisoAuthService.generateAuthUrl()); } diff --git a/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/controller/admin/agisoproxy/vo/AccessTokenData.java b/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/controller/admin/agisoproxy/vo/AgisoData.java similarity index 96% rename from yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/controller/admin/agisoproxy/vo/AccessTokenData.java rename to yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/controller/admin/agisoproxy/vo/AgisoData.java index 28089fb04f..541ff233af 100644 --- a/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/controller/admin/agisoproxy/vo/AccessTokenData.java +++ b/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/controller/admin/agisoproxy/vo/AgisoData.java @@ -7,7 +7,7 @@ import lombok.Data; * 授权成功后返回的AccessToken相关数据实体类 */ @Data -public class AccessTokenData { +public class AgisoData { /** * 平台标识 diff --git a/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/controller/admin/agisoproxy/vo/AccessTokenResponse.java b/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/controller/admin/agisoproxy/vo/AgisoResponse.java similarity index 89% rename from yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/controller/admin/agisoproxy/vo/AccessTokenResponse.java rename to yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/controller/admin/agisoproxy/vo/AgisoResponse.java index af5c1db4b8..0d4d015818 100644 --- a/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/controller/admin/agisoproxy/vo/AccessTokenResponse.java +++ b/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/controller/admin/agisoproxy/vo/AgisoResponse.java @@ -7,7 +7,7 @@ import lombok.Data; * 换取AccessToken的完整响应实体类 */ @Data -public class AccessTokenResponse { +public class AgisoResponse { /** * 操作是否成功 @@ -31,5 +31,5 @@ public class AccessTokenResponse { * 详细数据(成功时返回) */ @JsonProperty("Data") - private AccessTokenData data; + private AgisoData data; } \ No newline at end of file diff --git a/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/enums/ErrorCodeConstants.java b/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/enums/ErrorCodeConstants.java index 30da95b8c3..4c0ef0af4e 100644 --- a/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/enums/ErrorCodeConstants.java +++ b/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/enums/ErrorCodeConstants.java @@ -53,4 +53,7 @@ public interface ErrorCodeConstants { ErrorCode RESPONSE_IS_EMPTY = new ErrorCode(2_001_013_000,"响应体为空"); ErrorCode TOKEN_IS_EMPTY = new ErrorCode(2_001_013_001,"响应数据中未包含有效的Token"); + ErrorCode STATE_NOT_VALID = new ErrorCode(2_001_013_002,"回调失败:state参数无效或已过期"); + ErrorCode TOKEN_AUTH_FAILED = new ErrorCode(2_001_013_003,"认证失败:token令牌无效或已过期"); + ErrorCode USER_AUTH_FAILED = new ErrorCode(2_001_013_004,"用户授权失败"); } diff --git a/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/service/agiso/AgisoAuthService.java b/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/service/agiso/AgisoAuthService.java index 9372adacd2..e125d770f6 100644 --- a/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/service/agiso/AgisoAuthService.java +++ b/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/service/agiso/AgisoAuthService.java @@ -1,9 +1,26 @@ package cn.iocoder.yudao.module.kfc.service.agiso; +import cn.iocoder.yudao.module.kfc.controller.admin.agisoproxy.vo.AgisoResponse; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.util.List; +import java.util.Map; + public interface AgisoAuthService { String generateAuthUrl(); String authAndGetAccessToken(String code, String state, String error); + AgisoResponse execute(String url, String method, Map params, String sellerOpenUid) throws IOException, NoSuchAlgorithmException; + + AgisoResponse doPost(String url, Map params, String sellerOpenUid) throws NoSuchAlgorithmException; + + AgisoResponse doGet(String url, Map params, String sellerOpenUid) throws NoSuchAlgorithmException; + + AgisoResponse autoShipment(List tids, String sellerOpenUid) throws NoSuchAlgorithmException; + + String sign(Map params, String appSecret) + throws NoSuchAlgorithmException; } diff --git a/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/service/agiso/AgisoAuthServiceImpl.java b/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/service/agiso/AgisoAuthServiceImpl.java index 80a1efb971..383bbdb6d1 100644 --- a/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/service/agiso/AgisoAuthServiceImpl.java +++ b/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/service/agiso/AgisoAuthServiceImpl.java @@ -1,40 +1,41 @@ package cn.iocoder.yudao.module.kfc.service.agiso; import cn.hutool.core.util.RandomUtil; -import cn.iocoder.yudao.module.kfc.controller.admin.agisoproxy.vo.AccessTokenData; -import cn.iocoder.yudao.module.kfc.controller.admin.agisoproxy.vo.AccessTokenResponse; -import com.fasterxml.jackson.core.JsonProcessingException; +import cn.iocoder.yudao.module.kfc.config.AgisoConfig; +import cn.iocoder.yudao.module.kfc.controller.admin.agisoproxy.vo.AgisoData; +import cn.iocoder.yudao.module.kfc.controller.admin.agisoproxy.vo.AgisoResponse; +import cn.iocoder.yudao.module.kfc.utils.HttpUtils; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import org.springframework.beans.factory.annotation.Value; +import okhttp3.*; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.io.IOException; -import java.util.Objects; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.kfc.enums.ErrorCodeConstants.RESPONSE_IS_EMPTY; -import static cn.iocoder.yudao.module.kfc.enums.ErrorCodeConstants.TOKEN_IS_EMPTY; +import static cn.iocoder.yudao.module.kfc.enums.ErrorCodeConstants.*; +import static software.amazon.awssdk.http.HttpStatusCode.BAD_REQUEST; @Service @Slf4j public class AgisoAuthServiceImpl implements AgisoAuthService { - @Value("${agiso.appid}") - private String APPID; - @Value("${agiso.appsecret}") - private String APPSECRET; + @Resource + private AgisoConfig agisoConfig; - // Redis中存储state的前缀,过期时间10分钟(授权流程通常较短) + // Redis中存储state的前缀,过期时间10分钟 private static final String STATE_REDIS_KEY_PREFIX = "agiso:auth:state:"; + private static final String TOKEN_REDIS_KEY_PREFIX = "agiso:token:"; private static final long STATE_EXPIRE_MINUTES = 10; + private static final String AUTO_SHIPMENTS_URL = "http://gw.api.agiso.com/alds/Trade/AldsProcessTrades"; @Resource private RedisTemplate redisTemplate; @@ -46,7 +47,7 @@ public class AgisoAuthServiceImpl implements AgisoAuthService { private ObjectMapper objectMapper; /** - * 生成授权链接(新增方法,用于发起授权) + * 生成授权链接 */ @Override public String generateAuthUrl() { @@ -56,7 +57,7 @@ public class AgisoAuthServiceImpl implements AgisoAuthService { redisTemplate.opsForValue() .set(STATE_REDIS_KEY_PREFIX + state, "VALID", STATE_EXPIRE_MINUTES, TimeUnit.MINUTES); // 3. 拼接授权URL - return "https://alds.agiso.com/authorize.aspx?appId=" + APPID + "&state=" + state; + return "https://alds.agiso.com/authorize.aspx?appId=" + agisoConfig.getAppid() + "&state=" + state; } /** @@ -68,7 +69,7 @@ public class AgisoAuthServiceImpl implements AgisoAuthService { String redisKey = STATE_REDIS_KEY_PREFIX + state; if (!redisTemplate.hasKey(redisKey)) { log.warn("授权回调失败,state校验不通过,state={}", state); - return "回调失败:state参数无效或已过期"; + throw exception(STATE_NOT_VALID); } // 校验通过后删除state,防止重复使用 redisTemplate.delete(redisKey); @@ -76,33 +77,34 @@ public class AgisoAuthServiceImpl implements AgisoAuthService { // 2. 处理授权失败场景 if (error != null) { log.warn("用户授权失败,error={}", error); - return "授权失败:" + error; + throw exception(USER_AUTH_FAILED); } // 3. 处理授权成功场景 if (code != null) { - try { - String accessToken = getAccessToken(code, state); - log.info("授权成功,AccessToken已存储到redis"); - return "success"; - } catch (Exception e) { - log.error("换取AccessToken失败,code={}", code, e); - return "换取AccessToken失败:" + e.getMessage(); - } - } - return "err:回调参数异常"; + //异步执行,不阻塞回调线程 + CompletableFuture.runAsync(() -> { + try { + String accessToken = generateAccessToken(code, state); + log.info("授权成功,AccessToken已存储到redis"); + } catch (Exception e) { + log.error("换取AccessToken失败,code={}", code, e); + } + }); + } + return "success"; } /** * 用授权码code换取AccessToken */ - private String getAccessToken(String code, String state) throws IOException { + private String generateAccessToken(String code, String state) throws IOException { // 1. 构建请求URL HttpUrl url = Objects.requireNonNull(HttpUrl.parse("https://alds.agiso.com/auth/token")).newBuilder() .addQueryParameter("code", code) - .addQueryParameter("appId", APPID) - .addQueryParameter("secret", APPSECRET) + .addQueryParameter("appId", agisoConfig.getAppid()) + .addQueryParameter("secret", agisoConfig.getAppsecret()) .addQueryParameter("state", state) // 使用回调的state,保持一致 .build(); log.debug("请求AccessToken的URL:{}", url); @@ -128,7 +130,7 @@ public class AgisoAuthServiceImpl implements AgisoAuthService { } // 6. 解析响应 - AccessTokenResponse tokenResponse = objectMapper.readValue(responseBody, AccessTokenResponse.class); + AgisoResponse tokenResponse = objectMapper.readValue(responseBody, AgisoResponse.class); if (!tokenResponse.isSuccess()) { throw new RuntimeException(String.format( "接口返回失败,错误码:%d,错误信息:%s", @@ -138,13 +140,13 @@ public class AgisoAuthServiceImpl implements AgisoAuthService { } // 7. 提取Token和相关信息 - AccessTokenData data = tokenResponse.getData(); + AgisoData data = tokenResponse.getData(); if (data == null || data.getToken() == null || data.getToken().isEmpty()) { throw exception(TOKEN_IS_EMPTY); } // 8. 缓存Token - String cacheKey = "agiso:token:" + data.getSellerOpenUid(); + String cacheKey = TOKEN_REDIS_KEY_PREFIX + data.getSellerOpenUid(); redisTemplate.opsForValue() .set(cacheKey, data.getToken(), data.getExpiresIn(), TimeUnit.SECONDS); log.info("AccessToken缓存成功,商家ID:{},过期时间:{}秒", data.getSellerOpenUid(), data.getExpiresIn()); @@ -152,4 +154,105 @@ public class AgisoAuthServiceImpl implements AgisoAuthService { return data.getToken(); } } + + @Override + public AgisoResponse execute(String url, String method, Map params, String sellerOpenUid) throws NoSuchAlgorithmException { + + if (params == null) params = new HashMap<>(); + + //从redis获取accessToken + String accessToken = redisTemplate.opsForValue().get(TOKEN_REDIS_KEY_PREFIX + sellerOpenUid); + if (accessToken == null) throw exception(TOKEN_AUTH_FAILED); + + //构建公共请求参数 + long timestamp = System.currentTimeMillis() / 1000; + params.put("timestamp", Long.toString(timestamp)); + params.put("sign",sign(params,agisoConfig.getAppsecret())); + + //构建公共请求头 + Map headers = new HashMap<>(); + headers.put("Authorization", "Bearer " + accessToken); + headers.put("ApiVersion", "1"); + //执行请求 + return HttpUtils.execute(url,method, headers, params); + } + + @Override + public AgisoResponse doPost(String url, Map params, String sellerOpenUid) throws NoSuchAlgorithmException { + return execute(url,"POST",params,sellerOpenUid); + } + + @Override + public AgisoResponse doGet(String url, Map params, String sellerOpenUid) throws NoSuchAlgorithmException { + return execute(url,"GET",params,sellerOpenUid); + } + + @Override + public AgisoResponse autoShipment(List tids, String sellerOpenUid) throws NoSuchAlgorithmException { + // 校验参数 + + if (tids == null || tids.isEmpty()) { + log.warn("自动发货失败,订单ID列表为空"); + AgisoResponse emptyResponse = new AgisoResponse(); + emptyResponse.setSuccess(false); + emptyResponse.setErrorCode(BAD_REQUEST); + emptyResponse.setErrorMsg("订单ID列表不能为空"); + return emptyResponse; + } + + // 拼接订单ID字符串(去除最后一个逗号) + String tidsStr = String.join(",", tids); + log.debug("自动发货处理,订单ID列表:{},商家标识:{}", tidsStr, sellerOpenUid); + + // 构建请求参数 + Map params = new HashMap<>(); + params.put("tids", tidsStr); + + // 调用POST请求方法 + return doPost(AUTO_SHIPMENTS_URL, params, sellerOpenUid); + } + + /** + * 阿奇索签名算法 + * @param params 业务参数 + * @param appSecret appsecret + * @return 签名字符串 + * @throws NoSuchAlgorithmException 未找到算法异常 + */ + @Override + public String sign(Map params, String appSecret) throws NoSuchAlgorithmException { + String[] keys = params.keySet().toArray(new String[0]); + Arrays.sort(keys); + + StringBuilder query = new StringBuilder(); + query.append(appSecret); + for (String key : keys) { + String value = params.get(key); + query.append(key).append(value); + } + query.append(appSecret); + + byte[] md5byte = encryptMD5(query.toString()); + + return byte2hex(md5byte); + } + + // byte数组转成16进制字符串 + private String byte2hex(byte[] bytes) { + StringBuilder sign = new StringBuilder(); + for (byte aByte : bytes) { + String hex = Integer.toHexString(aByte & 0xFF); + if (hex.length() == 1) { + sign.append("0"); + } + sign.append(hex.toLowerCase()); + } + return sign.toString(); + } + + // Md5摘要 + private byte[] encryptMD5(String data) throws NoSuchAlgorithmException { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + return md5.digest(data.getBytes(StandardCharsets.UTF_8)); + } } \ No newline at end of file diff --git a/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/utils/HttpUtils.java b/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/utils/HttpUtils.java new file mode 100644 index 0000000000..b692e31c86 --- /dev/null +++ b/yudao-module-kfc/src/main/java/cn/iocoder/yudao/module/kfc/utils/HttpUtils.java @@ -0,0 +1,131 @@ +package cn.iocoder.yudao.module.kfc.utils; + +import cn.iocoder.yudao.module.kfc.controller.admin.agisoproxy.vo.AgisoData; +import cn.iocoder.yudao.module.kfc.controller.admin.agisoproxy.vo.AgisoResponse; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@Slf4j +public class HttpUtils { + // 单例OkHttpClient实例 + private static final OkHttpClient client = new OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .writeTimeout(30, TimeUnit.SECONDS) + .build(); + + // 用于JSON反序列化的ObjectMapper + private static final ObjectMapper objectMapper = new ObjectMapper(); + + /** + * 通用HTTP请求执行方法,返回结构化的AgisoResponse + * @param url 请求URL + * @param method 请求方法(GET, POST, PUT, DELETE等) + * @param headers 请求头 + * @param params 请求参数 + * @return 结构化的AgisoResponse响应 + */ + public static AgisoResponse execute(String url, String method, Map headers, Map params) { + // 参数处理 + if (params == null) { + params = new HashMap<>(); + } + if (headers == null) { + headers = new HashMap<>(); + } + + // 构建请求体 + RequestBody requestBody = null; + if ("POST".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) { + FormBody.Builder formBuilder = new FormBody.Builder(); + for (Map.Entry entry : params.entrySet()) { + formBuilder.add(entry.getKey(), entry.getValue()); + } + requestBody = formBuilder.build(); + } + // GET请求参数拼接 + else if ("GET".equalsIgnoreCase(method) && !params.isEmpty()) { + url = url + (url.contains("?") ? "&" : "?") + buildQueryString(params); + } + + // 构建请求 + Request.Builder requestBuilder = new Request.Builder() + .url(url); + + // 添加请求头 + for (Map.Entry entry : headers.entrySet()) { + requestBuilder.addHeader(entry.getKey(), entry.getValue()); + } + + // 设置请求方法 + Request request; + if ("GET".equalsIgnoreCase(method)) { + request = requestBuilder.get().build(); + } else if ("DELETE".equalsIgnoreCase(method)) { + request = requestBuilder.delete().build(); + } else { + request = requestBuilder.method(method, requestBody).build(); + } + + // 执行请求并处理响应 + try (Response response = client.newCall(request).execute()) { + String responseBody = response.body() != null ? response.body().string() : ""; + log.debug("HTTP请求响应内容: {}", responseBody); + + // 尝试将响应体反序列化为AgisoResponse + if (!responseBody.isEmpty()) { + return objectMapper.readValue(responseBody, AgisoResponse.class); + } + + // 响应体为空时构建默认失败响应 + AgisoResponse emptyResponse = new AgisoResponse(); + emptyResponse.setSuccess(false); + emptyResponse.setErrorCode(response.code()); + emptyResponse.setErrorMsg("响应体为空"); + return emptyResponse; + + } catch (IOException e) { + log.error("请求执行异常:{}", e.getMessage(), e); + // 异常时构建失败响应 + AgisoResponse errorResponse = new AgisoResponse(); + errorResponse.setSuccess(false); + errorResponse.setErrorCode(500); + errorResponse.setErrorMsg("请求异常:" + e.getMessage()); + return errorResponse; + } + } + + /** + * 简化的POST请求方法,返回结构化响应 + */ + public static AgisoResponse post(String url, Map headers, Map params) { + return execute(url, "POST", headers, params); + } + + /** + * 简化的GET请求方法,返回结构化响应 + */ + public static AgisoResponse get(String url, Map headers, Map params) { + return execute(url, "GET", headers, params); + } + + /** + * 构建查询字符串 + */ + private static String buildQueryString(Map params) { + StringBuilder sb = new StringBuilder(); + for (Map.Entry entry : params.entrySet()) { + if (sb.length() > 0) { + sb.append("&"); + } + sb.append(entry.getKey()).append("=").append(entry.getValue()); + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index 9a08e2a98a..f204e0404b 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -272,6 +272,7 @@ pf4j: # pluginsDir: /tmp/ pluginsDir: ../plugins +# 阿奇索的配置 agiso: appid: appid appsecret: appsecret \ No newline at end of file