Skip to content

Instantly share code, notes, and snippets.

@Frechet
Created June 13, 2020 12:38
Show Gist options
  • Save Frechet/fb771676d4581fa14e288f80b5a9646f to your computer and use it in GitHub Desktop.
Save Frechet/fb771676d4581fa14e288f80b5a9646f to your computer and use it in GitHub Desktop.
//------------------------------------------pom.xml
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
...
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.14.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<schemaLanguage>WSDL</schemaLanguage>
<generatePackage>dev.borovlev.demo.wsdl.generated</generatePackage>
<generateDirectory>${project.basedir}/src/main/java</generateDirectory>
<schemaDirectory>${project.basedir}/src/main/resources</schemaDirectory>
<schemaIncludes>
<include>DailyInfo.asmx.wsdl</include>
</schemaIncludes>
<episode>false</episode>
</configuration>
</plugin>
//------------------------------------------SoapConfig
@Configuration
public class SoapConfig {
/**
* This is the package name specified in the <generatePackage> specified in pom.xml for jaxb2 plugin
*/
private static final String WSDL_CONTEXT_PATH = "dev.borovlev.demo.wsdl.generated";
@Bean
public Jaxb2Marshaller cbrMarshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
// this is the package name specified in the <generatePackage> specified in pom.xml
marshaller.setContextPath(WSDL_CONTEXT_PATH);
return marshaller;
}
@Bean
public CbrSoapClient cbrSoapConnector(Jaxb2Marshaller cbrMarshaller,
DateTimeConverter dateTimeConverter, CbrProperties cbrProperties, ServiceProperties serviceProperties,
ExchangeRateFromMapsConverter ex) {
CbrSoapClient client = new CbrSoapClient(cbrProperties, dateTimeConverter, ex);
client.setMarshaller(cbrMarshaller);
client.setUnmarshaller(cbrMarshaller);
client.setMessageFactory(messageFactoryCbr());
return client;
}
@Bean
public SaajSoapMessageFactory messageFactoryCbr() {
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory();
messageFactory.setSoapVersion(SoapVersion.SOAP_12);
return messageFactory;
}
}
//------------------------------------------CbrSoapClient
import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
@RequiredArgsConstructor
public class CbrSoapClient extends WebServiceGatewaySupport {
private static final String CURS_ON_DATE_TAG_NAME = "ValuteCursOnDate";
private static final String RE_CURS_ON_DATE_TAG_NAME = "Currency";
private static final String ONDATE_FIELD_NAME = "OnDate";
private static final String SEPARATOR_ONDATE = "-";
private final CbrProperties cbrProperties;
private final DateTimeConverter dateTimeConverter;
private final ExchangeRateFromMapsConverter exchangeRateFromMapsConverter;
/**
* Получение курсов валют с цбр по рублевому апи.
*
* @param date - дата за которуб нужно получить курсы.
* @param qualifierCodes - коллекция с кодами валют для которых нужно получить курсы.
*/
public List<ExchangeRateDto> getDailyRate(LocalDate date, List<ExchangeRateCodesDto> qualifierCodes) {
GetCursOnDateXML request = new GetCursOnDateXML();
request.setOnDate(dateTimeConverter.toXmlDate(date));
GetCursOnDateXMLResponse testResponse =
(GetCursOnDateXMLResponse) getWebServiceTemplate().marshalSendAndReceive(
cbrProperties.getDailyRateSoapUri(), request);
GetCursOnDateXMLResponse.GetCursOnDateXMLResult result = testResponse.getGetCursOnDateXMLResult();
String actualDate = ((Element) result.getContent()
.get(0)).getAttributes().getNamedItem(ONDATE_FIELD_NAME).getNodeValue();
if (!actualDate.equals(date.toString().replace(SEPARATOR_ONDATE, ""))) {
return Collections.emptyList();
}
NodeList cursOnDateNodeList = ((Element) result.getContent().get(0))
.getElementsByTagName(CURS_ON_DATE_TAG_NAME);
List<ExchangeRateDto> exchangeRateList = parseDailyRate(cursOnDateNodeList, date);
//буквенные коды которые нам надо скачать
List<String> currencyCodes = qualifierCodes.stream()
.map(ExchangeRateCodesDto::getCurrencyCode)
.collect(
Collectors.toList());
//удаляем из скаченных, те которые нам не нужны
exchangeRateList.removeIf(exchangeRate -> !currencyCodes.contains(exchangeRate.getCurrencyCode()));
return exchangeRateList;
}
/**
* Получение курсов валют с цбр по долларовому апи.
*
* @param date - дата за которуб нужно получить курсы.
* @param qualifierCodes - коллекция с кодами валют для которых нужно получить курсы.
*/
public List<ExchangeRateDto> getReutersDailyRate(LocalDate date, List<ExchangeRateCodesDto> qualifierCodes,
Map<LocalDate, BigDecimal> dollarRate) {
if (dollarRate.get(date) == null) {
logger.info("No dollar rate for date: " + date);
return Collections.emptyList();
}
GetReutersCursOnDate reutersCursOnDateRequest = new GetReutersCursOnDate();
reutersCursOnDateRequest.setOnDate(dateTimeConverter.toXmlDate(date));
GetReutersCursOnDateResponse response =
(GetReutersCursOnDateResponse) getWebServiceTemplate().marshalSendAndReceive(
cbrProperties.getDailyRateSoapUri(), reutersCursOnDateRequest);
GetReutersCursOnDateResponse.GetReutersCursOnDateResult result = response.getGetReutersCursOnDateResult();
NodeList cursRe = ((Element) result.getAny()).getElementsByTagName(RE_CURS_ON_DATE_TAG_NAME);
List<ExchangeRateDto> exchangeRateList = parseReutersDailyRate(cursRe, date, dollarRate.get(date));
return exchangeRateList;
}
private List<ExchangeRateDto> parseDailyRate(NodeList list, LocalDate date) {
List<Map<String, String>> resultList = new ArrayList<>();
List<ExchangeRateDto> exchangeRateList = new ArrayList<>();
for (int i = 0; i < list.getLength(); i++) {
Map<String, String> map = new HashMap<>();
Node node = list.item(i);
parse(node.getFirstChild(), map);
resultList.add(map);
exchangeRateList.add(exchangeRateFromMapsConverter.fromDailyRateMapToModel(map, date));
}
return exchangeRateList;
}
private List<ExchangeRateDto> parseReutersDailyRate(NodeList list, LocalDate date, BigDecimal dollarRate) {
List<Map<String, String>> resultList = new ArrayList<>();
List<ExchangeRateDto> exchangeRateList = new ArrayList<>();
for (int i = 0; i < list.getLength(); i++) {
Map<String, String> map = new HashMap<>();
Node node = list.item(i);
parse(node.getFirstChild(), map);
resultList.add(map);
exchangeRateList.add(exchangeRateFromMapsConverter.fromReutersDailyRateMapToModel(map, date, dollarRate));
}
return exchangeRateList;
}
private void parse(Node node, Map<String, String> map) {
if (node == null) {
return;
}
map.put(node.getNodeName().trim(), node.getFirstChild().getNodeValue().trim());
parse(node.getNextSibling(), map);
}
}
//------------------------------------------DateTimeConverter
@Component
public class DateTimeConverter {
private DatatypeFactory datatypeFactory;
public DateTimeConverter() throws DatatypeConfigurationException {
datatypeFactory = DatatypeFactory.newInstance();
}
/**
* Конвертер из локалдейт в xml.
* Приходиться делать это именно таким, немного странным способом,
* Поскольку при другом виде конвертации не будут получены курсы для редких валют в доллаарах
* изза особенностей АПИ ЦБ РФ.
*
* @param localDate дата, которую надо сконвентировать
* @return дата в формате xml
*/
public XMLGregorianCalendar toXmlDate(LocalDate localDate) {
XMLGregorianCalendar xmlDate = datatypeFactory.newXMLGregorianCalendarDate(
localDate.getYear(),
localDate.getMonthValue(),
localDate.getDayOfMonth(),
DatatypeConstants.FIELD_UNDEFINED);
xmlDate.setTime(0, 0, 0, 0);
return xmlDate;
}
}
//------------------------------------------ExchangeRateFromMapsConverter
@Component
@RequiredArgsConstructor
public class ExchangeRateFromMapsConverter {
private static final String RATE_FIELD = "Vcurs";
private static final String NOMINAL_FIELD = "Vnom";
private static final String VCH_FIELD = "VchCode";
private static final String NAME_FIELD = "Vname";
private static final String CODE_FIELD = "Vcode";
private static final String NUM_CODE_FIELD = "num_code";
private static final String DIRECTION_FIELD = "dir";
private static final String VALUE_FIELD = "val";
//прямая котировка - указывает, сколько единиц валюты приходится на 1 доллар
private static final int DIRECT_DOLLAR_RATE = 0;
//обратная котировка - указывает, сколько долларов приходится на 1 единицу валюты
private static final int REVERSE_DOLLAR_RATE = 1;
public ExchangeRateDto fromDailyRateMapToModel(Map<String, String> map, LocalDate date) {
ExchangeRateDto exchangeRate = new ExchangeRateDto();
for (Map.Entry<String, String> entry : map.entrySet()) {
switch (entry.getKey()) {
case RATE_FIELD:
Optional.of(map.get(NOMINAL_FIELD))
.map(BigDecimal::new)
.ifPresent(nominal -> exchangeRate.setRate(
new BigDecimal(entry.getValue()).divide(nominal, CURRENCY_MATH_CONTEXT)));
break;
case VCH_FIELD:
exchangeRate.setCurrencyCode(entry.getValue());
break;
case NAME_FIELD:
exchangeRate.setCurrencyName(entry.getValue());
break;
case CODE_FIELD:
exchangeRate.setQualifierCode(entry.getValue());
break;
default:
break;
}
exchangeRate.setRubleRelation(BigDecimal.ONE.divide(exchangeRate.getRate(), CURRENCY_MATH_CONTEXT));
exchangeRate.setBeginDate(date);
exchangeRate.setEndDate(date);
}
return exchangeRate;
}
public ExchangeRateDto fromReutersDailyRateMapToModel(Map<String, String> map, LocalDate date,
BigDecimal dollarRate) {
ExchangeRateDto exchangeRate = new ExchangeRateDto();
exchangeRate.setQualifierCode(map.get(NUM_CODE_FIELD));
int dirType = Integer.parseInt(map.get(DIRECTION_FIELD));
BigDecimal rate = BigDecimal.ONE;
if (dirType == DIRECT_DOLLAR_RATE) {
rate = dollarRate.divide(new BigDecimal(map.get(VALUE_FIELD)), CURRENCY_MATH_CONTEXT);
} else if (dirType == REVERSE_DOLLAR_RATE) {
rate = dollarRate.multiply(new BigDecimal(map.get(VALUE_FIELD)));
}
exchangeRate.setRate(rate);
exchangeRate.setRubleRelation(BigDecimal.ONE.divide(exchangeRate.getRate(), CURRENCY_MATH_CONTEXT));
exchangeRate.setBeginDate(date);
exchangeRate.setEndDate(date);
return exchangeRate;
}
public ExchangeRateCodesDto fromExchangeRateToCode(ExchangeRateDto exchangeRate) {
return new ExchangeRateCodesDto(exchangeRate.getCurrencyCode(), exchangeRate.getQualifierCode());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment