Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package io.github.juwencheng.autoexchange.autoconfigure;

import io.github.juwencheng.autoexchange.autoconfigure.validation.RateRefreshConfigurationValidator;
import io.github.juwencheng.autoexchange.core.convertor.DefaultExchangeResultDataConvertor;
import io.github.juwencheng.autoexchange.core.convertor.IExchangeResultDataConvertor;
import io.github.juwencheng.autoexchange.core.interceptor.AutoExchangeInterceptor;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
Expand Down Expand Up @@ -33,8 +35,14 @@ public class AutoExchangeAutoConfiguration {
// ------------- 注册应用汇率的策略方法类 ------
@Bean
@ConditionalOnMissingBean
public IApplyExchangeStrategy autoExchangeStrategy(AutoExchangeProperties properties, ExchangeManager exchangeManager) {
return new AutoApplyExchangeStrategy(properties, exchangeManager);
public IExchangeResultDataConvertor exchangeResultDataConvertor() {
return new DefaultExchangeResultDataConvertor();
}

@Bean
@ConditionalOnMissingBean
public IApplyExchangeStrategy autoExchangeStrategy(AutoExchangeProperties properties, ExchangeManager exchangeManager, IExchangeResultDataConvertor convertor) {
return new AutoApplyExchangeStrategy(properties, exchangeManager, convertor);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.github.juwencheng.autoexchange.core.convertor;

import io.github.juwencheng.autoexchange.core.dto.ExchangeResultDto;

import java.util.Map;

/**
* 默认的汇率转换结果数据转换器,返回包含 base、trans、rate、price 字段的 Map
*/
public class DefaultExchangeResultDataConvertor implements IExchangeResultDataConvertor {
@Override
public Map<String, Object> convert(ExchangeResultDto exchangeResultDto) {
return exchangeResultDto.toMap();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.github.juwencheng.autoexchange.core.convertor;

import io.github.juwencheng.autoexchange.core.dto.ExchangeResultDto;

import java.util.Map;

/**
* 汇率转换结果数据转换器接口,允许用户自定义追加的汇率信息格式
*/
public interface IExchangeResultDataConvertor {
/**
* 将汇率转换结果转换为追加到响应中的数据格式
*
* @param exchangeResultDto 汇率转换结果
* @return 追加到响应中的数据
*/
Map<String, Object> convert(ExchangeResultDto exchangeResultDto);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.github.juwencheng.autoexchange.core.annotation.AutoExchangeField;
import io.github.juwencheng.autoexchange.core.context.AutoExchangeContext;
import io.github.juwencheng.autoexchange.core.context.AutoExchangeContextHolder;
import io.github.juwencheng.autoexchange.core.convertor.IExchangeResultDataConvertor;
import io.github.juwencheng.autoexchange.core.dto.ExchangeInfoRateDto;
import io.github.juwencheng.autoexchange.core.dto.ExchangeResultDto;
import io.github.juwencheng.autoexchange.core.manager.ExchangeManager;
Expand All @@ -25,10 +26,12 @@
public class AutoApplyExchangeStrategy extends AbstractApplyExchangeStrategy implements IApplyExchangeStrategy {
private static final Logger log = LoggerFactory.getLogger(AutoApplyExchangeStrategy.class);
private final ExchangeManager exchangeManager;
private final IExchangeResultDataConvertor convertor;

public AutoApplyExchangeStrategy(AutoExchangeProperties properties, ExchangeManager exchangeManager) {
public AutoApplyExchangeStrategy(AutoExchangeProperties properties, ExchangeManager exchangeManager, IExchangeResultDataConvertor convertor) {
super(properties);
this.exchangeManager = exchangeManager;
this.convertor = convertor;
}

@Override
Expand Down Expand Up @@ -75,7 +78,7 @@ private void traverseObjectGraph(Object object, Map<Object, Object> visitedMap)
if (newFieldName == null || newFieldName.trim().isEmpty()) {
newFieldName = exchangeableField.getName() + "AutoExchange";
}
context.addAppendedData(object, newFieldName, exchangeResult.toMap());
context.addAppendedData(object, newFieldName, convertor.convert(exchangeResult));
} catch (IllegalAccessException e) {
log.error("对@AutoExchangeField注解标注的属性进行自动换汇失败:" + object.getClass() + "." + exchangeableField.getName(), e);
throw new ExchangeProcessingException("对@AutoExchangeField注解标注的属性进行自动换汇失败:" + object.getClass() + "." + exchangeableField.getName(), e);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package io.github.juwencheng.autoexchange.testapp;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Primary;
import org.springframework.http.MediaType;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.MockMvc;
import io.github.juwencheng.autoexchange.core.convertor.IExchangeResultDataConvertor;
import io.github.juwencheng.autoexchange.core.dto.ExchangeResultDto;
import io.github.juwencheng.autoexchange.core.dto.ExchangeInfoRateDto;
import io.github.juwencheng.autoexchange.provider.IExchangeDataProvider;
import io.github.juwencheng.autoexchange.testapp.controller.TestController;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@WebMvcTest(TestController.class)
@TestPropertySource(properties = {
"auto.exchange.refresh-on-launch=true",
"auto.exchange.default-base-currency=CNY",
"auto.exchange.rate-refresh.enabled=false"
})
@EnableScheduling
@EnableAspectJAutoProxy
public class CustomConvertorTest {

@Autowired
private MockMvc mockMvc;

@TestConfiguration
static class MockConfig {
@Bean
@Primary
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(1);
scheduler.setThreadNamePrefix("test-scheduler-");
scheduler.initialize();
return scheduler;
}

@Bean
@Primary
public IExchangeDataProvider mockExchangeRateService() {
IExchangeDataProvider mock = Mockito.mock(IExchangeDataProvider.class);
when(mock.fetchData())
.thenReturn(List.of(
new ExchangeInfoRateDto("CNY", "CNY", BigDecimal.valueOf(1)),
new ExchangeInfoRateDto("USD", "CNY", BigDecimal.valueOf(8))));
return mock;
}

/**
* 自定义汇率转换结果数据转换器,追加额外的 convertedBy 字段
*/
@Bean
@Primary
public IExchangeResultDataConvertor customConvertor() {
return exchangeResultDto -> {
Map<String, Object> result = new HashMap<>();
result.put("convertedPrice", exchangeResultDto.getPrice());
result.put("currency", exchangeResultDto.getTransCurrency());
result.put("convertedBy", "custom");
return result;
};
}
}

@Test
@DisplayName("测试自定义IExchangeResultDataConvertor可以替换默认的格式")
void testCustomConvertor() throws Exception {
mockMvc.perform(get("/test/simple")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.name").value("Test Product"))
.andExpect(jsonPath("$.priceUsd").value(100.00))
// 验证自定义格式的字段存在
.andExpect(jsonPath("$.priceInCny.convertedPrice").value(100.00))
.andExpect(jsonPath("$.priceInCny.currency").value("CNY"))
.andExpect(jsonPath("$.priceInCny.convertedBy").value("custom"))
// 验证默认格式的字段不存在
.andExpect(jsonPath("$.priceInCny.base").doesNotExist())
.andExpect(jsonPath("$.priceInCny.trans").doesNotExist())
.andDo(print());
}
}