feat(db): 数据源更改

This commit is contained in:
BTMuli
2023-04-24 21:50:58 +08:00
parent ff36275184
commit 26048dc12e
8 changed files with 229 additions and 149 deletions

View File

@@ -6,12 +6,26 @@
*/ */
import { initDatabase, resetDatabase, checkDatabase, sqlitePath } from "./init"; import { initDatabase, resetDatabase, checkDatabase, sqlitePath } from "./init";
import { getAllSeries, getAchievementsBySeries, searchAchievement, getAchievementOverview } from "./select";
import { importUIAFData, exportUIAFData } from "./UIAF";
import { checkAchievement, checkAchievementSeries } from "./update"; import { checkAchievement, checkAchievementSeries } from "./update";
const TGSqlite = { const TGSqlite = {
initDB: initDatabase, initDB: initDatabase,
resetDB: resetDatabase, resetDB: resetDatabase,
checkDB: checkDatabase, checkDB: checkDatabase,
search: {
achievement: {
bySeries: getAchievementsBySeries,
bySearch: searchAchievement,
},
achievementSeries: getAllSeries,
overview: getAchievementOverview,
},
UIAF: {
import: importUIAFData,
export: exportUIAFData,
},
update: { update: {
achievement: checkAchievement, achievement: checkAchievement,
achievementSeries: checkAchievementSeries, achievementSeries: checkAchievementSeries,

86
src/core/database/UIAF.ts Normal file
View File

@@ -0,0 +1,86 @@
/**
* @file core database UIAF.ts
* @description UIAF 数据导入导出
* @author BTMuli<bt-muli@outlook.com>
* @since Alpha v0.1.4
*/
// tauri
import Database from "tauri-plugin-sql-api";
// local
import { sqlitePath } from "./init";
// utils
import { timestampToDate, getUiafStatus } from "../../utils/UIAF";
/**
* @description 根据本地数据跟导入数据获取更新 SQL
* @since Alpha v0.1.4
* @param {TGPlugin.UIAF.Achievement} importData - 导入数据
* @param {BTMuli.SQLite.Achievements} localData - 本地数据
* @returns {string} SQL
*/
function getUpdateSql (importData: TGPlugin.UIAF.Achievement, localData: BTMuli.SQLite.Achievements): string {
// 如果本地为未完成状态,直接更新
if (localData.isCompleted === 0) {
// 已完成
if (importData.status === 2 || importData.status === 3) {
const completedTime = timestampToDate(importData.timestamp);
return `UPDATE Achievements SET isCompleted = 1, progress = ${importData.current}, completedTime = '${completedTime}' WHERE id = ${importData.id}`;
} else if (importData.current > localData.progress) {
return `UPDATE Achievements SET progress = ${importData.current} WHERE id = ${importData.id}`;
} else {
return "";
}
} else {
// 本地为已完成状态,判断进度
if (importData.current > localData.progress) {
// 进度大于本地进度,更新
return `UPDATE Achievements SET progress = ${importData.current} WHERE id = ${importData.id}`;
} else {
return "";
}
}
}
/**
* @description 导入 UIAF 数据,更新数据库
* @since Alpha v0.1.4
* @param {TGPlugin.UIAF.Achievement[]} achievements - 成就列表
* @returns {Promise<void>}
*/
export async function importUIAFData (achievements: TGPlugin.UIAF.Achievement[]): Promise<void> {
const db = await Database.load(sqlitePath);
for (const achievement of achievements) {
const id = achievement.id;
const selects: BTMuli.SQLite.Achievements[] = await db.select(`SELECT * FROM Achievements WHERE id = ${id}`);
if (selects.length === 1) {
const sql = getUpdateSql(achievement, selects[0]);
if (sql !== "") await db.execute(sql);
}
}
await db.close();
}
/**
* @description 导出 UIAF 数据
* @since Alpha v0.1.4
* @returns {Promise<TGPlugin.UIAF.Achievement[]>}
*/
export async function exportUIAFData (): Promise<TGPlugin.UIAF.Achievement[]> {
const db = await Database.load(sqlitePath);
const sql = "SELECT * FROM Achievements WHERE isCompleted = 1 OR progress > 0";
const selects: BTMuli.SQLite.Achievements[] = await db.select(sql);
await db.close();
const achievements: TGPlugin.UIAF.Achievement[] = [];
for (const select of selects) {
const completed = select.isCompleted === 1;
const status = getUiafStatus(completed, select.progress);
achievements.push({
id: select.id,
status,
timestamp: completed && select.completedTime ? Math.round(new Date(select.completedTime).getTime() / 1000) : 0,
current: select.progress,
});
}
return achievements;
}

View File

@@ -29,7 +29,7 @@ CREATE TABLE IF NOT EXISTS AchievementSeries
version TEXT DEFAULT NULL, version TEXT DEFAULT NULL,
totalCount INTEGER DEFAULT 0, totalCount INTEGER DEFAULT 0,
finCount INTEGER DEFAULT 0, finCount INTEGER DEFAULT 0,
icon TEXT DEFAULT NULL, icon TEXT NOT NULL,
nameCard TEXT DEFAULT NULL, nameCard TEXT DEFAULT NULL,
updated TEXT DEFAULT NULL updated TEXT DEFAULT NULL
); );

View File

@@ -0,0 +1,71 @@
/**
* @file core database select.ts
* @description SQLite 数据库查询操作封装模块
* @author BTMuli<bt-muli@outlook.com>
* @since Alpha v0.1.4
*/
// tauri
import Database from "tauri-plugin-sql-api";
// local
import { sqlitePath } from "./init";
/**
* @description 返回所有成就系列数据
* @since Alpha v0.1.4
* @returns {Promise<BTMuli.SQLite.AchievementSeries[]>}
*/
export async function getAllSeries (): Promise<BTMuli.SQLite.AchievementSeries[]> {
const db = await Database.load(sqlitePath);
const sql = "SELECT * FROM AchievementSeries ORDER BY `order` ASC";
const result = await db.select(sql);
await db.close();
return result as BTMuli.SQLite.AchievementSeries[];
}
/**
* @description 查询成就数据,无参默认查询所有成就
* @since Alpha v0.1.4
* @param {number} series
* @returns {Promise<BTMuli.SQLite.Achievements[]>}
*/
export async function getAchievementsBySeries (series?: number): Promise<BTMuli.SQLite.Achievements[]> {
const db = await Database.load(sqlitePath);
let sql;
// 无参默认查询所有成就
if (!series) {
sql = "SELECT * FROM Achievements";
} else {
sql = `SELECT * FROM Achievements WHERE series = ${series}`;
}
const result = await db.select(sql);
await db.close();
return result as BTMuli.SQLite.Achievements[];
}
/**
* @description 条件查询
* @since Alpha v0.1.4
* @param {string} search
* @returns {Promise<BTMuli.SQLite.Achievements[]>}
*/
export async function searchAchievement (search: string): Promise<BTMuli.SQLite.Achievements[]> {
const db = await Database.load(sqlitePath);
const sql = `SELECT * FROM Achievements WHERE name LIKE '%${search}%' AND description LIKE '%${search}%'`;
const result = await db.select(sql);
await db.close();
return result as BTMuli.SQLite.Achievements[];
}
/**
* @description 返回成就概况
* @since Alpha v0.1.4
* @returns {Promise<{total: number, fin:number}>}
*/
export async function getAchievementOverview (): Promise<{ total: number, fin: number }> {
const db = await Database.load(sqlitePath);
const sql = "SELECT COUNT(*) AS total, SUM(isCompleted) AS fin FROM Achievements";
const res: Array<{ total: number, fin: number }> = await db.select(sql);
await db.close();
return res[0];
}

View File

@@ -39,7 +39,7 @@
<v-list-item-title> <v-list-item-title>
{{ series.name }} {{ series.name }}
</v-list-item-title> </v-list-item-title>
<v-list-item-subtitle> {{ series.completed_count }} / {{ series.total_count }} </v-list-item-subtitle> <v-list-item-subtitle> {{ series.finCount }} / {{ series.totalCount }} </v-list-item-subtitle>
</v-list-item> </v-list-item>
</v-list> </v-list>
</div> </div>
@@ -72,9 +72,9 @@
</div> </div>
<v-list-item> <v-list-item>
<template #prepend> <template #prepend>
<v-icon :color="achievement.completed ? '#fec90b' : '#485466'"> <v-icon :color="achievement.isCompleted ? '#fec90b' : '#485466'">
<!-- todo 图标替换 --> <!-- todo 图标替换 -->
{{ achievement.completed ? "mdi-check-circle" : "mdi-circle" }} {{ achievement.isCompleted ? "mdi-check-circle" : "mdi-circle" }}
</v-icon> </v-icon>
</template> </template>
<v-list-item-title> <v-list-item-title>
@@ -83,7 +83,7 @@
</v-list-item-title> </v-list-item-title>
<v-list-item-subtitle>{{ achievement.description }}</v-list-item-subtitle> <v-list-item-subtitle>{{ achievement.description }}</v-list-item-subtitle>
<template #append> <template #append>
<span v-show="achievement.completed" class="right-time">{{ achievement.completed_time }}</span> <span v-show="achievement.isCompleted" class="right-time">{{ achievement.completedTime }}</span>
<v-card class="reward-card" @click="showMaterial('/source/material/原石.webp')"> <v-card class="reward-card" @click="showMaterial('/source/material/原石.webp')">
<v-img src="/source/material/原石.webp" sizes="32" /> <v-img src="/source/material/原石.webp" sizes="32" />
<div class="reward-num"> <div class="reward-num">
@@ -112,8 +112,8 @@ import { useAchievementsStore } from "../store/modules/achievements";
// Utils // Utils
import { TGAppData } from "../data"; import { TGAppData } from "../data";
import { createTGWindow } from "../utils/TGWindow"; import { createTGWindow } from "../utils/TGWindow";
import { ReadAllTGData, ReadTGDataByIndex, ReadTGDataByKey, UpdateTGDataByKey } from "../utils/TGIndex"; import { getUiafHeader, readUiafData, verifyUiafData, backupUiafData } from "../utils/UIAF";
import { getUiafHeader, getUiafStatus, readUiafData, verifyUiafData, backupUiafData, timestampToDate } from "../utils/UIAF"; import TGSqlite from "../core/database/TGSqlite";
// Store // Store
const achievementsStore = useAchievementsStore(); const achievementsStore = useAchievementsStore();
@@ -127,10 +127,10 @@ const title = ref(achievementsStore.title as string);
const CardsInfo = ref([] as BTMuli.Genshin.NameCard[]); const CardsInfo = ref([] as BTMuli.Genshin.NameCard[]);
const getCardInfo = ref({} as BTMuli.Genshin.NameCard); const getCardInfo = ref({} as BTMuli.Genshin.NameCard);
// series // series
const seriesList = ref([] as BTMuli.Genshin.AchievementSeries[]); const seriesList = ref([] as BTMuli.SQLite.AchievementSeries[]);
const selectedIndex = ref(-1 as number); const selectedIndex = ref(-1 as number);
const selectedSeries = ref(-1 as number); const selectedSeries = ref(-1 as number);
const selectedAchievement = ref([] as BTMuli.Genshin.Achievement[]); const selectedAchievement = ref([] as BTMuli.SQLite.Achievements[]);
// render // render
const search = ref("" as string); const search = ref("" as string);
@@ -144,17 +144,18 @@ onMounted(async () => {
// 加载数据,数据源:合并后的本地数据 // 加载数据,数据源:合并后的本地数据
async function loadData () { async function loadData () {
const { total, fin } = await TGSqlite.search.overview();
achievementsStore.flushData(total, fin);
loadingTitle.value = "正在获取成就系列数据"; loadingTitle.value = "正在获取成就系列数据";
const seriesDB: BTMuli.Genshin.AchievementSeries[] = await ReadAllTGData("AchievementSeries");
CardsInfo.value = TGAppData.nameCards[1]; CardsInfo.value = TGAppData.nameCards[1];
seriesList.value = seriesDB.sort((a, b) => a.order - b.order); seriesList.value = await TGSqlite.search.achievementSeries();
loadingTitle.value = "正在获取成就数据"; loadingTitle.value = "正在获取成就数据";
const getAchievements = await ReadAllTGData("Achievements"); const getAchievements = await TGSqlite.search.achievement.bySeries();
getAchievements.sort((a, b) => { getAchievements.sort((a, b) => {
if (a.completed === b.completed) { if (a.isCompleted === b.isCompleted) {
return a.id - b.id; return a.id - b.id;
} else { } else {
return a.completed ? 1 : -1; return a.isCompleted ? 1 : -1;
} }
}); });
selectedAchievement.value = getAchievements; selectedAchievement.value = getAchievements;
@@ -171,21 +172,21 @@ async function selectSeries (index: number) {
} }
loading.value = true; loading.value = true;
loadingTitle.value = "正在获取对应的成就数据"; loadingTitle.value = "正在获取对应的成就数据";
const getAchievements = await ReadTGDataByIndex("Achievements", "series", seriesList.value[index].id);
selectedIndex.value = index; selectedIndex.value = index;
selectedSeries.value = seriesList.value[index].id; selectedSeries.value = seriesList.value[index].id;
const getAchievements = await TGSqlite.search.achievement.bySeries(selectedSeries.value);
loadingTitle.value = "正在查找对应的成就名片"; loadingTitle.value = "正在查找对应的成就名片";
let getCard: BTMuli.Genshin.NameCard; let getCard: BTMuli.Genshin.NameCard;
if (selectedSeries.value !== 0 && selectedSeries.value !== 17) { if (selectedSeries.value !== 0 && selectedSeries.value !== 17) {
getCard = CardsInfo.value.find((card) => card.name === seriesList.value[index].card)!; getCard = CardsInfo.value.find((card) => card.name === seriesList.value[index].nameCard)!;
} else { } else {
getCard = {} as BTMuli.Genshin.NameCard; getCard = {} as BTMuli.Genshin.NameCard;
} }
getAchievements.sort((a, b) => { getAchievements.sort((a, b) => {
if (a.completed === b.completed) { if (a.isCompleted === b.isCompleted) {
return a.id - b.id; return a.id - b.id;
} else { } else {
return a.completed ? 1 : -1; return a.isCompleted ? 1 : -1;
} }
}); });
selectedAchievement.value = getAchievements; selectedAchievement.value = getAchievements;
@@ -208,14 +209,7 @@ async function searchCard () {
} }
loadingTitle.value = "正在搜索"; loadingTitle.value = "正在搜索";
loading.value = true; loading.value = true;
const res: BTMuli.Genshin.Achievement[] = []; const res = await TGSqlite.search.achievement.bySearch(search.value);
const allAchievements = await ReadAllTGData("Achievements");
allAchievements.map((achievement) => {
if (achievement.name.includes(search.value) || achievement.description.includes(search.value)) {
return res.push(achievement);
}
return null;
});
selectedIndex.value = -1; selectedIndex.value = -1;
setTimeout(() => { setTimeout(() => {
loading.value = false; loading.value = false;
@@ -224,13 +218,13 @@ async function searchCard () {
snackbarColor.value = "#F5810A"; snackbarColor.value = "#F5810A";
snackbarText.value = "没有找到对应的成就"; snackbarText.value = "没有找到对应的成就";
snackbar.value = true; snackbar.value = true;
selectedAchievement.value = allAchievements; selectedAchievement.value = await TGSqlite.search.achievement.bySeries();
} else { } else {
res.sort((a, b) => { res.sort((a, b) => {
if (a.completed === b.completed) { if (a.isCompleted === b.isCompleted) {
return a.id - b.id; return a.id - b.id;
} else { } else {
return a.completed ? 1 : -1; return a.isCompleted ? 1 : -1;
} }
}); });
selectedAchievement.value = res; selectedAchievement.value = res;
@@ -258,59 +252,12 @@ async function importJson () {
loading.value = true; loading.value = true;
const remoteData: TGPlugin.UIAF.BaseData = JSON.parse(remoteRaw); const remoteData: TGPlugin.UIAF.BaseData = JSON.parse(remoteRaw);
loadingTitle.value = "正在合并成就数据"; loadingTitle.value = "正在合并成就数据";
await Promise.allSettled( await TGSqlite.UIAF.import(remoteData.list);
remoteData.list.map(async (data) => {
const id = data.id;
const localData: BTMuli.Genshin.Achievement = (await ReadTGDataByKey("Achievements", [id]))[0];
// 获取 timeStamp 2023-03-15 00:00:00
const localTime = localData.completed_time;
// 如果本地数据不存在,或者本地数据的 timeStamp 小于远程数据的 timeStamp更新数据
if (data.timestamp !== 0) {
const finishTime = timestampToDate(data.timestamp);
if (finishTime !== localTime || localData.progress !== data.current) {
// eslint-disable-next-line camelcase
localData.completed_time = finishTime;
localData.progress = data.current;
localData.completed = true;
// 更新数据
await UpdateTGDataByKey("Achievements", localData);
}
} else {
if (localData.progress !== data.current) {
// eslint-disable-next-line camelcase
localData.completed_time = "";
localData.progress = data.current;
localData.completed = false;
// 更新数据
await UpdateTGDataByKey("Achievements", localData);
}
}
}),
);
loadingTitle.value = "正在更新成就系列数据";
let seriesDB = await ReadAllTGData("AchievementSeries");
await Promise.allSettled(
seriesDB.map(async (data) => {
const seriesId = data.id;
const achievementsDB = await ReadTGDataByIndex("Achievements", "series", seriesId);
// eslint-disable-next-line camelcase
data.completed_count = achievementsDB.filter((data) => {
return data.completed === true;
}).length;
await UpdateTGDataByKey("AchievementSeries", data);
}),
);
loadingTitle.value = "正在备份数据"; loadingTitle.value = "正在备份数据";
await backupAchievementData(); await backupAchievementData();
loadingTitle.value = "正在刷新数据"; loadingTitle.value = "正在刷新数据";
seriesDB = await ReadAllTGData("AchievementSeries"); const overview = await TGSqlite.search.overview();
const finishAchievments = seriesDB.reduce((a, b) => { achievementsStore.flushData(overview.total, overview.fin);
return a + b.completed_count;
}, 0);
const totalAchievements = seriesDB.reduce((a, b) => {
return a + b.total_count;
}, 0);
achievementsStore.flushData(totalAchievements, finishAchievments);
// 刷新数据 // 刷新数据
await loadData(); await loadData();
selectedIndex.value = -1; selectedIndex.value = -1;
@@ -319,7 +266,7 @@ async function importJson () {
// 备份成就数据 // 备份成就数据
async function backupAchievementData () { async function backupAchievementData () {
const achievements = await ReadAllTGData("Achievements"); const achievements = await TGSqlite.UIAF.export();
await backupUiafData(achievements); await backupUiafData(achievements);
} }
@@ -333,22 +280,10 @@ async function exportJson () {
return; return;
} }
// 获取本地数据 // 获取本地数据
const achievements = (await ReadAllTGData("Achievements")).filter((data) => {
return data.progress !== 0 || data.completed === true;
});
const UiafData = { const UiafData = {
info: await getUiafHeader(), info: await getUiafHeader(),
list: [] as TGPlugin.UIAF.Achievement[], list: await TGSqlite.UIAF.export(),
}; };
// 转换数据
UiafData.list = achievements.map((data) => {
return {
id: data.id,
timestamp: data.completed && data.completed_time ? Math.round(new Date(data.completed_time).getTime() / 1000) : 0,
current: data.progress,
status: getUiafStatus(data.completed, data.progress),
};
});
const isSave = await dialog.save({ const isSave = await dialog.save({
// TODO: 设置保存文件名 // TODO: 设置保存文件名
filters: [ filters: [

View File

@@ -117,6 +117,7 @@
</v-list-item> </v-list-item>
<v-list-item title="删除 IndexedDB" prepend-icon="mdi-delete" @click="tryConfirm('delDB')" /> <v-list-item title="删除 IndexedDB" prepend-icon="mdi-delete" @click="tryConfirm('delDB')" />
<v-list-item title="检测 SQLite 数据库完整性" prepend-icon="mdi-database-check" @click="tryConfirm('checkDB')" /> <v-list-item title="检测 SQLite 数据库完整性" prepend-icon="mdi-database-check" @click="tryConfirm('checkDB')" />
<v-list-item title="重置 SQLite 数据库" prepend-icon="mdi-database-remove" @click="tryConfirm('resetDB')" />
<v-list-subheader inset class="config-header"> <v-list-subheader inset class="config-header">
路径 路径
</v-list-subheader> </v-list-subheader>
@@ -261,6 +262,11 @@ function tryConfirm (oper: string) {
confirmOper.value = "checkDB"; confirmOper.value = "checkDB";
confirmShow.value = true; confirmShow.value = true;
break; break;
case "resetDB":
confirmText.value = "确认重置 SQlite 数据库吗?";
confirmOper.value = "resetDB";
confirmShow.value = true;
break;
} }
} }
@@ -295,6 +301,9 @@ async function doConfirm (oper: string) {
case "checkDB": case "checkDB":
await checkDB(); await checkDB();
break; break;
case "resetDB":
await resetDB();
break;
default: default:
break; break;
} }
@@ -418,12 +427,10 @@ async function checkDB () {
loadingTitle.value = "正在检查数据库表单完整性..."; loadingTitle.value = "正在检查数据库表单完整性...";
const res = await TGSqlite.checkDB(); const res = await TGSqlite.checkDB();
if (!res) { if (!res) {
loadingTitle.value = "检测到表单不完整,正在重置数据库..."; confirmOper.value = "resetDB";
await TGSqlite.resetDB(); confirmText.value = "数据库表单不完整,是否重置数据库?";
loading.value = false; loading.value = false;
snackbarText.value = "数据库已重置!请载入备份数据。"; confirmShow.value = true;
snackbarColor.value = "success";
snackbar.value = true;
} else { } else {
loadingTitle.value = "正在检查数据库数据完整性..."; loadingTitle.value = "正在检查数据库数据完整性...";
await TGSqlite.update.achievement(); await TGSqlite.update.achievement();
@@ -434,6 +441,14 @@ async function checkDB () {
snackbar.value = true; snackbar.value = true;
} }
} }
// 重置 SQLite 数据库
async function resetDB () {
await TGSqlite.resetDB();
snackbarText.value = "数据库已重置!请载入备份数据。";
snackbarColor.value = "success";
snackbar.value = true;
}
</script> </script>
<style lang="css" scoped> <style lang="css" scoped>

View File

@@ -164,7 +164,7 @@ declare namespace BTMuli.SQLite {
version: string | null version: string | null
totalCount: number totalCount: number
finCount: number finCount: number
icon: string | null icon: string
nameCard: string | null nameCard: string | null
updated: string | null updated: string | null
} }

View File

@@ -7,16 +7,14 @@
// tauri // tauri
import { app, fs, path } from "@tauri-apps/api"; import { app, fs, path } from "@tauri-apps/api";
// data
import { TGAppData } from "../data";
// utils // utils
import { UpdateTGDataByKey } from "./TGIndex"; import TGSqlite from "../core/database/TGSqlite";
/** /**
* @description 时间戳转换为日期 * @description 时间戳转换为日期
* @since Alpha v0.1.4 * @since Alpha v0.1.4
* @param {number} timestamp - 时间戳 * @param {number} timestamp - 时间戳
* @returns {string} 日期 * @returns {string} 日期 2021-01-01 00:00:00
*/ */
export function timestampToDate (timestamp: number): string { export function timestampToDate (timestamp: number): string {
return new Date(timestamp * 1000).toLocaleString("zh", { return new Date(timestamp * 1000).toLocaleString("zh", {
@@ -101,66 +99,27 @@ export async function readUiafData (userPath: string): Promise<string | false> {
/** /**
* @description 根据成就数据导出 UIAF 数据 * @description 根据成就数据导出 UIAF 数据
* @since Alpha v0.1.4 * @since Alpha v0.1.4
* @param {BTMuli.Genshin.Achievement[]} achievementData - 成就数据 * @param {TGPlugin.UIAF.Achievement[]} achievementData - 成就数据
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
export async function backupUiafData (achievementData: BTMuli.Genshin.Achievement[]): Promise<void> { export async function backupUiafData (achievementData: TGPlugin.UIAF.Achievement[]): Promise<void> {
const res = [] as TGPlugin.UIAF.Achievement[];
const savePath = `${await path.appLocalDataDir()}\\userData\\UIAF.json`; const savePath = `${await path.appLocalDataDir()}\\userData\\UIAF.json`;
achievementData.forEach((achievement) => { await fs.writeTextFile(savePath, JSON.stringify(achievementData, null, 2));
if (achievement.completed || achievement.progress !== 0) {
return res.push({
id: achievement.id,
timestamp: achievement.completed && achievement.completed_time ? Math.round(new Date(achievement.completed_time).getTime() / 1000) : 0,
current: achievement.progress,
status: getUiafStatus(achievement.completed, achievement.progress),
});
}
});
await fs.writeTextFile(savePath, JSON.stringify(res, null, 2));
} }
/** /**
* @description 根据 UIAF 数据恢复成就数据 * @description 根据 UIAF 数据恢复成就数据
* @since Alpha v0.1.4 * @since Alpha v0.1.4
* @returns {Promise<{total: number, completed: number}> | false} 恢复的成就数量 * @returns {Promise<{total: number, fin: number}> | false} 恢复的成就数量
*/ */
export async function restoreUiafData (): Promise<{ total: number, completed: number } | false> { export async function restoreUiafData (): Promise<{ total: number, fin: number } | false> {
const uiafPath = `${await path.appLocalDataDir()}\\userData\\UIAF.json`; const uiafPath = `${await path.appLocalDataDir()}\\userData\\UIAF.json`;
// 检测是否存在 UIAF 数据 // 检测是否存在 UIAF 数据
if (!await fs.exists(uiafPath)) { if (!await fs.exists(uiafPath)) {
return false; return false;
} }
const uiafData = JSON.parse(await fs.readTextFile(uiafPath)) as TGPlugin.UIAF.Achievement[]; const uiafData = JSON.parse(await fs.readTextFile(uiafPath)) as TGPlugin.UIAF.Achievement[];
const achievementData = TGAppData.achievements; await TGSqlite.UIAF.import(uiafData);
const seriesData = TGAppData.achievementSeries;
uiafData.forEach((uiafAchievement) => {
// 更新成就数据
const localAchievement = achievementData[uiafAchievement.id];
localAchievement.completed = uiafAchievement.status === 2 || uiafAchievement.status === 3;
localAchievement.progress = uiafAchievement.current;
// eslint-disable-next-line camelcase
localAchievement.completed_time = uiafAchievement.timestamp !== 0 ? timestampToDate(uiafAchievement.timestamp) : "";
UpdateTGDataByKey("Achievements", localAchievement);
// 更新成就系列数据
if (localAchievement.completed) {
const localSeries = seriesData[localAchievement.series];
// eslint-disable-next-line camelcase
localSeries.completed_count += 1;
seriesData[localAchievement.series] = localSeries;
UpdateTGDataByKey("AchievementSeries", localSeries);
}
});
// 获取 total 和 completed
let total = 0;
let completed = 0;
Object.values(seriesData).forEach((series) => {
total += series.total_count;
completed += series.completed_count;
});
// 返回 // 返回
return { return await TGSqlite.search.overview();
total,
completed,
};
} }