From 6fe12da9bef56fae2d1c59814aefe99d990f66b5 Mon Sep 17 00:00:00 2001 From: Zichao Lin Date: Thu, 25 Jul 2024 13:25:26 +0800 Subject: [PATCH] feat(uigf): import UIGFv4.0 BREAKING CHANGES: `region` is no longer stored in/read from local file --- src/i18n/English.json | 1 + src/i18n/简体中文.json | 1 + src/i18n/繁體中文.json | 1 + src/main/UIGFJson.js | 99 +++++++++++++++++++++++++++++++++++------- src/main/getData.js | 57 +++++++++++++++++------- src/renderer/App.vue | 15 ++++++- 6 files changed, 143 insertions(+), 31 deletions(-) diff --git a/src/i18n/English.json b/src/i18n/English.json index 22022af..03d7392 100644 --- a/src/i18n/English.json +++ b/src/i18n/English.json @@ -6,6 +6,7 @@ "ui.button.files": "Export Files", "ui.button.excel": "Export Excel", "ui.button.uigf": "Export UIGF (Beta)", + "ui.button.import": "Import UIGF (Beta)", "ui.button.url": "Input URL", "ui.button.setting": "Settings", "ui.button.option": "Option", diff --git a/src/i18n/简体中文.json b/src/i18n/简体中文.json index c2151cc..47de6ea 100644 --- a/src/i18n/简体中文.json +++ b/src/i18n/简体中文.json @@ -6,6 +6,7 @@ "ui.button.files": "导出文件", "ui.button.excel": "导出Excel", "ui.button.uigf":"导出UIGF (Beta)", + "ui.button.import":"导入UIGF (Beta)", "ui.button.url": "输入URL", "ui.button.setting": "设置", "ui.button.option": "选项", diff --git a/src/i18n/繁體中文.json b/src/i18n/繁體中文.json index 61d7bc9..6b7ba23 100644 --- a/src/i18n/繁體中文.json +++ b/src/i18n/繁體中文.json @@ -6,6 +6,7 @@ "ui.button.files": "導出文件", "ui.button.excel": "導出Excel", "ui.button.uigf":"導出UIGF (Beta)", + "ui.button.import":"導入UIGF (Beta)", "ui.button.url": "輸入URL", "ui.button.setting": "設置", "ui.button.option": "選項", diff --git a/src/main/UIGFJson.js b/src/main/UIGFJson.js index 21b8f3d..b19cd13 100644 --- a/src/main/UIGFJson.js +++ b/src/main/UIGFJson.js @@ -1,10 +1,12 @@ const { app, ipcMain, dialog } = require('electron') const fs = require('fs-extra') const path = require('path') -const getData = require('./getData').getData +const { getData, saveData, changeCurrent, convertTimeZone } = require('./getData') +const config = require('./config') const { name, version } = require('../../package.json') const i18n = require('./i18n') -const { exit } = require('process') +const { mergeData } = require('./utils/mergeData') +const { sendMsg } = require('./utils') const getTimeString = () => { return new Date().toLocaleString('sv').replace(/[- :]/g, '').slice(0, -2) @@ -16,8 +18,7 @@ const formatDate = (date) => { let d = `${date.getDate()}`.padStart(2, '0') return `${y}-${m}-${d} ${date.toLocaleString('zh-cn', { hour12: false }).slice(-8)}` } - -const start = async (uids) => { +const exportUIGF = async (uids) => { const result = { info: { export_timestamp: Math.ceil(Date.now() / 1000), @@ -35,16 +36,7 @@ const start = async (uids) => { if (!fulldata.length) { throw new Error('数据为空') } - const serverTimeZone = new Map([ - ["prod_gf_cn", 8], - ["prod_gf_jp", 8] - ]) fulldata.forEach(data => { - let timezone - timezone = serverTimeZone.get(data.region) - if(!timezone) { - throw new Error('不支持此服务器') - } const listTemp = [] for (let [type, arr] of data.result) { arr.forEach(log => { @@ -64,7 +56,7 @@ const start = async (uids) => { listTemp.sort((a, b) => Number(BigInt(a.id) - BigInt(b.id))) let dataTemp = { uid: data.uid, - timezone: timezone, + timezone: data.region_time_zone, lang: data.lang, list: [] } @@ -87,6 +79,83 @@ const start = async (uids) => { } } +const importUIGF = async () => { + const filepath = await dialog.showOpenDialogSync({ + properties: ['openFile'], + filters: [ + { name: i18n.uigf.fileType, extensions: ['json'] } + ] + }) + if (!filepath) return + const { dataMap, current } = await getData() + try { + const jsonData = fs.readJsonSync(filepath[0]) + if('info' in jsonData && 'version' in jsonData.info) { + if (jsonData.info.version !== 'v4.0') { + sendMsg('不支持此版本UIGF') + console.error('不支持此版本UIGF') + return + } + } else { + sendMsg('UIGF格式错误') + console.error('UIGF格式错误') + return + } + jsonData.nap.forEach(uidData => { + const resultTemp = [] + const isNew = !Boolean(dataMap.has(uidData.uid)) + let region_time_zone + if (!isNew) region_time_zone = dataMap.get(uidData.uid).region_time_zone + else region_time_zone = uidData.timezone + + uidData.list.forEach(recordEntry => { + resultTemp.push({ + gacha_id: recordEntry.gacha_id, + gacha_type: recordEntry.gacha_type, + item_id: recordEntry.item_id, + count: recordEntry.count, + time: convertTimeZone(recordEntry.time, uidData.timezone, region_time_zone), + name: recordEntry.name, + item_type: recordEntry.item_type, + rank_type: recordEntry.rank_type, + id: recordEntry.id + }) + }) + const resultTempGrouped = resultTemp.reduce((acc, curr) => { + if (!acc[curr.gacha_type]) { + acc[curr.gacha_type] = [] + } + acc[curr.gacha_type].push(curr) + return acc; + }, {}) + const resultTempMap = new Map(Object.entries(resultTempGrouped)) + const resultMap = { result: resultTempMap, uid: uidData.uid} + let data + const mergedData = mergeData(dataMap.get(uidData.uid), resultMap) + if (isNew) { + data = { result: mergedData, time: Date.now(), uid: uidData.uid, lang: uidData.lang, region_time_zone: uidData.timezone, deleted: false } + } else { + data = { result: mergedData, time: Date.now(), uid: dataMap.get(uidData.uid).uid, lang: dataMap.get(uidData.uid).lang, region_time_zone: dataMap.get(uidData.uid).region_time_zone, deleted: dataMap.get(uidData.uid).deleted } + } + + saveData(data, '') + changeCurrent(uidData.uid) + dataMap.set(uidData.uid, data) + }) + return { + dataMap, + current: config.current + } + } catch (error) { + sendMsg(error, 'ERROR') + console.error(error) + } +} + ipcMain.handle('EXPORT_UIGF_JSON', async (event, uids) => { - await start(uids) + await exportUIGF(uids) }) + +ipcMain.handle('IMPORT_UIGF_JSON', async () => { + return await importUIGF() +}) \ No newline at end of file diff --git a/src/main/getData.js b/src/main/getData.js index 7bc52de..0f1fc03 100644 --- a/src/main/getData.js +++ b/src/main/getData.js @@ -29,6 +29,24 @@ const defaultTypeMap = new Map([ ['5', '邦布频段'] ]) +const serverTimeZone = new Map([ + ["prod_gf_cn", 8], + ["prod_gf_jp", 8] +]) + +const convertTimeZone = (dateTimeStr, fromTimeZoneOffset, toTimeZoneOffset) => { + let date = new Date(dateTimeStr.replace(' ', 'T') + 'Z'); + let utcDate = new Date(date.getTime() - fromTimeZoneOffset * 60 * 60 * 1000); + let targetDate = new Date(utcDate.getTime() + toTimeZoneOffset * 60 * 60 * 1000); + let year = targetDate.getUTCFullYear(); + let month = String(targetDate.getUTCMonth() + 1).padStart(2, '0'); + let day = String(targetDate.getUTCDate()).padStart(2, '0'); + let hours = String(targetDate.getUTCHours()).padStart(2, '0'); + let minutes = String(targetDate.getUTCMinutes()).padStart(2, '0'); + let seconds = String(targetDate.getUTCSeconds()).padStart(2, '0'); + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; +} + const findDataFiles = async (dataPath, fileMap) => { const files = await readdir(dataPath) if (files?.length) { @@ -203,7 +221,6 @@ const getGachaLogs = async ({ name, key }, queryString) => { let logs = [] let uid = '' let region = '' - let region_time_zone = '' let endId = '0' const url = `${apiDomain}/common/gacha_record/api/getGachaLog?${queryString}` do { @@ -221,9 +238,6 @@ const getGachaLogs = async ({ name, key }, queryString) => { if (!region) { region = res.region } - if (!region_time_zone) { - region_time_zone = res.region_time_zone - } list.push(...logs) page += 1 @@ -252,7 +266,7 @@ const getGachaLogs = async ({ name, key }, queryString) => { } } } while (logs.length > 0) - return { list, uid, region, region_time_zone } + return { list, uid, region } } const checkResStatus = (res) => { @@ -425,10 +439,25 @@ const fetchData = async (urlOverride) => { const typeMap = new Map() const lang = searchParams.get('lang') let originUid = '' - let originRegion = '' - let originTimeZone = '' + let localTimeZone for (const type of gachaType) { - const { list, uid, region, region_time_zone } = await getGachaLogs(type, queryString) + const { list, uid, region} = await getGachaLogs(type, queryString) + const region_time_zone = serverTimeZone.get(region) + if(!region_time_zone) { + sendMsg('不支持此服务器') + console.error('不支持此服务器') + return + } + if (localTimeZone === undefined) { + localTimeZone = dataMap.get(uid)?.region_time_zone + if (localTimeZone === undefined) { + localTimeZone = region_time_zone + } + } + localTimeZone === Number(localTimeZone) + list.forEach(item => { + item.time = convertTimeZone(item.time, region_time_zone, localTimeZone) + }) const logs = list.map((item) => { const { id, item_id, item_type, name, rank_type, time, gacha_id, gacha_type, count} = item return { id, item_id, item_type, name, rank_type, time, gacha_id, gacha_type, count } @@ -439,14 +468,8 @@ const fetchData = async (urlOverride) => { if (!originUid) { originUid = uid } - if (!originRegion) { - originRegion = region - } - if (!originTimeZone) { - originTimeZone = region_time_zone - } } - const data = { result, typeMap, time: Date.now(), uid: originUid, lang, region: originRegion, region_time_zone: originTimeZone } + const data = { result, typeMap, time: Date.now(), uid: originUid, lang, region_time_zone: localTimeZone } const localData = dataMap.get(originUid) const mergedResult = mergeData(localData, data) data.result = mergedResult @@ -527,5 +550,9 @@ exports.getData = () => { } } +exports.serverTimeZone = serverTimeZone exports.getUrl = getUrl exports.deleteData = deleteData +exports.saveData = saveData +exports.changeCurrent = changeCurrent +exports.convertTimeZone = convertTimeZone \ No newline at end of file diff --git a/src/renderer/App.vue b/src/renderer/App.vue index 1d4da53..2191754 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -4,7 +4,7 @@
{{state.status === 'init' ? ui.button.load: ui.button.update}} - + {{ui.button.files}} @@ -15,6 +15,7 @@ + {{ui.button.import}} @@ -272,6 +273,18 @@ const exportUIGFJSON = () => { }); } +const importData = async () => { + state.status = 'loading' + const data = await ipcRenderer.invoke('IMPORT_UIGF_JSON') + if (data) { + state.dataMap = data.dataMap + state.current = data.current + state.status = 'loaded' + } else { + state.status = 'failed' + } +} + const exportCommand = (type) => { if (type === 'excel') { saveExcel()