修复登录报错提示内容错误,增加节假日同步功能
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
const https = require('https');
|
||||
|
||||
/**
|
||||
* Fetch holiday data from timor.tech API for a specific year
|
||||
* @param {number} year
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
function fetchHolidaysFromNetwork(year) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Note: timor.tech API works best with trailing slash
|
||||
const url = `https://timor.tech/api/holiday/year/${year}/`;
|
||||
https.get(url, { headers: { 'User-Agent': 'WorkTime-Manager/1.0' } }, (res) => {
|
||||
let rawData = '';
|
||||
res.on('data', (chunk) => { rawData += chunk; });
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(rawData);
|
||||
if (parsed.code === 0 && parsed.holiday) {
|
||||
resolve(parsed.holiday);
|
||||
} else {
|
||||
reject(new Error(`API returned code ${parsed.code}`));
|
||||
}
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}).on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get holiday data for a specific year, from cache or network
|
||||
* @param {object} db DB wrapper instance
|
||||
* @param {number} year
|
||||
* @returns {Promise<object|null>}
|
||||
*/
|
||||
async function getHolidays(db, year) {
|
||||
try {
|
||||
const cached = db.get('SELECT holiday_data FROM holiday_cache WHERE year = ?', [year]);
|
||||
if (cached) {
|
||||
return JSON.parse(cached.holiday_data);
|
||||
}
|
||||
|
||||
// Not cached, fetch from network
|
||||
console.log(`[Holiday] Fetching holiday data from network for year ${year}...`);
|
||||
const holidays = await fetchHolidaysFromNetwork(year);
|
||||
|
||||
// Save to cache
|
||||
db.run(
|
||||
'INSERT OR REPLACE INTO holiday_cache (year, holiday_data, updated_at) VALUES (?, ?, datetime(\'now\', \'localtime\'))',
|
||||
[year, JSON.stringify(holidays)]
|
||||
);
|
||||
return holidays;
|
||||
} catch (e) {
|
||||
console.error(`[Holiday] Error getting holidays for ${year}:`, e.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to check if a date is weekend (Sat/Sun) in local time
|
||||
* @param {string} dateStr YYYY-MM-DD
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isWeekendDefault(dateStr) {
|
||||
const d = new Date(dateStr + 'T00:00:00');
|
||||
const day = d.getDay();
|
||||
return day === 0 || day === 6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the number of workdays for each month in a given year
|
||||
* @param {number} year
|
||||
* @param {object|null} holidayMap
|
||||
* @returns {Array<{month: number, days: number}>}
|
||||
*/
|
||||
function calculateMonthlyWorkdays(year, holidayMap) {
|
||||
const result = [];
|
||||
for (let m = 1; m <= 12; m++) {
|
||||
const daysInMonth = new Date(year, m, 0).getDate();
|
||||
let workdays = 0;
|
||||
|
||||
for (let day = 1; day <= daysInMonth; day++) {
|
||||
const dStr = `${year}-${String(m).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
|
||||
const mmDd = `${String(m).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
|
||||
|
||||
let isWork = true;
|
||||
if (holidayMap && holidayMap[mmDd]) {
|
||||
// If date is in the map, holiday === true means rest day, holiday === false means work day (調休补班)
|
||||
isWork = !holidayMap[mmDd].holiday;
|
||||
} else {
|
||||
// Fall back to default weekends
|
||||
isWork = !isWeekendDefault(dStr);
|
||||
}
|
||||
|
||||
if (isWork) {
|
||||
workdays++;
|
||||
}
|
||||
}
|
||||
result.push({ month: m, days: workdays });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync holiday data from network, calculate and update database workdays
|
||||
* @param {object} db
|
||||
* @param {number} year
|
||||
* @returns {Promise<Array<{month: number, days: number}>>}
|
||||
*/
|
||||
async function syncYearWorkdays(db, year) {
|
||||
// Clear cache first to force fresh fetch on manual sync request
|
||||
db.run('DELETE FROM holiday_cache WHERE year = ?', [year]);
|
||||
|
||||
const holidayMap = await getHolidays(db, year);
|
||||
if (!holidayMap) {
|
||||
throw new Error('无法从网络同步节假日数据,请检查网络连接');
|
||||
}
|
||||
|
||||
const monthlyWorkdays = calculateMonthlyWorkdays(year, holidayMap);
|
||||
|
||||
// Save to workdays table
|
||||
for (const item of monthlyWorkdays) {
|
||||
const existing = db.get('SELECT id FROM workdays WHERE year = ? AND month = ?', [year, item.month]);
|
||||
if (existing) {
|
||||
db.run('UPDATE workdays SET days = ? WHERE year = ? AND month = ?', [item.days, year, item.month]);
|
||||
} else {
|
||||
db.run('INSERT INTO workdays (year, month, days) VALUES (?, ?, ?)', [year, item.month, item.days]);
|
||||
}
|
||||
}
|
||||
|
||||
return monthlyWorkdays;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getHolidays,
|
||||
syncYearWorkdays,
|
||||
calculateMonthlyWorkdays
|
||||
};
|
||||
Reference in New Issue
Block a user