feat(uigf): import UIGFv4.0

BREAKING CHANGES: `region` is no longer stored in/read from local file
This commit is contained in:
Zichao Lin 2024-07-25 13:25:26 +08:00
parent 14cfda3986
commit 6fe12da9be
Signed by: earthjasonlin
GPG Key ID: 406D9913DE2E42FB
6 changed files with 143 additions and 31 deletions

@ -6,6 +6,7 @@
"ui.button.files": "Export Files", "ui.button.files": "Export Files",
"ui.button.excel": "Export Excel", "ui.button.excel": "Export Excel",
"ui.button.uigf": "Export UIGF (Beta)", "ui.button.uigf": "Export UIGF (Beta)",
"ui.button.import": "Import UIGF (Beta)",
"ui.button.url": "Input URL", "ui.button.url": "Input URL",
"ui.button.setting": "Settings", "ui.button.setting": "Settings",
"ui.button.option": "Option", "ui.button.option": "Option",

@ -6,6 +6,7 @@
"ui.button.files": "导出文件", "ui.button.files": "导出文件",
"ui.button.excel": "导出Excel", "ui.button.excel": "导出Excel",
"ui.button.uigf":"导出UIGF (Beta)", "ui.button.uigf":"导出UIGF (Beta)",
"ui.button.import":"导入UIGF (Beta)",
"ui.button.url": "输入URL", "ui.button.url": "输入URL",
"ui.button.setting": "设置", "ui.button.setting": "设置",
"ui.button.option": "选项", "ui.button.option": "选项",

@ -6,6 +6,7 @@
"ui.button.files": "導出文件", "ui.button.files": "導出文件",
"ui.button.excel": "導出Excel", "ui.button.excel": "導出Excel",
"ui.button.uigf":"導出UIGF (Beta)", "ui.button.uigf":"導出UIGF (Beta)",
"ui.button.import":"導入UIGF (Beta)",
"ui.button.url": "輸入URL", "ui.button.url": "輸入URL",
"ui.button.setting": "設置", "ui.button.setting": "設置",
"ui.button.option": "選項", "ui.button.option": "選項",

