Commit 41e4a56d by zhangxingmin

push

parent 1f199e88
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();
}
}
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);
}
...@@ -104,10 +104,10 @@ public class ApiRelFieldValueServiceImpl implements ApiRelFieldValueService { ...@@ -104,10 +104,10 @@ public class ApiRelFieldValueServiceImpl implements ApiRelFieldValueService {
return Result.success(resultList); return Result.success(resultList);
} }
// 1. 根据字段ID列表查询fvq_setting配置 // 根据字段ID列表查询fvq_setting配置
Map<String, FvqSetting> fvqSettingMap = getFvqSettingsByFieldIds(request.getFieldBizIdList()); Map<String, FvqSetting> fvqSettingMap = getFvqSettingsByFieldIds(request.getFieldBizIdList());
// 2. 分离有配置和无配置的字段ID // 分离有配置和无配置的字段ID
List<String> configuredFieldIds = new ArrayList<>(); List<String> configuredFieldIds = new ArrayList<>();
List<String> unconfiguredFieldIds = new ArrayList<>(); List<String> unconfiguredFieldIds = new ArrayList<>();
...@@ -120,13 +120,13 @@ public class ApiRelFieldValueServiceImpl implements ApiRelFieldValueService { ...@@ -120,13 +120,13 @@ public class ApiRelFieldValueServiceImpl implements ApiRelFieldValueService {
} }
} }
// 3. 处理有配置的字段(通过接口获取) // 处理有配置的字段(通过接口获取)
Map<String, GetFieldListResponse> configuredFieldsMap = processConfiguredFields(configuredFieldIds, fvqSettingMap); Map<String, GetFieldListResponse> configuredFieldsMap = processConfiguredFields(configuredFieldIds, fvqSettingMap);
// 4. 处理无配置的字段(默认查询方式) // 处理无配置的字段(默认查询方式)
Map<String, GetFieldListResponse> unconfiguredFieldsMap = processUnconfiguredFields(unconfiguredFieldIds); Map<String, GetFieldListResponse> unconfiguredFieldsMap = processUnconfiguredFields(unconfiguredFieldIds);
// 5. 按照请求字段ID的顺序合并结果 // 按照请求字段ID的顺序合并结果
for (String fieldBizId : request.getFieldBizIdList()) { for (String fieldBizId : request.getFieldBizIdList()) {
if (configuredFieldsMap.containsKey(fieldBizId)) { if (configuredFieldsMap.containsKey(fieldBizId)) {
resultList.add(configuredFieldsMap.get(fieldBizId)); resultList.add(configuredFieldsMap.get(fieldBizId));
......
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();
}
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;
}
};
}
}
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
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
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> {
}
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;
}
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
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
...@@ -21,7 +21,7 @@ public class MyBatisPlusCodeGenerator { ...@@ -21,7 +21,7 @@ public class MyBatisPlusCodeGenerator {
}) })
.strategyConfig(builder -> { .strategyConfig(builder -> {
builder.addInclude( builder.addInclude(
"fvq_setting" "exchange_rate"
) )
.entityBuilder() .entityBuilder()
.enableLombok() .enableLombok()
......
<?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>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment