| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473 |
- package com.crm.manager.service.impl;
- import com.alibaba.fastjson2.JSON;
- import com.crm.manager.dao.mapper.TransactionItemMapper;
- import com.crm.manager.repository.TransactionItemRepository;
- import com.crm.manager.service.SysConfigService;
- import com.crm.manager.service.SysVaultodyConfigService;
- import com.crm.manager.service.VaultodyService;
- import com.crm.manager.util.DateUtils;
- import com.crm.rely.backend.core.constant.Constants;
- import com.crm.rely.backend.core.dto.base.BaseResultDto;
- import com.crm.rely.backend.core.dto.base.PageDto;
- import com.crm.rely.backend.core.dto.base.ResultWithPagerDto;
- import com.crm.rely.backend.core.exception.ServiceException;
- import com.crm.rely.backend.core.pojo.table.SysConfigTable;
- import com.crm.rely.backend.model.config.VaultodyConfig;
- import com.crm.rely.backend.model.constant.ConfigConstants;
- import com.crm.rely.backend.model.dto.vaultody.vaults.TransactionItemDto;
- import com.crm.rely.backend.model.dto.vaultody.vaults.VaultTransaction;
- import com.crm.rely.backend.model.dto.vaultody.vaults.VaultodyVaultsListDto;
- import com.crm.rely.backend.model.dto.vaultody.vaults.response.ResponseData;
- import com.crm.rely.backend.model.dto.vaultody.vaults.response.TransactionItem;
- import com.crm.rely.backend.model.dto.vaultody.vaults.response.TransactionResponse;
- import com.crm.rely.backend.model.dto.vaultody.vaults.response.VaultsListResponseDto;
- import com.crm.rely.backend.model.entity.vaultody.vaults.VaultTransactionsEntity;
- import com.crm.rely.backend.model.entity.vaultody.vaults.VaultTransactionsSearchEntity;
- import com.crm.rely.backend.model.pojo.table.SysVaultodyConfigTable;
- import com.crm.rely.backend.model.pojo.table.TransactionItemTable;
- import com.crm.rely.backend.util.AESUtil;
- import com.crm.rely.backend.util.HttpUtil;
- import com.crm.rely.backend.util.UUIDUtil;
- import com.google.common.collect.Lists;
- import lombok.extern.slf4j.Slf4j;
- import org.apache.commons.lang3.StringUtils;
- import org.jsoup.Connection;
- import org.springframework.beans.BeanUtils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import org.springframework.util.CollectionUtils;
- import org.springframework.util.ObjectUtils;
- import javax.crypto.Mac;
- import javax.crypto.spec.SecretKeySpec;
- import java.io.IOException;
- import java.nio.charset.StandardCharsets;
- import java.util.*;
- import java.util.stream.Collectors;
- @Slf4j
- @Service
- public class VaultodyServiceImpl implements VaultodyService {
- @Autowired
- private SysVaultodyConfigService vaultodyConfigService;
- @Autowired
- private SysConfigService sysConfigService;
- @Autowired
- private TransactionItemRepository transactionItemRepository;
- @Autowired
- private TransactionItemMapper transactionItemMapper;
- public static String getSignature(String message, String apiSecret) {
- try {
- byte[] decodedSecret = Base64.getDecoder().decode(apiSecret);
- Mac mac = Mac.getInstance("HmacSHA256");
- SecretKeySpec secretKeySpec = new SecretKeySpec(decodedSecret, "HmacSHA256");
- mac.init(secretKeySpec);
- byte[] hash = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
- return Base64.getEncoder().encodeToString(hash);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
- public static void main2(String[] args) {
- String s = "{\n" +
- " \"apiKey\": \"6f3cc6caf513a5cde2df5d3ed805e3703d4d43b2\",\n" +
- " \"apiSecret\": \"MLjTUAYgxSM2dg==\",\n" +
- " \"passphrase\": \"7UGMi2*t0h\",\n" +
- " \"vaultId\": \"\",\n" +
- " \"baseUrl\": \"https://rest.vaultody.com\",\n" +
- " \"vaultsListUrl\": \"/vaults/test\",\n" +
- " \"network\": \"\",\n" +
- " \"vaultsTransactionsPathTemplate\": \"/vaults/%s/transactions\",\n" +
- " \"webhooksPassphrase\": \"\"\n" +
- "}";
- System.out.println(AESUtil.encrypt(s, "bfa5559109f94c78af615bcf00d52060"));
- System.out.println(AESUtil.decrypt(AESUtil.encrypt(s, "bfa5559109f94c78af615bcf00d52060"), "bfa5559109f94c78af615bcf00d52060"));
- }
- public static void main(String[] args) {
- String s = "{\n" +
- " \"apiKey\": \"6f3cc6caf513a5cde2df5d3ed805e3703d4d43b2\",\n" +
- " \"apiSecret\": \"MLjTUAYgxSM2dg==\",\n" +
- " \"passphrase\": \"7UGMi2*t0h\",\n" +
- " \"vaultId\": \"\",\n" +
- " \"baseUrl\": \"https://rest.vaultody.com\",\n" +
- " \"vaultsListUrl\": \"/vaults/%s\",\n" +
- " \"networkType\": \"test\",\n" +
- " \"vaultsTransactionsPathTemplate\": \"/vaults/%s/transactions\",\n" +
- " \"webhooksPassphrase\": \"\",\n" +
- " \"vaultodyList\": [\n" +
- " {\n" +
- " \"apiKey\": \"6f3cc6caf513a5cde2df5d3ed805e3703d4d43b2\",\n" +
- " \"apiSecret\": \"MLjTUAYgxSM2dg==\",\n" +
- " \"passphrase\": \"7UGMi2*t0h\",\n" +
- " \"vaultId\": \"69cb34038d64830006453c0c\",\n" +
- " \"baseUrl\": \"https://rest.vaultody.com\",\n" +
- " \"vaultsListUrl\": \"/vaults/%s\",\n" +
- " \"networkType\": \"test\",\n" +
- " \"vaultsTransactionsPathTemplate\": \"/vaults/%s/transactions\",\n" +
- " \"webhooksPassphrase\": \"\"\n" +
- " },\n" +
- " {\n" +
- " \"apiKey\": \"002key\",\n" +
- " \"apiSecret\": \"002pwd\",\n" +
- " \"passphrase\": \"7UGMi2*t0h\",\n" +
- " \"vaultId\": \"002\",\n" +
- " \"baseUrl\": \"https://rest.vaultody.com\",\n" +
- " \"vaultsListUrl\": \"/vaults/%s\",\n" +
- " \"networkType\": \"test\",\n" +
- " \"vaultsTransactionsPathTemplate\": \"/vaults/%s/transactions\",\n" +
- " \"webhooksPassphrase\": \"\"\n" +
- " }\n" +
- " ]\n" +
- "}";
- System.out.println(AESUtil.encrypt(s, "bfa5559109f94c78af615bcf00d52060"));
- System.out.println(AESUtil.decrypt(AESUtil.encrypt(s, "bfa5559109f94c78af615bcf00d52060"), "bfa5559109f94c78af615bcf00d52060"));
- }
- /**
- * Coin
- */
- public static void main1(String[] args) {
- try {
- List<VaultodyVaultsListDto> dtos = new ArrayList<>();
- // ------------------ 配置 ------------------
- String apiKey = "6f3cc6caf513a5cde2df5d3ed805e3703d4d43b2";
- String apiSecret = "MLjTUAYgxSM2dg=="; // Base64编码的secret
- String passphrase = "7UGMi2*t0h";
- String method = "GET";
- String requestPath = "/vaults/test";
- String baseUrl = "https://rest.vaultody.com";
- String query = "{}"; // POST 时 query 通常为空,否则按接口要求填写
- String body = "{}"; // POST 时 query 通常为空,否则按接口要求填写
- // ------------------ 时间戳(秒) ------------------
- String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
- // ------------------ 构建消息用于签名 ------------------
- String message = timestamp + method + requestPath + body + query;
- System.out.println("Message: " + message);
- String signature = getSignature(message, apiSecret);
- System.out.println("Signature: " + signature);
- Map<String, String> headers = new HashMap<>();
- headers.put("x-api-key", apiKey);
- headers.put("x-api-sign", signature);
- headers.put("x-api-timestamp", timestamp);
- headers.put("x-api-passphrase", passphrase);
- headers.put("Content-Type", "application/json");
- try {
- Connection.Response response = HttpUtil.get(baseUrl + requestPath, headers);
- if (response.statusCode() != 200){
- System.out.println("Error: " + response.statusMessage());
- }
- VaultsListResponseDto responseDto = JSON.parseObject(response.body(), VaultsListResponseDto.class);
- List<VaultsListResponseDto.Item> items = responseDto.getData().getItems();
- for (VaultsListResponseDto.Item item : items){
- VaultodyVaultsListDto dto = new VaultodyVaultsListDto();
- BeanUtils.copyProperties(item, dto);
- dtos.add(dto);
- }
- System.out.println(JSON.toJSONString(dtos));
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * 交易记录接口拼接url
- */
- public String getPath(String pathTemplate, String vaultId) {
- // String pathTemplate = "/vaults/%s/transactions";
- String actualPath = String.format(pathTemplate, vaultId);
- return actualPath;
- }
- /**
- * 交易记录接口拼接url
- */
- public String getVaultsPath(String pathTemplate, String networkType) {
- // String pathTemplate = "/vaults/{networkType}";
- String actualPath = String.format(pathTemplate, networkType);
- return actualPath;
- }
- public VaultodyConfig getVaultodyConfig() {
- SysVaultodyConfigTable configTable = vaultodyConfigService.getByCode(ConfigConstants.VAULTODY_MANAGER_CONFIG);
- if (configTable == null) {
- throw ServiceException.exception(Constants.SYSTEM_ERROR);
- }
- String aesKey = getPropertyKey();
- String property = AESUtil.decrypt(configTable.getValue(), aesKey);
- VaultodyConfig vaultodyConfig = JSON.parseObject(property, VaultodyConfig.class);
- return vaultodyConfig;
- }
- public VaultodyConfig getVaultodyConfig(String vaultId) {
- SysVaultodyConfigTable configTable = vaultodyConfigService.getByCode(ConfigConstants.VAULTODY_MANAGER_CONFIG);
- if (configTable == null) {
- throw ServiceException.exception(Constants.SYSTEM_ERROR);
- }
- String aesKey = getPropertyKey();
- String property = AESUtil.decrypt(configTable.getValue(), aesKey);
- VaultodyConfig vaultodyConfig = JSON.parseObject(property, VaultodyConfig.class);
- List<VaultodyConfig> vaultodyList = vaultodyConfig.getVaultodyList();
- if(CollectionUtils.isEmpty(vaultodyList)){
- throw ServiceException.exception(Constants.NOT_PERMIT);
- }
- Map<String, VaultodyConfig> list = vaultodyList.stream().collect(Collectors.toMap(VaultodyConfig::getVaultId, v -> v));
- VaultodyConfig config = new VaultodyConfig();
- if(!list.containsKey(vaultId)){
- throw ServiceException.exception(Constants.NOT_PERMIT);
- }
- config = list.get(vaultId);
- return config;
- }
- private String getPropertyKey() throws ServiceException {
- SysConfigTable table = sysConfigService.getByCode(ConfigConstants.VAULTODY_FINANCE_PROPERTY_KEY);
- if (table == null) {
- throw ServiceException.exception(Constants.SYSTEM_ERROR);
- }
- return table.getValue();
- }
- @Override
- public BaseResultDto vaultsList() throws Exception {
- List<VaultodyVaultsListDto> dtos = new ArrayList<>();
- VaultodyConfig config = getVaultodyConfig();
- String apiKey = config.getApiKey();
- String apiSecret = config.getApiSecret();
- String passphrase = config.getPassphrase();
- String method = "GET";
- String requestPath = getVaultsPath(config.getVaultsListUrl(), config.getNetworkType());
- String baseUrl = config.getBaseUrl();
- String query = "{}"; // POST 时 query 通常为空,否则按接口要求填写
- String body = "{}";
- // ------------------ 时间戳(秒) ------------------
- String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
- // ------------------ 构建消息用于签名 ------------------
- String message = timestamp + method + requestPath + body + query;
- String signature = getSignature(message, apiSecret);
- System.out.println("Signature: " + signature);
- Map<String, String> headers = new HashMap<>();
- headers.put("x-api-key", apiKey);
- headers.put("x-api-sign", signature);
- headers.put("x-api-timestamp", timestamp);
- headers.put("x-api-passphrase", passphrase);
- headers.put("Content-Type", "application/json");
- Connection.Response response = HttpUtil.get(baseUrl + requestPath, headers);
- if (response.statusCode() != 200){
- return BaseResultDto.error(response.statusMessage());
- }
- VaultsListResponseDto responseDto = JSON.parseObject(response.body(), VaultsListResponseDto.class);
- List<VaultsListResponseDto.Item> items = responseDto.getData().getItems();
- for (VaultsListResponseDto.Item item : items){
- VaultodyVaultsListDto dto = new VaultodyVaultsListDto();
- BeanUtils.copyProperties(item, dto);
- dtos.add(dto);
- }
- return BaseResultDto.success(dtos);
- }
- public VaultTransaction query3Items(VaultTransactionsEntity entity,VaultodyConfig config) throws Exception{
- String apiKey = config.getApiKey();
- String apiSecret = config.getApiSecret(); // Base64编码的secret
- String passphrase = config.getPassphrase();
- String method = "GET";
- // String requestPath = "/vaults/"+entity.getVaultId()+"/transactions";
- String requestPath = getPath(config.getVaultsTransactionsPathTemplate(), entity.getVaultId());
- String baseUrl = config.getBaseUrl();
- String query = "{}";
- Map<String, String> params = new HashMap();
- if(!ObjectUtils.isEmpty(entity.getLimit())){
- params.put("limit", String.valueOf(entity.getLimit()));
- }
- if(StringUtils.isNotBlank(entity.getStartingAfter())){
- params.put("startingAfter", entity.getStartingAfter());
- }
- if (StringUtils.isNotBlank(entity.getContext())){
- params.put("context", entity.getContext());
- }
- // ------------------ 时间戳(秒) ------------------
- String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
- // ------------------ 构建消息用于签名 ------------------
- String message = timestamp + method + requestPath + query + JSON.toJSONString(params);
- String signature = getSignature(message, apiSecret);
- log.info("Signature: {}",signature);
- Map<String, String> headers = new HashMap<>();
- headers.put("x-api-key", apiKey);
- headers.put("x-api-sign", signature);
- headers.put("x-api-timestamp", timestamp);
- headers.put("x-api-passphrase", passphrase);
- headers.put("Content-Type", "application/json");
- Connection.Response response = HttpUtil.get(baseUrl + requestPath, headers, params);
- log.info("response.statusCode: {} , response.body: {}",response.statusCode() , response.body());
- if (response.statusCode() != 200) {
- log.error("response.statusCode() != 200,{}","3Item Error!");
- throw ServiceException.exception(Constants.SYSTEM_ERROR);
- }
- TransactionResponse responseDto = JSON.parseObject(response.body(), TransactionResponse.class);
- ResponseData data = responseDto.getData();
- VaultTransaction vaultTransaction = getVaultTransaction(data);
- return vaultTransaction;
- }
- private VaultTransaction getVaultTransaction(ResponseData data) {
- VaultTransaction vaultTransaction = new VaultTransaction();
- vaultTransaction.setHasMore(data.getHasMore());
- vaultTransaction.setLimit(data.getLimit());
- vaultTransaction.setStartingAfter(data.getStartingAfter());
- List<TransactionItemDto> list = Lists.newArrayList();
- for (TransactionItem item : data.getItems()) {
- TransactionItemDto dto = new TransactionItemDto();
- dto.setRequestId(UUIDUtil.getUUID());
- dto.setId(item.getId());
- dto.setTransactionId(item.getTransactionId());
- dto.setStatus(item.getStatus());
- dto.setCreatedTimestamp(item.getCreatedTimestamp());
- dto.setSenderAddress(item.getSenders().get(0).getAddress());
- dto.setSenderIsVaultAddress(item.getSenders().get(0).getIsVaultAddress());
- dto.setSenderAmountUnit(item.getSenders().get(0).getAmountUnit());
- dto.setSenderAmount(item.getSenders().get(0).getAmount());
- dto.setRecipientAddress(item.getRecipients().get(0).getAddress());
- dto.setRecipientIsVaultAddress(item.getRecipients().get(0).getIsVaultAddress());
- dto.setRecipientAmountUnit(item.getRecipients().get(0).getAmountUnit());
- dto.setRecipientAmount(item.getRecipients().get(0).getAmount());
- dto.setBlockchain(item.getBlockchain());
- dto.setMinedInBlockHeight(item.getMinedInBlockHeight());
- dto.setFeeAmount(item.getTransactionFee().getAmount());
- dto.setFeeAmountUnit(item.getTransactionFee().getAmountUnit());
- dto.setSenderLabel(item.getSenders().get(0).getLabel());
- dto.setRecipientLabel(item.getRecipients().get(0).getLabel());
- list.add(dto);
- }
- vaultTransaction.setList(list);
- return vaultTransaction;
- }
- @Override
- public void batchSave(List<TransactionItemTable> tables) {
- transactionItemRepository.saveAll(tables);
- }
- @Override
- public List<TransactionItemTable> finAllByVaultId(String vaultId) {
- return transactionItemRepository.findAllByVaultId(vaultId);
- }
- @Override
- public BaseResultDto searchList(VaultTransactionsSearchEntity entity) throws Exception {
- List<TransactionItemTable> tables = new LinkedList<>();
- VaultodyConfig vaultodyConfig = getVaultodyConfig(entity.getVaultId());
- List<TransactionItemDto> list = queryWithFilter(vaultodyConfig);
- if(!CollectionUtils.isEmpty(list)){
- for (TransactionItemDto transactionItemDto : list) {
- TransactionItemTable table = new TransactionItemTable() ;
- BeanUtils.copyProperties(transactionItemDto, table);
- table.setVaultId(entity.getVaultId());
- table.setItemId(transactionItemDto.getId());
- tables.add(table);
- }
- batchSave(tables);
- }
- Long startSecond = null;
- Long endSecond = null;
- if(entity.getStartTime() != null){
- startSecond = DateUtils.dateToSecondTimestamp(entity.getStartTime());
- }
- if (entity.getEndTime() != null){
- endSecond = DateUtils.dateToSecondTimestamp(entity.getEndTime());
- }
- Integer count = transactionItemMapper.countList(entity,startSecond,endSecond);
- if (count == null || count <= 0) {
- return ResultWithPagerDto.success(new PageDto(), new ArrayList<>());
- }
- PageDto pageDto = PageDto.format(entity, count);
- List<TransactionItemTable> dtos = transactionItemMapper.pageList(entity,startSecond,endSecond);
- if (dtos == null || dtos.size() <= 0) {
- throw new ServiceException(Constants.SYSTEM_ERROR);
- }
- return ResultWithPagerDto.success(pageDto, dtos);
- }
- public List<TransactionItemDto> queryWithFilter(VaultodyConfig config) throws Exception {
- VaultTransactionsEntity entity = new VaultTransactionsEntity();
- entity.setVaultId(config.getVaultId());
- List<TransactionItemDto> result = new ArrayList<>();
- // 先获取数据库中已存在的item ID列表
- List<String> existingItemIds = recordByVaultId(config.getVaultId());
- Set<String> existingIdSet = new HashSet<>(existingItemIds);
- VaultTransaction vaultTransaction = query3Items(entity,config);
- // 处理第一页数据
- if (vaultTransaction.getList() != null && !vaultTransaction.getList().isEmpty()) {
- List<TransactionItemDto> filteredList = filterExistingItems(vaultTransaction.getList(), existingIdSet);
- result.addAll(filteredList);
- }
- // 分页查询剩余数据
- while (Boolean.TRUE.equals(vaultTransaction.getHasMore())
- && vaultTransaction.getList() != null
- && !vaultTransaction.getList().isEmpty()) {
- String lastId = vaultTransaction.getList().get(vaultTransaction.getList().size() - 1).getId();
- entity.setStartingAfter(lastId);
- vaultTransaction = query3Items(entity,config);
- if (vaultTransaction.getList() != null && !vaultTransaction.getList().isEmpty()) {
- List<TransactionItemDto> filteredList = filterExistingItems(vaultTransaction.getList(), existingIdSet);
- result.addAll(filteredList);
- } else {
- break;
- }
- }
- return result;
- }
- private List<TransactionItemDto> filterExistingItems(List<TransactionItemDto> items, Set<String> existingIdSet) {
- if (items == null || items.isEmpty()) {
- return Collections.emptyList();
- }
- return items.stream()
- .filter(item -> item != null && item.getId() != null)
- .filter(item -> !existingIdSet.contains(item.getId()))
- .collect(Collectors.toList());
- }
- private List<String> recordByVaultId(String vaultId){
- List<TransactionItemTable> list = finAllByVaultId(vaultId);
- if(CollectionUtils.isEmpty(list)){
- return new ArrayList<>();
- }
- return list.stream().map(TransactionItemTable::getItemId).toList();
- }
- }
|