✨ 新增业财融合(BFI)API对接模块,支持令牌获取校验撤回与汇率查询

This commit is contained in:
刘泉佳
2026-09-15 17:12:07 +08:00
parent 0d1f14f703
commit ad8bea1696
22 changed files with 1533 additions and 0 deletions
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springblade</groupId>
<artifactId>blade-third-party-api</artifactId>
<version>${revision}</version>
</parent>
<artifactId>blade-bfi-api</artifactId>
<name>${project.artifactId}</name>
<packaging>jar</packaging>
<description>BladeX 业财融合(BFI)API 契约模块</description>
<dependencies>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-redis</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,54 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.config;
import feign.Request;
import feign.RequestInterceptor;
import org.springblade.thirdparty.bfi.interceptor.BfiRequestInterceptor;
import org.springframework.context.annotation.Bean;
import java.util.concurrent.TimeUnit;
/**
* 业财融合(BFI)Feign 客户端配置
* <p>
* 仅通过 {@link org.springframework.cloud.openfeign.FeignClient#configuration} 引用,避免拦截器全局生效
*
* @author Chill
*/
public class BfiFeignClientConfig {
@Bean
public RequestInterceptor requestInterceptor(BfiProperties properties) {
// 屏蔽全局拦截器 BladeFeignRequestInterceptor,仅注入 BFI 网关认证请求头
return new BfiRequestInterceptor(properties);
}
@Bean
public Request.Options options() {
return new Request.Options(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true);
}
}
@@ -0,0 +1,67 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 业财融合(BFI)配置类
*
* @author Chill
*/
@Data
@ConfigurationProperties(prefix = "third-party.bfi")
public class BfiProperties {
/**
* 业财融合(BFI)接口基础地址,如 http://ip:port/gwzh/BFI
*/
private String baseUrl;
/**
* 网关认证请求头 Authorization 的值
*/
private String authorization;
/**
* 第三方应用系统编码,即 appId
*/
private String clientId;
/**
* 第三方应用 AccessToken 认证密钥,即 appSecret
*/
private String clientSecret;
/**
* 访问用户名
*/
private String username;
/**
* 数据中心ID
*/
private String accountId;
/**
* 语言字串,默认 zh_CN
*/
private String language = "zh_CN";
}
@@ -0,0 +1,41 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* 第三方API 业财融合(BFI)配置类
*
* @author Chill
*/
@ComponentScan("org.springblade.thirdparty.bfi")
@EnableConfigurationProperties(BfiProperties.class)
@Configuration
public class ThirdPartyBfiAutoConfiguration {
}
@@ -0,0 +1,74 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.constant;
/**
* 业财融合(BFI)常量类
*
* @author Chill
*/
public class BfiConstant {
/**
* 接口调用成功错误码
*/
public static final String SUCCESS_CODE = "0";
/**
* 网关认证请求头名称
*/
public static final String HEADER_AUTHORIZATION = "Authorization";
/**
* 访问令牌请求头名称
*/
public static final String HEADER_ACCESS_TOKEN = "access_token";
/**
* 令牌类型 hint:access_token
*/
public static final String TOKEN_TYPE_HINT_ACCESS = "access_token";
/**
* 令牌类型 hint:id_token
*/
public static final String TOKEN_TYPE_HINT_ID = "id_token";
/**
* 默认语言
*/
public static final String LANGUAGE_ZH_CN = "zh_CN";
/**
* BFI access_token 缓存key
*/
public static final String BFI_TOKEN_CACHE_KEY = "blade:third:bfi:token";
/**
* token 提前失效缓冲时长(毫秒),防止刚好过期
*/
public static final long TOKEN_EXPIRE_BUFFER_MILLIS = 120_000L;
}
@@ -0,0 +1,42 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.exception;
/**
* 业财融合(BFI)接口异常
*
* @author Chill
*/
public class BfiException extends RuntimeException {
public BfiException(String message) {
super(message);
}
public BfiException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -0,0 +1,54 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.exception;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.api.ResultCode;
import org.springblade.core.tool.utils.Func;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 业财融合(BFI)异常转换器
*
* @author Chill
*/
@AllArgsConstructor
@Slf4j
@RestControllerAdvice
public class BfiExceptionTranslator {
@ExceptionHandler(BfiException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public R handleError(BfiException e) {
log.error("业财融合接口异常", e);
return R.fail(ResultCode.INTERNAL_SERVER_ERROR, (Func.isEmpty(e.getMessage()) ? ResultCode.INTERNAL_SERVER_ERROR.getMessage() : e.getMessage()));
}
}
@@ -0,0 +1,95 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.feign;
import org.springblade.thirdparty.bfi.config.BfiFeignClientConfig;
import org.springblade.thirdparty.bfi.constant.BfiConstant;
import org.springblade.thirdparty.bfi.pojo.dto.BfiExchangeRateQueryDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiTokenDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiVerifyTokenDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiWithdrawTokenDTO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiExchangeRateVO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiResultVO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiTokenVO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiVerifyTokenVO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import java.util.List;
/**
* 业财融合(BFI)接口
*
* @author Chill
*/
@FeignClient(name = "BFI", url = "${thirdParty.bfi.baseUrl}", configuration = BfiFeignClientConfig.class)
public interface IBfiClient {
String GET_TOKEN_URL = "${thirdParty.bfi.getTokenUrl:/ierp/kapi/oauth2/getToken}";
String VERIFY_TOKEN_URL = "${thirdParty.bfi.verifyTokenUrl:/ierp/kapi/oauth2/verifyToken}";
String WITHDRAW_TOKEN_URL = "${thirdParty.bfi.withdrawTokenUrl:/ierp/kapi/oauth2/withdrawToken}";
String FOREX_QUERY_URL = "${thirdParty.bfi.forexQueryUrl:/ierp/kapi/v2/awrp/base/bd_exrate_tree/Forexquery}";
/**
* A100-1 获取令牌
*
* @param param 请求参数
* @return 令牌信息
*/
@PostMapping(GET_TOKEN_URL)
BfiResultVO<BfiTokenVO> getToken(@RequestBody BfiTokenDTO param);
/**
* A100-2 校验令牌
*
* @param param 请求参数
* @return 校验结果
*/
@PostMapping(VERIFY_TOKEN_URL)
BfiResultVO<BfiVerifyTokenVO> verifyToken(@RequestBody BfiVerifyTokenDTO param);
/**
* A100-3 撤回令牌
*
* @param param 请求参数
* @return 撤回结果
*/
@PostMapping(WITHDRAW_TOKEN_URL)
BfiResultVO<Boolean> withdrawToken(@RequestBody BfiWithdrawTokenDTO param);
/**
* A801-4 汇率查询
*
* @param accessToken 访问令牌,通过 getToken 获取
* @param param 请求参数
* @return 汇率列表
*/
@GetMapping(FOREX_QUERY_URL)
BfiResultVO<List<BfiExchangeRateVO>> forexQuery(@RequestHeader(BfiConstant.HEADER_ACCESS_TOKEN) String accessToken, @RequestBody BfiExchangeRateQueryDTO param);
}
@@ -0,0 +1,48 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.interceptor;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.AllArgsConstructor;
import org.springblade.thirdparty.bfi.config.BfiProperties;
import org.springblade.thirdparty.bfi.constant.BfiConstant;
/**
* 业财融合(BFI)请求拦截器,统一注入网关认证请求头
*
* @author Chill
*/
@AllArgsConstructor
public class BfiRequestInterceptor implements RequestInterceptor {
private final BfiProperties properties;
@Override
public void apply(RequestTemplate template) {
template.header(BfiConstant.HEADER_AUTHORIZATION, properties.getAuthorization());
}
}
@@ -0,0 +1,54 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.pojo.dto;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 业财融合(BFI)请求基础参数,统一携带随机数与时间戳
*
* @author Chill
*/
@Data
public class BfiBaseDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 随机数,不可以重复
*/
private String nonce;
/**
* 时间戳,格式:yyyy-MM-dd HH:mm:ss,服务端校验当前时间前后5分钟
*/
private String timestamp;
/**
* 语言字串:zh_CN、zh_TW、en_US等,默认 zh_CN
*/
private String language;
}
@@ -0,0 +1,50 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.pojo.dto;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 业财融合(BFI)A801-4 汇率查询参数
*
* @author Chill
*/
@Data
public class BfiExchangeRateQueryDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 生效日期-开始,格式:yyyy-MM-dd
*/
private String effectdate;
/**
* 生效日期-结束,格式:yyyy-MM-dd
*/
private String effectdateE;
}
@@ -0,0 +1,63 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.pojo.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.io.Serializable;
/**
* 业财融合(BFI)A100-1 获取令牌参数
*
* @author Chill
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class BfiTokenDTO extends BfiBaseDTO {
@Serial
private static final long serialVersionUID = 1L;
/**
* 第三方应用系统编码,即 appId
*/
@JsonProperty("client_id")
private String clientId;
/**
* 第三方应用 AccessToken 认证密钥,即 appSecret
*/
@JsonProperty("client_secret")
private String clientSecret;
/**
* 访问用户名
*/
private String username;
/**
* 数据中心ID
*/
private String accountId;
}
@@ -0,0 +1,63 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.pojo.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.io.Serializable;
/**
* 业财融合(BFI)A100-2 校验令牌参数
*
* @author Chill
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class BfiVerifyTokenDTO extends BfiBaseDTO {
@Serial
private static final long serialVersionUID = 1L;
/**
* 第三方应用系统编码,即 appId
*/
@JsonProperty("client_id")
private String clientId;
/**
* 令牌类型,access_token/id_token
*/
@JsonProperty("token_type_hint")
private String tokenTypeHint;
/**
* 令牌值
*/
private String token;
/**
* 数据中心ID
*/
private String accountId;
}
@@ -0,0 +1,68 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.pojo.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.io.Serializable;
/**
* 业财融合(BFI)A100-3 撤回令牌参数
*
* @author Chill
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class BfiWithdrawTokenDTO extends BfiBaseDTO {
@Serial
private static final long serialVersionUID = 1L;
/**
* 第三方应用系统编码,即 appId
*/
@JsonProperty("client_id")
private String clientId;
/**
* 第三方应用 AccessToken 认证密钥,即 appSecret
*/
@JsonProperty("client_secret")
private String clientSecret;
/**
* 令牌类型,access_token/id_token
*/
@JsonProperty("token_type_hint")
private String tokenTypeHint;
/**
* 令牌值
*/
private String token;
/**
* 数据中心ID
*/
private String accountId;
}
@@ -0,0 +1,154 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.pojo.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 业财融合(BFI)A801-4 汇率查询返回结果
*
* @author Chill
*/
@Data
public class BfiExchangeRateVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
/**
* 编码
*/
private String number;
/**
* 名称
*/
private String name;
/**
* 数据状态[A:暂存, B:已提交, C:已审核]
*/
private String status;
/**
* 数据状态标题
*/
@JsonProperty("status_title")
private String statusTitle;
/**
* 使用状态[0:禁用, 1:可用]
*/
private String enable;
/**
* 使用状态标题
*/
@JsonProperty("enable_title")
private String enableTitle;
/**
* 创建时间
*/
private String createtime;
/**
* 修改时间
*/
private String modifytime;
/**
* 主数据内码
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long masterid;
/**
* 禁用时间
*/
private String disabledate;
/**
* 汇率类型[1:固定汇率, 2:浮动汇率]
*/
private String exctype;
/**
* 汇率类型标题
*/
@JsonProperty("exctype_title")
private String exctypeTitle;
/**
* 直接汇率值
*/
private BigDecimal excval;
/**
* 外币牌价[1:买入价, 2:中间价, 3:卖出价]
*/
private String foreignexcid;
/**
* 外币牌价标题
*/
@JsonProperty("foreignexcid_title")
private String foreignexcidTitle;
/**
* 生效日期
*/
private String effectdate;
/**
* 换算方式[1:直接汇率, 2:间接汇率]
*/
private String convertmode;
/**
* 换算方式标题
*/
@JsonProperty("convertmode_title")
private String convertmodeTitle;
/**
* 数据源[FX_001:人民币中间价, FX_002:交叉汇率, FX_REF:人民币参考汇率]
*/
private String datasource;
/**
* 数据源标题
*/
@JsonProperty("datasource_title")
private String datasourceTitle;
/**
* 失效日期
*/
private String expirydate;
/**
* 间接汇率值
*/
private BigDecimal indirectexrate;
/**
* 显示精度参数
*/
private Integer precision;
/**
* 计算规则
*/
private String computationrules;
}
@@ -0,0 +1,70 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.pojo.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import org.springblade.thirdparty.bfi.constant.BfiConstant;
import java.io.Serial;
import java.io.Serializable;
/**
* 业财融合(BFI)统一返回结果
*
* @author Chill
*/
@Data
public class BfiResultVO<T> implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 数据
*/
private T data;
/**
* 错误码,成功时为0
*/
@JsonProperty("errorCode")
private String errorCode;
/**
* 错误消息
*/
private String message;
/**
* 是否成功,true或false
*/
private Boolean status;
/**
* 是否调用成功
*
* @return true-成功
*/
public boolean isSuccess() {
return Boolean.TRUE.equals(status) && BfiConstant.SUCCESS_CODE.equals(errorCode);
}
}
@@ -0,0 +1,81 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.pojo.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 业财融合(BFI)A100-1 获取令牌返回结果
*
* @author Chill
*/
@Data
public class BfiTokenVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 请求令牌,第三方应用调用 OpenAPI 的凭证,有效期2小时
*/
@JsonProperty("access_token")
private String accessToken;
/**
* 令牌类型,默认为 Bearer
*/
@JsonProperty("token_type")
private String tokenType;
/**
* 刷新令牌,暂不支持
*/
@JsonProperty("refresh_token")
private String refreshToken;
/**
* 作用域,用户授权给第三方的权限,默认为API
*/
private String scope;
/**
* access_token 的有效期,即距离过期的剩余时长(单位:毫秒)
*/
@JsonProperty("expires_in")
private Long expiresIn;
/**
* JWT(Json Web Token),此处可忽略
*/
@JsonProperty("id_token")
private String idToken;
/**
* JWT 的有效期,即距离过期的剩余时长(单位:毫秒)
*/
@JsonProperty("id_token_expires_in")
private Long idTokenExpiresIn;
/**
* 语言
*/
private String language;
}
@@ -0,0 +1,56 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.pojo.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 业财融合(BFI)A100-2 校验令牌返回结果
*
* @author Chill
*/
@Data
public class BfiVerifyTokenVO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 令牌的有效期,即距离过期的剩余时长(单位:毫秒)
*/
@JsonProperty("expires_in")
private Long expiresIn;
/**
* 令牌是否有效,true/false
*/
private Boolean active;
/**
* 作用域,用户授权给第三方的权限,默认为API
*/
private String scope;
}
@@ -0,0 +1,89 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.service;
import org.springblade.thirdparty.bfi.pojo.vo.BfiExchangeRateVO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiTokenVO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiVerifyTokenVO;
import java.time.LocalDate;
import java.util.List;
/**
* 业财融合(BFI)服务
*
* @author Chill
*/
public interface IBfiService {
/**
* A100-1 获取令牌,每次调用实时请求远端并刷新缓存
*
* @return 令牌信息,含 access_token、refresh_token、expires_in 等
*/
BfiTokenVO getToken();
/**
* 获取访问令牌,优先取 Redis 缓存,过期后自动重新获取
*
* @return access_token
*/
String getAccessToken();
/**
* A100-2 校验令牌有效性
*
* @param token 待校验的令牌值
* @return 校验结果,含 expires_in、active、scope
*/
BfiVerifyTokenVO verifyToken(String token);
/**
* A100-3 撤回令牌
*
* @param token 待撤回的令牌值
* @return true-撤回成功
*/
boolean withdrawToken(String token);
/**
* A801-4 汇率查询
*
* @param effectdate 生效日期-开始,格式:yyyy-MM-dd
* @param effectdateE 生效日期-结束,格式:yyyy-MM-dd
* @return 汇率列表
*/
List<BfiExchangeRateVO> queryExchangeRate(String effectdate, String effectdateE);
/**
* A801-4 汇率查询
*
* @param effectdate 生效日期-开始
* @param effectdateE 生效日期-结束
* @return 汇率列表
*/
List<BfiExchangeRateVO> queryExchangeRate(LocalDate effectdate, LocalDate effectdateE);
}
@@ -0,0 +1,204 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.service.impl;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.thirdparty.bfi.config.BfiProperties;
import org.springblade.thirdparty.bfi.constant.BfiConstant;
import org.springblade.thirdparty.bfi.exception.BfiException;
import org.springblade.thirdparty.bfi.feign.IBfiClient;
import org.springblade.thirdparty.bfi.pojo.dto.BfiExchangeRateQueryDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiTokenDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiVerifyTokenDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiWithdrawTokenDTO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiExchangeRateVO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiResultVO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiTokenVO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiVerifyTokenVO;
import org.springblade.thirdparty.bfi.service.IBfiService;
import org.springblade.thirdparty.bfi.util.BfiUtil;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
/**
* 业财融合(BFI)服务实现
*
* @author Chill
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class BfiServiceImpl implements IBfiService {
/**
* 汇率查询日期格式
*/
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private final BfiProperties properties;
private final IBfiClient client;
private final BladeRedis bladeRedis;
@Override
public BfiTokenVO getToken() {
BfiTokenDTO param = new BfiTokenDTO();
param.setClientId(properties.getClientId());
param.setClientSecret(properties.getClientSecret());
param.setUsername(properties.getUsername());
param.setAccountId(properties.getAccountId());
param.setLanguage(properties.getLanguage());
BfiUtil.fillRandomParam(param);
long start = System.currentTimeMillis();
log.info("调用BFI接口 获取令牌开始");
BfiResultVO<BfiTokenVO> result = commonCall("获取令牌", () -> client.getToken(param));
log.info("调用BFI接口 获取令牌结束 耗时:{}", System.currentTimeMillis() - start);
BfiTokenVO tokenVO = Optional.ofNullable(result).map(BfiResultVO::getData).orElse(null);
if (tokenVO == null || StringUtil.isBlank(tokenVO.getAccessToken())) {
log.error("调用BFI接口 获取令牌返回数据为空:{}", JSONUtil.toJsonStr(result));
throw new BfiException("调用BFI接口获取令牌异常,返回数据为空");
}
// 缓存令牌,过期前提前失效
cacheToken(tokenVO);
return tokenVO;
}
@Override
public String getAccessToken() {
String token = bladeRedis.get(BfiConstant.BFI_TOKEN_CACHE_KEY);
if (token != null) {
return token;
}
return getToken().getAccessToken();
}
@Override
public BfiVerifyTokenVO verifyToken(String token) {
if (StringUtil.isBlank(token)) {
throw new BfiException("校验BFI令牌失败,令牌值不能为空");
}
BfiVerifyTokenDTO param = new BfiVerifyTokenDTO();
param.setClientId(properties.getClientId());
param.setAccountId(properties.getAccountId());
param.setTokenTypeHint(BfiConstant.TOKEN_TYPE_HINT_ACCESS);
param.setToken(token);
param.setLanguage(properties.getLanguage());
BfiUtil.fillRandomParam(param);
BfiResultVO<BfiVerifyTokenVO> result = commonCall("校验令牌", () -> client.verifyToken(param));
return result.getData();
}
@Override
public boolean withdrawToken(String token) {
if (StringUtil.isBlank(token)) {
throw new BfiException("撤回BFI令牌失败,令牌值不能为空");
}
BfiWithdrawTokenDTO param = new BfiWithdrawTokenDTO();
param.setClientId(properties.getClientId());
param.setClientSecret(properties.getClientSecret());
param.setAccountId(properties.getAccountId());
param.setTokenTypeHint(BfiConstant.TOKEN_TYPE_HINT_ACCESS);
param.setToken(token);
param.setLanguage(properties.getLanguage());
BfiUtil.fillRandomParam(param);
BfiResultVO<Boolean> result = commonCall("撤回令牌", () -> client.withdrawToken(param));
// 撤回的令牌与缓存一致时移除本地缓存
if (Boolean.TRUE.equals(result.getData()) && token.equals(bladeRedis.get(BfiConstant.BFI_TOKEN_CACHE_KEY))) {
bladeRedis.del(BfiConstant.BFI_TOKEN_CACHE_KEY);
}
return Boolean.TRUE.equals(result.getData());
}
@Override
public List<BfiExchangeRateVO> queryExchangeRate(String effectdate, String effectdateE) {
if (StringUtil.isBlank(effectdate) || StringUtil.isBlank(effectdateE)) {
throw new BfiException("查询BFI汇率失败,生效日期不能为空");
}
BfiExchangeRateQueryDTO param = new BfiExchangeRateQueryDTO();
param.setEffectdate(effectdate);
param.setEffectdateE(effectdateE);
String accessToken = getAccessToken();
BfiResultVO<List<BfiExchangeRateVO>> result = commonCall("汇率查询", () -> client.forexQuery(accessToken, param));
return result.getData();
}
@Override
public List<BfiExchangeRateVO> queryExchangeRate(LocalDate effectdate, LocalDate effectdateE) {
if (effectdate == null || effectdateE == null) {
throw new BfiException("查询BFI汇率失败,生效日期不能为空");
}
return queryExchangeRate(effectdate.format(DATE_FORMATTER), effectdateE.format(DATE_FORMATTER));
}
/**
* 公共调用方法,统一校验响应结果
*
* @param type 调用类型描述
* @param supplier 实际调用
* @param <T> 响应数据类型
* @return 响应结果
*/
private <T> BfiResultVO<T> commonCall(String type, Supplier<BfiResultVO<T>> supplier) {
try {
BfiResultVO<T> result = supplier.get();
if (result != null && result.isSuccess()) {
return result;
}
log.error("调用BFI接口 {}异常响应参数:{}", type, JSONUtil.toJsonStr(result));
String message = Optional.ofNullable(result).map(BfiResultVO::getMessage).orElse("");
throw new BfiException("调用BFI接口" + type + "异常 " + message);
} catch (BfiException e) {
throw e;
} catch (Exception e) {
log.error("调用BFI接口 {}异常", type, e);
throw new BfiException("调用BFI接口" + type + "异常", e);
}
}
/**
* 缓存令牌,按 expires_in 毫秒数设置提前失效时间
*
* @param tokenVO 令牌信息
*/
private void cacheToken(BfiTokenVO tokenVO) {
if (tokenVO.getExpiresIn() == null || tokenVO.getExpiresIn() <= BfiConstant.TOKEN_EXPIRE_BUFFER_MILLIS) {
return;
}
// 过期时间 - 缓冲时长,防止刚好过期
long expireMillis = tokenVO.getExpiresIn() - BfiConstant.TOKEN_EXPIRE_BUFFER_MILLIS;
bladeRedis.getValueOps().setIfAbsent(BfiConstant.BFI_TOKEN_CACHE_KEY, tokenVO.getAccessToken(), Duration.ofMillis(expireMillis));
}
}
@@ -0,0 +1,82 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.thirdparty.bfi.util;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.thirdparty.bfi.pojo.dto.BfiBaseDTO;
import java.util.Date;
import java.util.UUID;
/**
* 业财融合(BFI)接口通用参数工具类
* <p>
* BFI 开放接口要求每次请求携带不可重复的随机数 nonce 以及服务端时间戳 timestamp,
* 后续新增接口统一继承 {@link BfiBaseDTO} 并调用 {@link #fillRandomParam(BfiBaseDTO)} 填充
*
* @author Chill
*/
public class BfiUtil {
/**
* 时间戳格式:yyyy-MM-dd HH:mm:ss
*/
public static final String TIMESTAMP_PATTERN = "yyyy-MM-dd HH:mm:ss";
private BfiUtil() {
}
/**
* 生成随机数,每次请求不可重复
*
* @return 随机数字符串
*/
public static String nonce() {
return UUID.randomUUID().toString();
}
/**
* 生成当前时间戳,服务端校验当前时间前后5分钟
*
* @return yyyy-MM-dd HH:mm:ss 格式时间字符串
*/
public static String timestamp() {
return DateUtil.formatDateTime(new Date());
}
/**
* 填充随机数与时间戳,每次调用均生成新值
*
* @param param 业务请求参数
* @param <T> 参数类型
* @return 填充后的参数
*/
public static <T extends BfiBaseDTO> T fillRandomParam(T param) {
param.setNonce(nonce());
param.setTimestamp(timestamp());
return param;
}
}
@@ -0,0 +1 @@
org.springblade.thirdparty.bfi.config.ThirdPartyBfiAutoConfiguration