@ -1,10 +1,12 @@
const { app, ipcMain, dialog } = require('electron') const { app, ipcMain, dialog } = require('electron')
const fs = require('fs-extra') const fs = require('fs-extra')
const path = require('path') 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 { name, version } = require('../../package.json')
const i18n = require('./i18n') const i18n = require('./i18n')
const { exit } = require('process') const { mergeData } = require('./utils/mergeData')
const { sendMsg } = require('./utils')
const getTimeString = () => { const getTimeString = () => {
return new Date().toLocaleString('sv').replace(/[- :]/g, '').slice(0, -2) return new Date().toLocaleString('sv').replace(/[- :]/g, '').slice(0, -2)
@ -16,8 +18,7 @@ const formatDate = (date) => {
let d = `${date.getDate()}`.padStart(2, '0') let d = `${date.getDate()}`.padStart(2, '0')
return `${y}-${m}-${d} ${date.toLocaleString('zh-cn', { hour12: false }).slice(-8)}` return `${y}-${m}-${d} ${date.toLocaleString('zh-cn', { hour12: false }).slice(-8)}`
} }
const exportUIGF = async (uids) => {
const start = async (uids) => {
const result = { const result = {
info: { info: {
export_timestamp: Math.ceil(Date.now() / 1000), export_timestamp: Math.ceil(Date.now() / 1000),
@ -35,16 +36,7 @@ const start = async (uids) => {
if (!fulldata.length) { if (!fulldata.length) {
throw new Error('数据为空') throw new Error('数据为空')
} }
const serverTimeZone = new Map([
["prod_gf_cn", 8],
["prod_gf_jp", 8]
])
fulldata.forEach(data => { fulldata.forEach(data => {
let timezone
timezone = serverTimeZone.get(data.region)
if(!timezone) {
throw new Error('不支持此服务器')
}
const listTemp = [] const listTemp = []
for (let [type, arr] of data.result) { for (let [type, arr] of data.result) {
arr.forEach(log => { arr.forEach(log => {
@ -64,7 +56,7 @@ const start = async (uids) => {
listTemp.sort((a, b) => Number(BigInt(a.id) - BigInt(b.id))) listTemp.sort((a, b) => Number(BigInt(a.id) - BigInt(b.id)))
let dataTemp = { let dataTemp = {
uid: data.uid, uid: data.uid,
timezone: timezone, timezone: data.region_time_zone,
lang: data.lang, lang: data.lang,
list: [] 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) => { ipcMain.handle('EXPORT_UIGF_JSON', async (event, uids) => {
await start(uids) await exportUIGF(uids)
})
ipcMain.handle('IMPORT_UIGF_JSON', async () => {
return await importUIGF()
}) })

@ -29,6 +29,24 @@ const defaultTypeMap = new Map([
['5', '邦布频段'] ['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 findDataFiles = async (dataPath, fileMap) => {
const files = await readdir(dataPath) const files = await readdir(dataPath)
if (files?.length) { if (files?.length) {
@ -203,7 +221,6 @@ const getGachaLogs = async ({ name, key }, queryString) => {
let logs = [] let logs = []
let uid = '' let uid = ''
let region = '' let region = ''
let region_time_zone = ''
let endId = '0' let endId = '0'
const url = `${apiDomain}/common/gacha_record/api/getGachaLog?${queryString}` const url = `${apiDomain}/common/gacha_record/api/getGachaLog?${queryString}`
do { do {
@ -221,9 +238,6 @@ const getGachaLogs = async ({ name, key }, queryString) => {
if (!region) { if (!region) {
region = res.region region = res.region
} }
if (!region_time_zone) {
region_time_zone = res.region_time_zone
}
list.push(...logs) list.push(...logs)
page += 1 page += 1
@ -252,7 +266,7 @@ const getGachaLogs = async ({ name, key }, queryString) => {
} }
} }
} while (logs.length > 0) } while (logs.length > 0)
return { list, uid, region, region_time_zone } return { list, uid, region }
} }
const checkResStatus = (res) => { const checkResStatus = (res) => {
@ -425,10 +439,25 @@ const fetchData = async (urlOverride) => {
const typeMap = new Map() const typeMap = new Map()
const lang = searchParams.get('lang') const lang = searchParams.get('lang')
let originUid = '' let originUid = ''
let originRegion = '' let localTimeZone
let originTimeZone = ''
for (const type of gachaType) { 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 logs = list.map((item) => {
const { id, item_id, item_type, name, rank_type, time, gacha_id, gacha_type, count} = 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 } 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) { if (!originUid) {
originUid = uid originUid = uid
} }
if (!originRegion) {
originRegion = region
} }
if (!originTimeZone) { const data = { result, typeMap, time: Date.now(), uid: originUid, lang, region_time_zone: localTimeZone }
originTimeZone = region_time_zone
}
}
const data = { result, typeMap, time: Date.now(), uid: originUid, lang, region: originRegion, region_time_zone: originTimeZone }
const localData = dataMap.get(originUid) const localData = dataMap.get(originUid)
const mergedResult = mergeData(localData, data) const mergedResult = mergeData(localData, data)
data.result = mergedResult data.result = mergedResult
@ -527,5 +550,9 @@ exports.getData = () => {
} }
} }
exports.serverTimeZone = serverTimeZone
exports.getUrl = getUrl exports.getUrl = getUrl
exports.deleteData = deleteData exports.deleteData = deleteData
exports.saveData = saveData
exports.changeCurrent = changeCurrent
exports.convertTimeZone = convertTimeZone

@ -4,7 +4,7 @@
<div class="space-x-3"> <div class="space-x-3">
<el-button type="primary" :icon="state.status === 'init' ? 'milk-tea': 'refresh-right'" class="focus:outline-none" :disabled="!allowClick()" plain @click="fetchData()" :loading="state.status === 'loading'">{{state.status === 'init' ? ui.button.load: ui.button.update}}</el-button> <el-button type="primary" :icon="state.status === 'init' ? 'milk-tea': 'refresh-right'" class="focus:outline-none" :disabled="!allowClick()" plain @click="fetchData()" :loading="state.status === 'loading'">{{state.status === 'init' ? ui.button.load: ui.button.update}}</el-button>
<el-dropdown :disabled="!gachaData" @command="exportCommand"> <el-dropdown :disabled="!gachaData" @command="exportCommand">
<el-button :disabled="!gachaData" icon="folder-opened" class="focus:outline-none" type="success" plain> <el-button :disabled="!gachaData" icon="download" class="focus:outline-none" type="success" plain>
{{ui.button.files}} {{ui.button.files}}
<el-icon class="el-icon--right"><arrow-down /></el-icon> <el-icon class="el-icon--right"><arrow-down /></el-icon>
</el-button> </el-button>
@ -15,6 +15,7 @@
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
</el-dropdown> </el-dropdown>
<el-button @click="importData()" icon="upload" class="focus:outline-none" type="success" plain>{{ui.button.import}}</el-button>
<el-tooltip v-if="detail && state.status !== 'loading'" :content="ui.hint.newAccount" placement="bottom"> <el-tooltip v-if="detail && state.status !== 'loading'" :content="ui.hint.newAccount" placement="bottom">
<el-button @click="newUser()" plain icon="plus" class="focus:outline-none"></el-button> <el-button @click="newUser()" plain icon="plus" class="focus:outline-none"></el-button>
</el-tooltip> </el-tooltip>
@ -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) => { const exportCommand = (type) => {
if (type === 'excel') { if (type === 'excel') {
saveExcel() saveExcel()