Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yd-base
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
xingmin
yd-base
Commits
41e4a56d
Commit
41e4a56d
authored
Jan 13, 2026
by
zhangxingmin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
push
parent
1f199e88
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
1274 additions
and
6 deletions
+1274
-6
yd-base-api/src/main/java/com/yd/base/api/controller/ApiExchangeRateController.java
+79
-0
yd-base-api/src/main/java/com/yd/base/api/service/ApiExchangeRateService.java
+21
-0
yd-base-api/src/main/java/com/yd/base/api/service/impl/ApiExchangeRateServiceImpl.java
+455
-0
yd-base-api/src/main/java/com/yd/base/api/service/impl/ApiRelFieldValueServiceImpl.java
+5
-5
yd-base-feign/src/main/java/com/yd/base/feign/client/exchangerate/ApiExchangeRateFeignClient.java
+65
-0
yd-base-feign/src/main/java/com/yd/base/feign/fallback/exchangerate/ApiExchangeRateFeignFallbackFactory.java
+49
-0
yd-base-feign/src/main/java/com/yd/base/feign/request/exchangerate/ApiExchangeRateConvertRequest.java
+80
-0
yd-base-feign/src/main/java/com/yd/base/feign/response/exchangerate/ApiExchangeRateConvertResponse.java
+211
-0
yd-base-service/src/main/java/com/yd/base/service/dao/ExchangeRateMapper.java
+16
-0
yd-base-service/src/main/java/com/yd/base/service/model/ExchangeRate.java
+130
-0
yd-base-service/src/main/java/com/yd/base/service/service/IExchangeRateService.java
+64
-0
yd-base-service/src/main/java/com/yd/base/service/service/impl/ExchangeRateServiceImpl.java
+93
-0
yd-base-service/src/main/java/com/yd/base/service/utils/MyBatisPlusCodeGenerator.java
+1
-1
yd-base-service/src/main/resources/mappers/ExchangeRateMapper.xml
+5
-0
No files found.
yd-base-api/src/main/java/com/yd/base/api/controller/ApiExchangeRateController.java
0 → 100644
View file @
41e4a56d
package
com
.
yd
.
base
.
api
.
controller
;
import
com.yd.base.api.service.ApiExchangeRateService
;
import
com.yd.base.feign.client.exchangerate.ApiExchangeRateFeignClient
;
import
com.yd.base.feign.request.exchangerate.ApiExchangeRateConvertRequest
;
import
com.yd.base.feign.response.exchangerate.ApiExchangeRateConvertResponse
;
import
com.yd.common.result.Result
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.validation.annotation.Validated
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
java.math.BigDecimal
;
import
java.util.List
;
/**
* 汇率信息
*
* @author zxm
* @since 2025-12-29
*/
@RestController
@RequestMapping
(
"/exchangeRate"
)
@Validated
public
class
ApiExchangeRateController
implements
ApiExchangeRateFeignClient
{
@Autowired
private
ApiExchangeRateService
apiExchangeRateService
;
/**
* 货币转换
* @param request
* @return
*/
@Override
public
Result
<
ApiExchangeRateConvertResponse
>
convert
(
ApiExchangeRateConvertRequest
request
)
{
return
apiExchangeRateService
.
convert
(
request
);
}
/**
* 获取汇率
* @param baseCurrency 基础币种
* @param targetCurrency 目标币种
* @param date
* @return
*/
@Override
public
Result
<
BigDecimal
>
getExchangeRate
(
String
baseCurrency
,
String
targetCurrency
,
String
date
)
{
return
apiExchangeRateService
.
getExchangeRate
(
baseCurrency
,
targetCurrency
,
date
);
}
/**
* 批量转换
* @param requests
* @return
*/
@Override
public
Result
<
List
<
ApiExchangeRateConvertResponse
>>
batchConvert
(
List
<
ApiExchangeRateConvertRequest
>
requests
)
{
return
apiExchangeRateService
.
batchConvert
(
requests
);
}
/**
* 验证币种是否支持
* @param currency
* @return
*/
@Override
public
Result
<
Boolean
>
isCurrencySupportedResult
(
String
currency
)
{
return
apiExchangeRateService
.
isCurrencySupportedResult
(
currency
);
}
/**
* 获取支持的币种列表
* @return
*/
@Override
public
Result
<
List
<
String
>>
getSupportedCurrencies
()
{
return
apiExchangeRateService
.
getSupportedCurrencies
();
}
}
yd-base-api/src/main/java/com/yd/base/api/service/ApiExchangeRateService.java
0 → 100644
View file @
41e4a56d
package
com
.
yd
.
base
.
api
.
service
;
import
com.yd.base.feign.request.exchangerate.ApiExchangeRateConvertRequest
;
import
com.yd.base.feign.response.exchangerate.ApiExchangeRateConvertResponse
;
import
com.yd.common.result.Result
;
import
java.math.BigDecimal
;
import
java.util.List
;
public
interface
ApiExchangeRateService
{
Result
<
List
<
String
>>
getSupportedCurrencies
();
Result
<
ApiExchangeRateConvertResponse
>
convert
(
ApiExchangeRateConvertRequest
request
);
Result
<
BigDecimal
>
getExchangeRate
(
String
baseCurrency
,
String
targetCurrency
,
String
date
);
Result
<
List
<
ApiExchangeRateConvertResponse
>>
batchConvert
(
List
<
ApiExchangeRateConvertRequest
>
requests
);
Result
<
Boolean
>
isCurrencySupportedResult
(
String
currency
);
}
yd-base-api/src/main/java/com/yd/base/api/service/impl/ApiExchangeRateServiceImpl.java
0 → 100644
View file @
41e4a56d
package
com
.
yd
.
base
.
api
.
service
.
impl
;
import
com.yd.base.api.service.ApiExchangeRateService
;
import
com.yd.base.feign.request.exchangerate.ApiExchangeRateConvertRequest
;
import
com.yd.base.feign.response.exchangerate.ApiExchangeRateConvertResponse
;
import
com.yd.base.service.model.ExchangeRate
;
import
com.yd.base.service.service.IExchangeRateService
;
import
com.yd.common.exception.BusinessException
;
import
com.yd.common.result.Result
;
import
lombok.extern.slf4j.Slf4j
;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
org.springframework.util.CollectionUtils
;
import
java.math.BigDecimal
;
import
java.math.RoundingMode
;
import
java.text.SimpleDateFormat
;
import
java.time.LocalDate
;
import
java.time.format.DateTimeFormatter
;
import
java.time.format.DateTimeParseException
;
import
java.util.*
;
@Slf4j
@Service
public
class
ApiExchangeRateServiceImpl
implements
ApiExchangeRateService
{
@Autowired
private
IExchangeRateService
iExchangeRateService
;
private
static
final
String
DEFAULT_BASE_CURRENCY
=
"USD"
;
private
static
final
DateTimeFormatter
DATE_FORMATTER
=
DateTimeFormatter
.
ofPattern
(
"yyyy-MM-dd"
);
private
static
final
SimpleDateFormat
SIMPLE_DATE_FORMAT
=
new
SimpleDateFormat
(
"yyyy-MM-dd"
);
/**
* 货币转换
* @param request
* @return
*/
@Override
public
Result
<
ApiExchangeRateConvertResponse
>
convert
(
ApiExchangeRateConvertRequest
request
)
{
try
{
log
.
info
(
"货币转换请求: {}"
,
request
);
// 参数验证
if
(
request
.
getAmount
()
==
null
)
{
return
Result
.
fail
(
"金额不能为空"
);
}
if
(
StringUtils
.
isBlank
(
request
.
getFromCurrency
()))
{
return
Result
.
fail
(
"源币种不能为空"
);
}
if
(
StringUtils
.
isBlank
(
request
.
getToCurrency
()))
{
return
Result
.
fail
(
"目标币种不能为空"
);
}
// 验证币种是否支持
if
(!
isCurrencySupported
(
request
.
getFromCurrency
()))
{
return
Result
.
fail
(
"源币种不支持: "
+
request
.
getFromCurrency
());
}
if
(!
isCurrencySupported
(
request
.
getToCurrency
()))
{
return
Result
.
fail
(
"目标币种不支持: "
+
request
.
getToCurrency
());
}
// 获取转换日期
LocalDate
effectiveDate
=
getEffectiveDate
(
request
.
getTransactionDate
(),
request
.
getDate
());
// 执行转换
BigDecimal
convertedAmount
=
convertAmount
(
request
.
getAmount
(),
request
.
getFromCurrency
(),
request
.
getToCurrency
(),
effectiveDate
);
// 获取汇率
BigDecimal
exchangeRate
=
getExchangeRate
(
request
.
getFromCurrency
(),
request
.
getToCurrency
(),
effectiveDate
);
// 构建响应
ApiExchangeRateConvertResponse
response
=
new
ApiExchangeRateConvertResponse
();
response
.
setOriginalAmount
(
request
.
getAmount
());
response
.
setOriginalCurrency
(
request
.
getFromCurrency
());
response
.
setConvertedAmount
(
convertedAmount
);
response
.
setTargetCurrency
(
request
.
getToCurrency
());
response
.
setExchangeRate
(
exchangeRate
);
response
.
setTransactionDate
(
request
.
getTransactionDate
());
log
.
info
(
"货币转换结果: {} {} = {} {}, 汇率: {}"
,
request
.
getAmount
(),
request
.
getFromCurrency
(),
convertedAmount
,
request
.
getToCurrency
(),
exchangeRate
);
return
Result
.
success
(
response
);
}
catch
(
BusinessException
e
)
{
log
.
error
(
"货币转换业务异常"
,
e
);
return
Result
.
fail
(
e
.
getMessage
());
}
catch
(
Exception
e
)
{
log
.
error
(
"货币转换系统异常"
,
e
);
return
Result
.
fail
(
"货币转换失败: "
+
e
.
getMessage
());
}
}
/**
* 获取汇率
* @param baseCurrency 基础币种
* @param targetCurrency 目标币种
* @param date
* @return
*/
@Override
public
Result
<
BigDecimal
>
getExchangeRate
(
String
baseCurrency
,
String
targetCurrency
,
String
date
)
{
try
{
log
.
info
(
"获取汇率: {} -> {}, date: {}"
,
baseCurrency
,
targetCurrency
,
date
);
if
(
StringUtils
.
isBlank
(
baseCurrency
))
{
return
Result
.
fail
(
"基础币种不能为空"
);
}
if
(
StringUtils
.
isBlank
(
targetCurrency
))
{
return
Result
.
fail
(
"目标币种不能为空"
);
}
// 解析日期
LocalDate
effectiveDate
=
parseDate
(
date
);
BigDecimal
rate
=
getExchangeRate
(
baseCurrency
,
targetCurrency
,
effectiveDate
);
if
(
rate
==
null
)
{
return
Result
.
fail
(
"汇率不存在"
);
}
return
Result
.
success
(
rate
);
}
catch
(
BusinessException
e
)
{
log
.
error
(
"获取汇率业务异常"
,
e
);
return
Result
.
fail
(
e
.
getMessage
());
}
catch
(
Exception
e
)
{
log
.
error
(
"获取汇率系统异常"
,
e
);
return
Result
.
fail
(
"获取汇率失败: "
+
e
.
getMessage
());
}
}
/**
* 批量转换
* @param requests
* @return
*/
@Override
public
Result
<
List
<
ApiExchangeRateConvertResponse
>>
batchConvert
(
List
<
ApiExchangeRateConvertRequest
>
requests
)
{
try
{
log
.
info
(
"批量货币转换: {}条记录"
,
requests
==
null
?
0
:
requests
.
size
());
if
(
CollectionUtils
.
isEmpty
(
requests
))
{
return
Result
.
success
(
new
ArrayList
<>());
}
List
<
ApiExchangeRateConvertResponse
>
responses
=
new
ArrayList
<>();
for
(
ApiExchangeRateConvertRequest
request
:
requests
)
{
try
{
// 参数验证
if
(
request
.
getAmount
()
==
null
||
StringUtils
.
isBlank
(
request
.
getFromCurrency
())
||
StringUtils
.
isBlank
(
request
.
getToCurrency
()))
{
log
.
warn
(
"批量转换中跳过无效请求: {}"
,
request
);
continue
;
}
// 获取转换日期
LocalDate
effectiveDate
=
getEffectiveDate
(
request
.
getTransactionDate
(),
request
.
getDate
());
// 执行转换
BigDecimal
convertedAmount
=
convertAmount
(
request
.
getAmount
(),
request
.
getFromCurrency
(),
request
.
getToCurrency
(),
effectiveDate
);
// 获取汇率
BigDecimal
exchangeRate
=
getExchangeRate
(
request
.
getFromCurrency
(),
request
.
getToCurrency
(),
effectiveDate
);
// 构建响应
ApiExchangeRateConvertResponse
response
=
new
ApiExchangeRateConvertResponse
();
response
.
setOriginalAmount
(
request
.
getAmount
());
response
.
setOriginalCurrency
(
request
.
getFromCurrency
());
response
.
setConvertedAmount
(
convertedAmount
);
response
.
setTargetCurrency
(
request
.
getToCurrency
());
response
.
setExchangeRate
(
exchangeRate
);
response
.
setTransactionDate
(
request
.
getTransactionDate
());
responses
.
add
(
response
);
}
catch
(
Exception
e
)
{
log
.
error
(
"批量转换中单条转换失败: {}"
,
request
,
e
);
// 跳过失败记录
}
}
return
Result
.
success
(
responses
);
}
catch
(
Exception
e
)
{
log
.
error
(
"批量货币转换失败"
,
e
);
return
Result
.
fail
(
"批量货币转换失败: "
+
e
.
getMessage
());
}
}
/**
* 验证币种是否支持
* @param currency
* @return
*/
@Override
public
Result
<
Boolean
>
isCurrencySupportedResult
(
String
currency
)
{
try
{
log
.
info
(
"验证币种是否支持: {}"
,
currency
);
if
(
StringUtils
.
isBlank
(
currency
))
{
return
Result
.
success
(
false
);
}
boolean
isSupported
=
isCurrencySupported
(
currency
);
return
Result
.
success
(
isSupported
);
}
catch
(
Exception
e
)
{
log
.
error
(
"验证币种支持失败"
,
e
);
return
Result
.
fail
(
"验证币种支持失败: "
+
e
.
getMessage
());
}
}
/**
* 获取支持的币种列表
* @return
*/
@Override
public
Result
<
List
<
String
>>
getSupportedCurrencies
()
{
try
{
log
.
info
(
"获取支持的币种列表"
);
List
<
String
>
currencies
=
getSupportedCurrenciesList
();
return
Result
.
success
(
currencies
);
}
catch
(
Exception
e
)
{
log
.
error
(
"获取支持币种列表失败"
,
e
);
return
Result
.
fail
(
"获取支持币种列表失败: "
+
e
.
getMessage
());
}
}
// ==================== 业务逻辑方法 ====================
/**
* 货币转换(业务逻辑)
*/
private
BigDecimal
convertAmount
(
BigDecimal
amount
,
String
fromCurrency
,
String
toCurrency
,
LocalDate
effectiveDate
)
{
if
(
amount
==
null
)
{
return
BigDecimal
.
ZERO
;
}
if
(
StringUtils
.
isBlank
(
fromCurrency
)
||
StringUtils
.
isBlank
(
toCurrency
))
{
throw
new
BusinessException
(
"币种代码不能为空"
);
}
// 如果币种相同,直接返回
if
(
fromCurrency
.
equalsIgnoreCase
(
toCurrency
))
{
return
amount
;
}
// 获取汇率
BigDecimal
exchangeRate
=
getExchangeRate
(
fromCurrency
,
toCurrency
,
effectiveDate
);
// 计算转换后的金额
BigDecimal
convertedAmount
=
amount
.
multiply
(
exchangeRate
);
// 设置精度(保留2位小数,银行家舍入法)
return
convertedAmount
.
setScale
(
2
,
RoundingMode
.
HALF_EVEN
);
}
/**
* 获取汇率(业务逻辑)
*/
private
BigDecimal
getExchangeRate
(
String
baseCurrency
,
String
targetCurrency
,
LocalDate
effectiveDate
)
{
if
(
StringUtils
.
isBlank
(
baseCurrency
)
||
StringUtils
.
isBlank
(
targetCurrency
))
{
throw
new
BusinessException
(
"币种代码不能为空"
);
}
// 如果币种相同,返回1
if
(
baseCurrency
.
equalsIgnoreCase
(
targetCurrency
))
{
return
BigDecimal
.
ONE
;
}
// 1. 尝试获取直接汇率
BigDecimal
directRate
=
getDirectExchangeRate
(
baseCurrency
,
targetCurrency
,
effectiveDate
);
if
(
directRate
!=
null
)
{
return
directRate
;
}
// 2. 尝试获取反向汇率
BigDecimal
reverseRate
=
getDirectExchangeRate
(
targetCurrency
,
baseCurrency
,
effectiveDate
);
if
(
reverseRate
!=
null
)
{
// 计算反向汇率的倒数
return
BigDecimal
.
ONE
.
divide
(
reverseRate
,
8
,
RoundingMode
.
HALF_UP
);
}
// 3. 尝试通过中间货币计算交叉汇率
return
calculateCrossRate
(
baseCurrency
,
targetCurrency
,
effectiveDate
);
}
/**
* 获取直接汇率
*/
private
BigDecimal
getDirectExchangeRate
(
String
baseCurrency
,
String
targetCurrency
,
LocalDate
effectiveDate
)
{
ExchangeRate
exchangeRate
;
if
(
effectiveDate
!=
null
)
{
// 查询指定日期的汇率
exchangeRate
=
iExchangeRateService
.
getByCurrencyAndDate
(
baseCurrency
,
targetCurrency
,
effectiveDate
);
}
else
{
// 查询最新汇率
exchangeRate
=
iExchangeRateService
.
getLatestByCurrency
(
baseCurrency
,
targetCurrency
);
}
return
exchangeRate
!=
null
?
exchangeRate
.
getExchangeRate
()
:
null
;
}
/**
* 计算交叉汇率
*/
private
BigDecimal
calculateCrossRate
(
String
baseCurrency
,
String
targetCurrency
,
LocalDate
effectiveDate
)
{
log
.
debug
(
"尝试计算交叉汇率: {} -> {}"
,
baseCurrency
,
targetCurrency
);
try
{
// 通过默认基础货币(如USD)计算交叉汇率
// 获取 baseCurrency -> USD 的汇率
BigDecimal
baseToUsdRate
;
if
(
baseCurrency
.
equals
(
DEFAULT_BASE_CURRENCY
))
{
baseToUsdRate
=
BigDecimal
.
ONE
;
}
else
{
BigDecimal
rate
=
getDirectExchangeRate
(
baseCurrency
,
DEFAULT_BASE_CURRENCY
,
effectiveDate
);
if
(
rate
!=
null
)
{
baseToUsdRate
=
rate
;
}
else
{
// 尝试反向
BigDecimal
reverseRate
=
getDirectExchangeRate
(
DEFAULT_BASE_CURRENCY
,
baseCurrency
,
effectiveDate
);
if
(
reverseRate
!=
null
)
{
baseToUsdRate
=
BigDecimal
.
ONE
.
divide
(
reverseRate
,
8
,
RoundingMode
.
HALF_UP
);
}
else
{
throw
new
BusinessException
(
"无法获取 "
+
baseCurrency
+
" 到 "
+
DEFAULT_BASE_CURRENCY
+
" 的汇率"
);
}
}
}
// 获取 USD -> targetCurrency 的汇率
BigDecimal
usdToTargetRate
;
if
(
targetCurrency
.
equals
(
DEFAULT_BASE_CURRENCY
))
{
usdToTargetRate
=
BigDecimal
.
ONE
;
}
else
{
BigDecimal
rate
=
getDirectExchangeRate
(
DEFAULT_BASE_CURRENCY
,
targetCurrency
,
effectiveDate
);
if
(
rate
!=
null
)
{
usdToTargetRate
=
rate
;
}
else
{
// 尝试反向
BigDecimal
reverseRate
=
getDirectExchangeRate
(
targetCurrency
,
DEFAULT_BASE_CURRENCY
,
effectiveDate
);
if
(
reverseRate
!=
null
)
{
usdToTargetRate
=
BigDecimal
.
ONE
.
divide
(
reverseRate
,
8
,
RoundingMode
.
HALF_UP
);
}
else
{
throw
new
BusinessException
(
"无法获取 "
+
DEFAULT_BASE_CURRENCY
+
" 到 "
+
targetCurrency
+
" 的汇率"
);
}
}
}
// 计算交叉汇率
BigDecimal
crossRate
=
baseToUsdRate
.
multiply
(
usdToTargetRate
);
log
.
debug
(
"交叉汇率计算结果: {} -> {} = {}"
,
baseCurrency
,
targetCurrency
,
crossRate
);
return
crossRate
;
}
catch
(
Exception
e
)
{
log
.
error
(
"计算交叉汇率失败: {} -> {}"
,
baseCurrency
,
targetCurrency
,
e
);
throw
new
BusinessException
(
"无法获取汇率: "
+
baseCurrency
+
" -> "
+
targetCurrency
);
}
}
/**
* 验证币种是否支持(业务逻辑)
*/
private
boolean
isCurrencySupported
(
String
currency
)
{
if
(
StringUtils
.
isBlank
(
currency
))
{
return
false
;
}
// 查询该币种是否在汇率表中存在(作为基础币种或目标币种)
List
<
ExchangeRate
>
baseCurrencyRates
=
iExchangeRateService
.
getByBaseCurrency
(
currency
);
List
<
ExchangeRate
>
targetCurrencyRates
=
iExchangeRateService
.
getByTargetCurrency
(
currency
);
return
!
CollectionUtils
.
isEmpty
(
baseCurrencyRates
)
||
!
CollectionUtils
.
isEmpty
(
targetCurrencyRates
);
}
/**
* 获取支持的币种列表(业务逻辑)
*/
private
List
<
String
>
getSupportedCurrenciesList
()
{
// 查询所有有效的汇率记录
List
<
ExchangeRate
>
exchangeRates
=
iExchangeRateService
.
getAllActiveRates
();
Set
<
String
>
currencies
=
new
HashSet
<>();
for
(
ExchangeRate
rate
:
exchangeRates
)
{
currencies
.
add
(
rate
.
getBaseCurrency
());
currencies
.
add
(
rate
.
getTargetCurrency
());
}
return
new
ArrayList
<>(
currencies
);
}
/**
* 获取生效日期
*/
private
LocalDate
getEffectiveDate
(
Date
transactionDate
,
String
dateStr
)
{
if
(
transactionDate
!=
null
)
{
try
{
String
dateString
=
SIMPLE_DATE_FORMAT
.
format
(
transactionDate
);
return
LocalDate
.
parse
(
dateString
,
DATE_FORMATTER
);
}
catch
(
Exception
e
)
{
log
.
warn
(
"交易日期格式转换失败: {}, 使用当前日期"
,
transactionDate
);
}
}
if
(
StringUtils
.
isNotBlank
(
dateStr
))
{
try
{
return
LocalDate
.
parse
(
dateStr
,
DATE_FORMATTER
);
}
catch
(
Exception
e
)
{
log
.
warn
(
"日期字符串格式错误: {}, 使用当前日期"
,
dateStr
);
}
}
// 默认返回null,查询最新汇率
return
null
;
}
/**
* 解析日期字符串
*/
private
LocalDate
parseDate
(
String
dateStr
)
{
if
(
StringUtils
.
isBlank
(
dateStr
))
{
return
null
;
}
try
{
return
LocalDate
.
parse
(
dateStr
,
DATE_FORMATTER
);
}
catch
(
DateTimeParseException
e
)
{
log
.
warn
(
"日期格式错误: {}, 使用最新汇率"
,
dateStr
);
return
null
;
}
}
}
\ No newline at end of file
yd-base-api/src/main/java/com/yd/base/api/service/impl/ApiRelFieldValueServiceImpl.java
View file @
41e4a56d
...
...
@@ -104,10 +104,10 @@ public class ApiRelFieldValueServiceImpl implements ApiRelFieldValueService {
return
Result
.
success
(
resultList
);
}
//
1.
根据字段ID列表查询fvq_setting配置
// 根据字段ID列表查询fvq_setting配置
Map
<
String
,
FvqSetting
>
fvqSettingMap
=
getFvqSettingsByFieldIds
(
request
.
getFieldBizIdList
());
//
2.
分离有配置和无配置的字段ID
// 分离有配置和无配置的字段ID
List
<
String
>
configuredFieldIds
=
new
ArrayList
<>();
List
<
String
>
unconfiguredFieldIds
=
new
ArrayList
<>();
...
...
@@ -120,13 +120,13 @@ public class ApiRelFieldValueServiceImpl implements ApiRelFieldValueService {
}
}
//
3.
处理有配置的字段(通过接口获取)
// 处理有配置的字段(通过接口获取)
Map
<
String
,
GetFieldListResponse
>
configuredFieldsMap
=
processConfiguredFields
(
configuredFieldIds
,
fvqSettingMap
);
//
4.
处理无配置的字段(默认查询方式)
// 处理无配置的字段(默认查询方式)
Map
<
String
,
GetFieldListResponse
>
unconfiguredFieldsMap
=
processUnconfiguredFields
(
unconfiguredFieldIds
);
//
5.
按照请求字段ID的顺序合并结果
// 按照请求字段ID的顺序合并结果
for
(
String
fieldBizId
:
request
.
getFieldBizIdList
())
{
if
(
configuredFieldsMap
.
containsKey
(
fieldBizId
))
{
resultList
.
add
(
configuredFieldsMap
.
get
(
fieldBizId
));
...
...
yd-base-feign/src/main/java/com/yd/base/feign/client/exchangerate/ApiExchangeRateFeignClient.java
0 → 100644
View file @
41e4a56d
package
com
.
yd
.
base
.
feign
.
client
.
exchangerate
;
import
com.yd.base.feign.fallback.exchangerate.ApiExchangeRateFeignFallbackFactory
;
import
com.yd.base.feign.request.exchangerate.ApiExchangeRateConvertRequest
;
import
com.yd.base.feign.response.exchangerate.ApiExchangeRateConvertResponse
;
import
com.yd.common.result.Result
;
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.RequestParam
;
import
java.math.BigDecimal
;
import
java.util.List
;
/**
* 基础数据服务-汇率信息Feign客户端
*/
@FeignClient
(
name
=
"yd-base-api"
,
path
=
"/base/api/exchangeRate"
,
fallbackFactory
=
ApiExchangeRateFeignFallbackFactory
.
class
)
public
interface
ApiExchangeRateFeignClient
{
/**
* 货币转换
* @param request
* @return
*/
@PostMapping
(
"/convert"
)
Result
<
ApiExchangeRateConvertResponse
>
convert
(
@RequestBody
ApiExchangeRateConvertRequest
request
);
/**
* 获取汇率
* @param baseCurrency 基础币种
* @param targetCurrency 目标币种
* @param date
* @return
*/
@GetMapping
(
"/rate"
)
Result
<
BigDecimal
>
getExchangeRate
(
@RequestParam
(
"baseCurrency"
)
String
baseCurrency
,
@RequestParam
(
"targetCurrency"
)
String
targetCurrency
,
@RequestParam
(
value
=
"date"
,
required
=
false
)
String
date
);
/**
* 批量转换
* @param requests
* @return
*/
@PostMapping
(
"/batch/convert"
)
Result
<
List
<
ApiExchangeRateConvertResponse
>>
batchConvert
(
@RequestBody
List
<
ApiExchangeRateConvertRequest
>
requests
);
/**
* 验证币种是否支持
* @param currency
* @return
*/
@GetMapping
(
"/currency/supported"
)
Result
<
Boolean
>
isCurrencySupportedResult
(
@RequestParam
(
"currency"
)
String
currency
);
/**
* 获取支持的币种列表
* @return
*/
@GetMapping
(
"/currencies"
)
Result
<
List
<
String
>>
getSupportedCurrencies
();
}
yd-base-feign/src/main/java/com/yd/base/feign/fallback/exchangerate/ApiExchangeRateFeignFallbackFactory.java
0 → 100644
View file @
41e4a56d
package
com
.
yd
.
base
.
feign
.
fallback
.
exchangerate
;
import
com.yd.base.feign.client.exchangerate.ApiExchangeRateFeignClient
;
import
com.yd.base.feign.request.exchangerate.ApiExchangeRateConvertRequest
;
import
com.yd.base.feign.response.exchangerate.ApiExchangeRateConvertResponse
;
import
com.yd.common.result.Result
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.cloud.openfeign.FallbackFactory
;
import
org.springframework.stereotype.Component
;
import
java.math.BigDecimal
;
import
java.util.List
;
/**
* 基础数据服务-汇率信息Feign降级处理
*/
@Slf4j
@Component
public
class
ApiExchangeRateFeignFallbackFactory
implements
FallbackFactory
<
ApiExchangeRateFeignClient
>
{
@Override
public
ApiExchangeRateFeignClient
create
(
Throwable
cause
)
{
return
new
ApiExchangeRateFeignClient
()
{
@Override
public
Result
<
ApiExchangeRateConvertResponse
>
convert
(
ApiExchangeRateConvertRequest
request
)
{
return
null
;
}
@Override
public
Result
<
BigDecimal
>
getExchangeRate
(
String
baseCurrency
,
String
targetCurrency
,
String
date
)
{
return
null
;
}
@Override
public
Result
<
List
<
ApiExchangeRateConvertResponse
>>
batchConvert
(
List
<
ApiExchangeRateConvertRequest
>
requests
)
{
return
null
;
}
@Override
public
Result
<
Boolean
>
isCurrencySupportedResult
(
String
currency
)
{
return
null
;
}
@Override
public
Result
<
List
<
String
>>
getSupportedCurrencies
()
{
return
null
;
}
};
}
}
yd-base-feign/src/main/java/com/yd/base/feign/request/exchangerate/ApiExchangeRateConvertRequest.java
0 → 100644
View file @
41e4a56d
package
com
.
yd
.
base
.
feign
.
request
.
exchangerate
;
import
lombok.Data
;
import
javax.validation.constraints.NotBlank
;
import
javax.validation.constraints.NotNull
;
import
java.math.BigDecimal
;
import
java.util.Date
;
/**
* 汇率转换请求DTO
* @author zxm
* @since 2025-12-29
*/
@Data
public
class
ApiExchangeRateConvertRequest
{
/**
* 金额
*/
@NotNull
(
message
=
"金额不能为空"
)
private
BigDecimal
amount
;
/**
* 源币种代码
*/
@NotBlank
(
message
=
"源币种不能为空"
)
private
String
fromCurrency
;
/**
* 目标币种代码
*/
@NotBlank
(
message
=
"目标币种不能为空"
)
private
String
toCurrency
;
/**
* 交易日期(用于历史汇率查询)
*/
private
Date
transactionDate
;
/**
* 日期字符串(格式:yyyy-MM-dd),优先级低于transactionDate
*/
private
String
date
;
/**
* 是否需要返回汇率详情
*/
private
Boolean
needDetail
=
false
;
/**
* 业务场景标识
*/
private
String
businessScene
;
/**
* 业务ID
*/
private
String
businessId
;
/**
* 请求来源
*/
private
String
source
;
/**
* 请求ID(用于幂等性控制)
*/
private
String
requestId
;
/**
* 备注
*/
private
String
remark
;
/**
* 扩展字段(JSON格式)
*/
private
String
extInfo
;
}
\ No newline at end of file
yd-base-feign/src/main/java/com/yd/base/feign/response/exchangerate/ApiExchangeRateConvertResponse.java
0 → 100644
View file @
41e4a56d
package
com
.
yd
.
base
.
feign
.
response
.
exchangerate
;
import
lombok.Data
;
import
java.math.BigDecimal
;
import
java.util.Date
;
/**
* 汇率转换响应DTO
* @author zxm
* @since 2025-12-29
*/
@Data
public
class
ApiExchangeRateConvertResponse
{
/**
* 原始金额
*/
private
BigDecimal
originalAmount
;
/**
* 原始币种代码
*/
private
String
originalCurrency
;
/**
* 转换后金额
*/
private
BigDecimal
convertedAmount
;
/**
* 目标币种代码
*/
private
String
targetCurrency
;
/**
* 汇率(原始币种 -> 目标币种)
*/
private
BigDecimal
exchangeRate
;
/**
* 反向汇率(目标币种 -> 原始币种)
*/
private
BigDecimal
reverseExchangeRate
;
/**
* 汇率类型
*/
private
String
rateType
;
/**
* 生效日期
*/
private
Date
effectiveDate
;
/**
* 交易日期
*/
private
Date
transactionDate
;
/**
* 汇率来源
*/
private
String
rateSource
;
/**
* 转换时间
*/
private
Date
convertTime
;
/**
* 是否使用交叉汇率计算
*/
private
Boolean
isCrossRate
=
false
;
/**
* 交叉汇率中间币种
*/
private
String
crossRateCurrency
;
/**
* 转换状态
*/
private
String
convertStatus
;
/**
* 错误码
*/
private
String
errorCode
;
/**
* 错误信息
*/
private
String
errorMessage
;
/**
* 汇率记录ID
*/
private
Long
exchangeRateId
;
/**
* 基础币种名称
*/
private
String
baseCurrencyName
;
/**
* 目标币种名称
*/
private
String
targetCurrencyName
;
/**
* 业务场景标识
*/
private
String
businessScene
;
/**
* 业务ID
*/
private
String
businessId
;
/**
* 请求ID
*/
private
String
requestId
;
/**
* 扩展字段(JSON格式)
*/
private
String
extInfo
;
/**
* 转换详情
*/
@Data
public
static
class
ConvertDetail
{
/**
* 直接汇率是否存在
*/
private
Boolean
directRateExists
;
/**
* 是否使用反向汇率
*/
private
Boolean
useReverseRate
;
/**
* 汇率计算路径
*/
private
String
ratePath
;
/**
* 汇率更新时间
*/
private
Date
rateUpdateTime
;
/**
* 汇率有效期
*/
private
Date
rateExpireTime
;
}
/**
* 转换详情(当needDetail=true时返回)
*/
private
ConvertDetail
convertDetail
;
/**
* 创建成功响应
*/
public
static
ApiExchangeRateConvertResponse
success
(
BigDecimal
originalAmount
,
String
originalCurrency
,
BigDecimal
convertedAmount
,
String
targetCurrency
,
BigDecimal
exchangeRate
)
{
ApiExchangeRateConvertResponse
response
=
new
ApiExchangeRateConvertResponse
();
response
.
setOriginalAmount
(
originalAmount
);
response
.
setOriginalCurrency
(
originalCurrency
);
response
.
setConvertedAmount
(
convertedAmount
);
response
.
setTargetCurrency
(
targetCurrency
);
response
.
setExchangeRate
(
exchangeRate
);
response
.
setConvertTime
(
new
Date
());
response
.
setConvertStatus
(
"SUCCESS"
);
// 计算反向汇率
if
(
exchangeRate
!=
null
&&
exchangeRate
.
compareTo
(
BigDecimal
.
ZERO
)
!=
0
)
{
try
{
BigDecimal
reverseRate
=
BigDecimal
.
ONE
.
divide
(
exchangeRate
,
8
,
BigDecimal
.
ROUND_HALF_UP
);
response
.
setReverseExchangeRate
(
reverseRate
);
}
catch
(
Exception
e
)
{
// 忽略计算错误
}
}
return
response
;
}
/**
* 创建失败响应
*/
public
static
ApiExchangeRateConvertResponse
error
(
String
errorCode
,
String
errorMessage
)
{
ApiExchangeRateConvertResponse
response
=
new
ApiExchangeRateConvertResponse
();
response
.
setConvertTime
(
new
Date
());
response
.
setConvertStatus
(
"FAILED"
);
response
.
setErrorCode
(
errorCode
);
response
.
setErrorMessage
(
errorMessage
);
return
response
;
}
}
\ No newline at end of file
yd-base-service/src/main/java/com/yd/base/service/dao/ExchangeRateMapper.java
0 → 100644
View file @
41e4a56d
package
com
.
yd
.
base
.
service
.
dao
;
import
com.yd.base.service.model.ExchangeRate
;
import
com.baomidou.mybatisplus.core.mapper.BaseMapper
;
/**
* <p>
* 汇率表 Mapper 接口
* </p>
*
* @author zxm
* @since 2025-12-29
*/
public
interface
ExchangeRateMapper
extends
BaseMapper
<
ExchangeRate
>
{
}
yd-base-service/src/main/java/com/yd/base/service/model/ExchangeRate.java
0 → 100644
View file @
41e4a56d
package
com
.
yd
.
base
.
service
.
model
;
import
com.baomidou.mybatisplus.annotation.IdType
;
import
com.baomidou.mybatisplus.annotation.TableField
;
import
com.baomidou.mybatisplus.annotation.TableId
;
import
com.baomidou.mybatisplus.annotation.TableName
;
import
java.io.Serializable
;
import
java.math.BigDecimal
;
import
java.time.LocalDate
;
import
java.time.LocalDateTime
;
import
lombok.Getter
;
import
lombok.Setter
;
/**
* <p>
* 汇率表
* </p>
*
* @author zxm
* @since 2025-12-29
*/
@Getter
@Setter
@TableName
(
"exchange_rate"
)
public
class
ExchangeRate
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
/**
* 主键ID
*/
@TableId
(
value
=
"id"
,
type
=
IdType
.
AUTO
)
private
Long
id
;
/**
* 基础币种代码(ISO 4217标准)
*/
@TableField
(
"base_currency"
)
private
String
baseCurrency
;
/**
* 基础币种名称
*/
@TableField
(
"base_currency_name"
)
private
String
baseCurrencyName
;
/**
* 目标币种代码(ISO 4217标准)
*/
@TableField
(
"target_currency"
)
private
String
targetCurrency
;
/**
* 目标币种名称
*/
@TableField
(
"target_currency_name"
)
private
String
targetCurrencyName
;
/**
* 汇率(1单位基础币种可兑换的目标币种数量)
*/
@TableField
(
"exchange_rate"
)
private
BigDecimal
exchangeRate
;
/**
* 生效日期
*/
@TableField
(
"effective_date"
)
private
LocalDate
effectiveDate
;
/**
* 失效日期(NULL表示永久有效)
*/
@TableField
(
"expiration_date"
)
private
LocalDate
expirationDate
;
/**
* 汇率类型: SPOT-即期, FORWARD-远期, HISTORICAL-历史
*/
@TableField
(
"rate_type"
)
private
String
rateType
;
/**
* 数据来源: SYSTEM-系统设置, API-外部接口, MANUAL-手动录入
*/
@TableField
(
"source"
)
private
String
source
;
/**
* 是否有效: 0-无效, 1-有效
*/
@TableField
(
"is_active"
)
private
Integer
isActive
;
/**
* 通用备注
*/
@TableField
(
"remark"
)
private
String
remark
;
/**
* 删除标识:0-正常,1-删除
*/
@TableField
(
"is_deleted"
)
private
Integer
isDeleted
;
/**
* 创建人ID
*/
@TableField
(
"creator_id"
)
private
String
creatorId
;
/**
* 更新人ID
*/
@TableField
(
"updater_id"
)
private
String
updaterId
;
/**
* 创建时间
*/
@TableField
(
"create_time"
)
private
LocalDateTime
createTime
;
/**
* 更新时间
*/
@TableField
(
"update_time"
)
private
LocalDateTime
updateTime
;
}
yd-base-service/src/main/java/com/yd/base/service/service/IExchangeRateService.java
0 → 100644
View file @
41e4a56d
package
com
.
yd
.
base
.
service
.
service
;
import
com.yd.base.service.model.ExchangeRate
;
import
com.baomidou.mybatisplus.extension.service.IService
;
import
java.math.BigDecimal
;
import
java.time.LocalDate
;
import
java.util.List
;
/**
* <p>
* 汇率表 服务类
* </p>
*
* @author zxm
* @since 2025-12-29
*/
public
interface
IExchangeRateService
extends
IService
<
ExchangeRate
>
{
/**
* 根据币种对和日期查询汇率
* @param baseCurrency 基础币种
* @param targetCurrency 目标币种
* @param effectiveDate 生效日期
* @return 汇率记录
*/
ExchangeRate
getByCurrencyAndDate
(
String
baseCurrency
,
String
targetCurrency
,
LocalDate
effectiveDate
);
/**
* 根据币种对查询最新汇率
* @param baseCurrency 基础币种
* @param targetCurrency 目标币种
* @return 最新汇率记录
*/
ExchangeRate
getLatestByCurrency
(
String
baseCurrency
,
String
targetCurrency
);
/**
* 根据生效日期查询汇率列表
* @param effectiveDate 生效日期
* @return 汇率列表
*/
List
<
ExchangeRate
>
getByEffectiveDate
(
LocalDate
effectiveDate
);
/**
* 查询所有有效的汇率记录
* @return 汇率列表
*/
List
<
ExchangeRate
>
getAllActiveRates
();
/**
* 根据基础币种查询汇率
* @param baseCurrency 基础币种
* @return 汇率列表
*/
List
<
ExchangeRate
>
getByBaseCurrency
(
String
baseCurrency
);
/**
* 根据目标币种查询汇率
* @param targetCurrency 目标币种
* @return 汇率列表
*/
List
<
ExchangeRate
>
getByTargetCurrency
(
String
targetCurrency
);
}
\ No newline at end of file
yd-base-service/src/main/java/com/yd/base/service/service/impl/ExchangeRateServiceImpl.java
0 → 100644
View file @
41e4a56d
package
com
.
yd
.
base
.
service
.
service
.
impl
;
import
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
;
import
com.yd.base.service.model.ExchangeRate
;
import
com.yd.base.service.dao.ExchangeRateMapper
;
import
com.yd.base.service.service.IExchangeRateService
;
import
com.baomidou.mybatisplus.extension.service.impl.ServiceImpl
;
import
org.springframework.stereotype.Service
;
import
java.time.LocalDate
;
import
java.util.List
;
/**
* <p>
* 汇率表 服务实现类
* </p>
*
* @author zxm
* @since 2025-12-29
*/
@Service
public
class
ExchangeRateServiceImpl
extends
ServiceImpl
<
ExchangeRateMapper
,
ExchangeRate
>
implements
IExchangeRateService
{
@Override
public
ExchangeRate
getByCurrencyAndDate
(
String
baseCurrency
,
String
targetCurrency
,
LocalDate
effectiveDate
)
{
LambdaQueryWrapper
<
ExchangeRate
>
queryWrapper
=
new
LambdaQueryWrapper
<>();
queryWrapper
.
eq
(
ExchangeRate:
:
getBaseCurrency
,
baseCurrency
)
.
eq
(
ExchangeRate:
:
getTargetCurrency
,
targetCurrency
)
.
eq
(
ExchangeRate:
:
getEffectiveDate
,
effectiveDate
)
.
eq
(
ExchangeRate:
:
getIsActive
,
1
)
.
eq
(
ExchangeRate:
:
getIsDeleted
,
0
)
.
last
(
"LIMIT 1"
);
return
this
.
getOne
(
queryWrapper
);
}
@Override
public
ExchangeRate
getLatestByCurrency
(
String
baseCurrency
,
String
targetCurrency
)
{
LambdaQueryWrapper
<
ExchangeRate
>
queryWrapper
=
new
LambdaQueryWrapper
<>();
queryWrapper
.
eq
(
ExchangeRate:
:
getBaseCurrency
,
baseCurrency
)
.
eq
(
ExchangeRate:
:
getTargetCurrency
,
targetCurrency
)
.
eq
(
ExchangeRate:
:
getIsActive
,
1
)
.
eq
(
ExchangeRate:
:
getIsDeleted
,
0
)
.
orderByDesc
(
ExchangeRate:
:
getEffectiveDate
)
.
last
(
"LIMIT 1"
);
return
this
.
getOne
(
queryWrapper
);
}
@Override
public
List
<
ExchangeRate
>
getByEffectiveDate
(
LocalDate
effectiveDate
)
{
LambdaQueryWrapper
<
ExchangeRate
>
queryWrapper
=
new
LambdaQueryWrapper
<>();
queryWrapper
.
eq
(
ExchangeRate:
:
getEffectiveDate
,
effectiveDate
)
.
eq
(
ExchangeRate:
:
getIsActive
,
1
)
.
eq
(
ExchangeRate:
:
getIsDeleted
,
0
);
return
this
.
list
(
queryWrapper
);
}
@Override
public
List
<
ExchangeRate
>
getAllActiveRates
()
{
LambdaQueryWrapper
<
ExchangeRate
>
queryWrapper
=
new
LambdaQueryWrapper
<>();
queryWrapper
.
eq
(
ExchangeRate:
:
getIsActive
,
1
)
.
eq
(
ExchangeRate:
:
getIsDeleted
,
0
)
.
orderByAsc
(
ExchangeRate:
:
getBaseCurrency
)
.
orderByAsc
(
ExchangeRate:
:
getTargetCurrency
);
return
this
.
list
(
queryWrapper
);
}
@Override
public
List
<
ExchangeRate
>
getByBaseCurrency
(
String
baseCurrency
)
{
LambdaQueryWrapper
<
ExchangeRate
>
queryWrapper
=
new
LambdaQueryWrapper
<>();
queryWrapper
.
eq
(
ExchangeRate:
:
getBaseCurrency
,
baseCurrency
)
.
eq
(
ExchangeRate:
:
getIsActive
,
1
)
.
eq
(
ExchangeRate:
:
getIsDeleted
,
0
)
.
orderByAsc
(
ExchangeRate:
:
getTargetCurrency
);
return
this
.
list
(
queryWrapper
);
}
@Override
public
List
<
ExchangeRate
>
getByTargetCurrency
(
String
targetCurrency
)
{
LambdaQueryWrapper
<
ExchangeRate
>
queryWrapper
=
new
LambdaQueryWrapper
<>();
queryWrapper
.
eq
(
ExchangeRate:
:
getTargetCurrency
,
targetCurrency
)
.
eq
(
ExchangeRate:
:
getIsActive
,
1
)
.
eq
(
ExchangeRate:
:
getIsDeleted
,
0
)
.
orderByAsc
(
ExchangeRate:
:
getBaseCurrency
);
return
this
.
list
(
queryWrapper
);
}
}
\ No newline at end of file
yd-base-service/src/main/java/com/yd/base/service/utils/MyBatisPlusCodeGenerator.java
View file @
41e4a56d
...
...
@@ -21,7 +21,7 @@ public class MyBatisPlusCodeGenerator {
})
.
strategyConfig
(
builder
->
{
builder
.
addInclude
(
"
fvq_setting
"
"
exchange_rate
"
)
.
entityBuilder
()
.
enableLombok
()
...
...
yd-base-service/src/main/resources/mappers/ExchangeRateMapper.xml
0 → 100644
View file @
41e4a56d
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper
namespace=
"com.yd.base.service.dao.ExchangeRateMapper"
>
</mapper>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment