mirror of
https://github.com/tiennm99/exchange-rate-export.git
synced 2026-10-11 03:13:21 +00:00
feat: server api
- tcb: work - bidv: not work
This commit is contained in:
1 parent
59d8625952
commit
f3debe9cd9
2 files changed
+179
-140
No files matched your search
@@ -14,165 +14,68 @@
|
|||||||
let results = [];
|
let results = [];
|
||||||
let showTable = false;
|
let showTable = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Format date for display
|
// Format date for display
|
||||||
const formatDisplayDate = (dateStr) => {
|
const formatDisplayDate = (dateStr) => {
|
||||||
const [year, month, day] = dateStr.split('-');
|
try {
|
||||||
return `${day}/${month}/${year}`;
|
// Handle both yyyy-MM-dd and dd/MM/yyyy formats
|
||||||
};
|
let date;
|
||||||
|
if (dateStr.includes('/')) {
|
||||||
// Get date range between start and end dates
|
const [day, month, year] = dateStr.split('/');
|
||||||
const getDateRange = (start, end) => {
|
date = new Date(`${year}-${month}-${day}`);
|
||||||
const dates = [];
|
} else {
|
||||||
let current = new Date(start);
|
date = new Date(dateStr);
|
||||||
const last = new Date(end);
|
}
|
||||||
|
return format(date, 'dd/MM/yyyy');
|
||||||
while (current <= last) {
|
} catch (e) {
|
||||||
dates.push(new Date(current));
|
return dateStr; // Return as is if parsing fails
|
||||||
current.setDate(current.getDate() + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return dates;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Format date for API requests
|
// Handle form submission
|
||||||
const formatDateForApi = (date) => {
|
|
||||||
const d = new Date(date);
|
|
||||||
const day = String(d.getDate()).padStart(2, '0');
|
|
||||||
const month = String(d.getMonth() + 1).padStart(2, '0');
|
|
||||||
const year = d.getFullYear();
|
|
||||||
return `${day}/${month}/${year}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fetch BIDV exchange rates
|
|
||||||
const fetchBIDVRates = async (dateObj) => {
|
|
||||||
const dateStr = format(dateObj, 'dd/MM/yyyy');
|
|
||||||
|
|
||||||
// First API call to get namerecord
|
|
||||||
const timeRes = await fetch('https://bidv.com.vn/ServicesBIDV/ExchangeDetailSearchTimeServlet', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
|
||||||
},
|
|
||||||
body: `date=${encodeURIComponent(dateStr)}`
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!timeRes.ok) throw new Error('Failed to fetch BIDV time data');
|
|
||||||
const timeData = await timeRes.json();
|
|
||||||
|
|
||||||
if (timeData.status !== 1 || !timeData.data?.length) return null;
|
|
||||||
|
|
||||||
// Get the latest record
|
|
||||||
const latest = timeData.data.reduce((latest, current) =>
|
|
||||||
current.time > latest.time ? current : latest, timeData.data[0]);
|
|
||||||
|
|
||||||
// Second API call to get exchange rates
|
|
||||||
const rateRes = await fetch('https://bidv.com.vn/ServicesBIDV/ExchangeDetailServlet', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
|
||||||
},
|
|
||||||
body: `date=${encodeURIComponent(dateStr)}&time=${latest.namerecord}`
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!rateRes.ok) throw new Error('Failed to fetch BIDV rate data');
|
|
||||||
const rateData = await rateRes.json();
|
|
||||||
|
|
||||||
if (rateData.status !== 1 || !rateData.data) return null;
|
|
||||||
|
|
||||||
// Find USD data
|
|
||||||
const usd = rateData.data.find(item => item.currency === 'USD');
|
|
||||||
if (!usd) return null;
|
|
||||||
|
|
||||||
return {
|
|
||||||
date: dateStr,
|
|
||||||
nameVI: usd.nameVI || '',
|
|
||||||
muaTm: usd.muaTm || '',
|
|
||||||
muaCk: usd.muaCk || '',
|
|
||||||
currency: usd.currency || 'USD',
|
|
||||||
nameEN: usd.nameEN || '',
|
|
||||||
ban: usd.ban || ''
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fetch TCB exchange rates
|
|
||||||
const fetchTCBRates = async (dateObj) => {
|
|
||||||
const dateStr = format(dateObj, 'yyyy-MM-dd');
|
|
||||||
const url = `https://techcombank.com/content/techcombank/web/vn/vi/cong-cu-tien-ich/ty-gia/_jcr_content.exchange-rates.${dateStr}.integration.json`;
|
|
||||||
|
|
||||||
const res = await fetch(url);
|
|
||||||
if (!res.ok) throw new Error('Failed to fetch TCB data');
|
|
||||||
|
|
||||||
const data = await res.json();
|
|
||||||
if (!data.exchangeRate?.data) return null;
|
|
||||||
|
|
||||||
// Find USD data
|
|
||||||
const usd = data.exchangeRate.data.find(item => item.label === 'USD (50,100)');
|
|
||||||
if (!usd) return null;
|
|
||||||
|
|
||||||
return {
|
|
||||||
date: formatDisplayDate(dateStr),
|
|
||||||
label: usd.label || '',
|
|
||||||
askRate: usd.askRate || '',
|
|
||||||
bidRateCK: usd.bidRateCK || '',
|
|
||||||
bidRateTM: usd.bidRateTM || '',
|
|
||||||
askRateTM: usd.askRateTM || '',
|
|
||||||
sourceCurrency: usd.sourceCurrency || 'USD',
|
|
||||||
targetCurrency: usd.targetCurrency || 'VND'
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle fetch button click
|
|
||||||
const handleFetch = async () => {
|
const handleFetch = async () => {
|
||||||
if (!startDate || !endDate) {
|
if (!startDate || !endDate) {
|
||||||
error = 'Please select both start and end dates';
|
error = 'Please select both start and end dates';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const start = new Date(startDate);
|
|
||||||
const end = new Date(endDate);
|
|
||||||
|
|
||||||
if (start > end) {
|
|
||||||
error = 'Start date must be before end date';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dateRange = getDateRange(start, end);
|
|
||||||
if (dateRange.length > 31) {
|
|
||||||
error = 'Date range cannot exceed 31 days';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
isLoading = true;
|
|
||||||
error = '';
|
error = '';
|
||||||
|
isLoading = true;
|
||||||
results = [];
|
results = [];
|
||||||
showTable = false;
|
showTable = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const fetchPromises = dateRange.map(async (date) => {
|
const start = new Date(startDate);
|
||||||
try {
|
const end = new Date(endDate);
|
||||||
if (selectedBank === 'bidv') {
|
|
||||||
return await fetchBIDVRates(date);
|
if (start > end) {
|
||||||
} else {
|
error = 'Start date cannot be after end date';
|
||||||
return await fetchTCBRates(date);
|
return;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
console.error(`Error fetching data for ${date}:`, e);
|
// Call our server endpoint
|
||||||
return null;
|
const response = await fetch('/api/exchange-rates', {
|
||||||
}
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
startDate: start.toISOString().split('T')[0],
|
||||||
|
endDate: end.toISOString().split('T')[0],
|
||||||
|
bank: selectedBank
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchedResults = await Promise.all(fetchPromises);
|
if (!response.ok) {
|
||||||
results = fetchedResults.filter(Boolean);
|
const errorData = await response.json();
|
||||||
showTable = results.length > 0;
|
throw new Error(errorData.error || 'Failed to fetch exchange rates');
|
||||||
|
|
||||||
if (results.length === 0) {
|
|
||||||
error = 'No data found for the selected date range';
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
console.error('Error fetching data:', e);
|
const data = await response.json();
|
||||||
error = 'An error occurred while fetching data. Please try again.';
|
results = data.data || [];
|
||||||
|
showTable = true;
|
||||||
|
} catch (err) {
|
||||||
|
error = `An error occurred while fetching exchange rates: ${err.message}`;
|
||||||
|
console.error(err);
|
||||||
} finally {
|
} finally {
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
import { json } from '@sveltejs/kit';
|
||||||
|
import { format, parse } from 'date-fns';
|
||||||
|
|
||||||
|
// Helper function to fetch BIDV exchange rates
|
||||||
|
async function fetchBIDVRates(dateObj) {
|
||||||
|
const dateStr = format(dateObj, 'dd/MM/yyyy');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// First API call to get namerecord
|
||||||
|
const timeRes = await fetch('https://bidv.com.vn/ServicesBIDV/ExchangeDetailSearchTimeServlet', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
body: `date=${encodeURIComponent(dateStr)}`
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!timeRes.ok) throw new Error('Failed to fetch BIDV time data');
|
||||||
|
const timeData = await timeRes.json();
|
||||||
|
|
||||||
|
if (timeData.status !== 1 || !timeData.data?.length) return null;
|
||||||
|
|
||||||
|
// Get the latest record
|
||||||
|
const latest = timeData.data.reduce((latest, current) =>
|
||||||
|
current.time > latest.time ? current : latest, timeData.data[0]);
|
||||||
|
|
||||||
|
// Second API call to get exchange rates
|
||||||
|
const rateRes = await fetch('https://bidv.com.vn/ServicesBIDV/ExchangeDetailServlet', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
body: `date=${encodeURIComponent(dateStr)}&time=${latest.namerecord}`
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!rateRes.ok) throw new Error('Failed to fetch BIDV rate data');
|
||||||
|
const rateData = await rateRes.json();
|
||||||
|
|
||||||
|
if (rateData.status !== 1 || !rateData.data) return null;
|
||||||
|
|
||||||
|
// Find USD data
|
||||||
|
const usd = rateData.data.find(item => item.currency === 'USD');
|
||||||
|
if (!usd) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
date: dateStr,
|
||||||
|
nameVI: usd.nameVI || '',
|
||||||
|
muaTm: usd.muaTm || '',
|
||||||
|
muaCk: usd.muaCk || '',
|
||||||
|
currency: usd.currency || 'USD',
|
||||||
|
nameEN: usd.nameEN || '',
|
||||||
|
ban: usd.ban || ''
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching BIDV rates:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to fetch TCB exchange rates
|
||||||
|
async function fetchTCBRates(dateObj) {
|
||||||
|
const dateStr = format(dateObj, 'yyyy-MM-dd');
|
||||||
|
const url = `https://techcombank.com/content/techcombank/web/vn/vi/cong-cu-tien-ich/ty-gia/_jcr_content.exchange-rates.${dateStr}.integration.json`;
|
||||||
|
|
||||||
|
const res = await fetch(url);
|
||||||
|
if (!res.ok) throw new Error('Failed to fetch TCB data');
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
if (!data.exchangeRate?.data) return null;
|
||||||
|
|
||||||
|
// Find USD data
|
||||||
|
const usd = data.exchangeRate.data.find(item => item.label === 'USD (50,100)');
|
||||||
|
if (!usd) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
date: dateStr,
|
||||||
|
label: usd.label || '',
|
||||||
|
askRate: usd.askRate || '',
|
||||||
|
bidRateCK: usd.bidRateCK || '',
|
||||||
|
bidRateTM: usd.bidRateTM || '',
|
||||||
|
askRateTM: usd.askRateTM || '',
|
||||||
|
sourceCurrency: usd.sourceCurrency || 'USD',
|
||||||
|
targetCurrency: usd.targetCurrency || 'VND'
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get date range between start and end dates
|
||||||
|
function getDateRange(start, end) {
|
||||||
|
const dates = [];
|
||||||
|
let current = new Date(start);
|
||||||
|
const last = new Date(end);
|
||||||
|
|
||||||
|
while (current <= last) {
|
||||||
|
dates.push(new Date(current));
|
||||||
|
current.setDate(current.getDate() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dates;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST({ request }) {
|
||||||
|
try {
|
||||||
|
const { startDate, endDate, bank } = await request.json();
|
||||||
|
|
||||||
|
if (!startDate || !endDate || !bank) {
|
||||||
|
return json({ error: 'Missing required parameters' }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const dates = getDateRange(new Date(startDate), new Date(endDate));
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
for (const date of dates) {
|
||||||
|
let rate = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (bank === 'bidv') {
|
||||||
|
rate = await fetchBIDVRates(date);
|
||||||
|
} else if (bank === 'tcb') {
|
||||||
|
rate = await fetchTCBRates(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rate) {
|
||||||
|
results.push(rate);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error fetching rates for ${date}:`, error);
|
||||||
|
// Continue with next date even if one fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return json({ data: results });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Server error:', error);
|
||||||
|
return json({ error: 'Internal server error' }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user