Created
June 13, 2020 12:38
-
-
Save Frechet/fb771676d4581fa14e288f80b5a9646f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//------------------------------------------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