♻️ 重构深渊数据加载逻辑,适配多存档

#126
This commit is contained in:
目棃
2024-09-21 14:05:41 +08:00
parent b8f6f3b1e7
commit 7fce3bad19
11 changed files with 379 additions and 337 deletions

View File

@@ -8,13 +8,7 @@ import { app } from "@tauri-apps/api";
import Database from "@tauri-apps/plugin-sql";
import initDataSql from "./sql/initData.js";
import {
importAbyssData,
insertAbyssData,
insertAppData,
insertGameAccountData,
insertRecordData,
} from "./sql/insertData.js";
import { insertAppData, insertGameAccountData, insertRecordData } from "./sql/insertData.js";
class Sqlite {
/**
@@ -30,11 +24,9 @@ class Sqlite {
*/
private readonly tables: string[] = [
"Achievements",
"AchievementSeries",
"AppCharacters",
"AppData",
"GameAccount",
"NameCard",
"SpiralAbyss",
"UserCharacters",
"UserRecord",
@@ -181,53 +173,6 @@ class Sqlite {
await this.initDB();
}
/**
* @description 保存深渊数据
* @since Beta v0.3.3
* @param {string} uid 游戏 UID
* @param {TGApp.Game.Abyss.FullData} data 深渊数据
* @returns {Promise<void>}
*/
public async saveAbyss(uid: string, data: TGApp.Game.Abyss.FullData): Promise<void> {
const db = await this.getDB();
const sql = insertAbyssData(uid, data);
await db.execute(sql);
}
/**
* @description 获取深渊数据
* @since Beta v0.3.3
* @param {string} uid 游戏 UID
* @returns {Promise<TGApp.Game.Abyss.FullData>}
*/
public async getAbyss(uid?: string): Promise<TGApp.Sqlite.Abyss.SingleTable[]> {
const db = await this.getDB();
let sql;
if (uid) {
sql = `SELECT *
FROM SpiralAbyss
WHERE uid = '${uid}'
order by id desc`;
} else {
sql = "SELECT * FROM SpiralAbyss order by uid, id desc";
}
return await db.select(sql);
}
/**
* @description 恢复深渊数据
* @since Beta v0.3.3
* @param {TGApp.Sqlite.Abyss.SingleTable[]} data 深渊数据
* @returns {Promise<void>}
*/
public async restoreAbyss(data: TGApp.Sqlite.Abyss.SingleTable[]): Promise<void> {
const db = await this.getDB();
for (const item of data) {
const sql = importAbyssData(item);
await db.execute(sql);
}
}
/**
* @description 保存战绩数据
* @since Beta v0.3.3

View File

@@ -0,0 +1,198 @@
/**
* @file plugins/Sqlite/modules/userAbyss.ts
* @description Sqlite-用户深渊模块
* @since Beta v0.6.0
*/
import { path } from "@tauri-apps/api";
import { exists, mkdir, readTextFile, writeTextFile } from "@tauri-apps/plugin-fs";
import TGLogger from "../../../utils/TGLogger.js";
import { timestampToDate } from "../../../utils/toolFunc.js";
import TGSqlite from "../index.js";
import { transCharacterData, transFloorData } from "../utils/transAbyssData.js";
/**
* @description 直接插入数据
* @since Beta v0.6.0
* @param {TGApp.Sqlite.Abyss.SingleTable} data - 数据
* @returns {string}
*/
function getRestoreSql(data: TGApp.Sqlite.Abyss.SingleTable): string {
const timeNow = timestampToDate(new Date().getTime());
return `
INSERT INTO SpiralAbyss (uid, id, startTime, endTime, totalBattleTimes, totalWinTimes,
maxFloor, totalStar, isUnlock, revealRank, defeatRank, damageRank,
takeDamageRank, normalSkillRank, energySkillRank, floors, updated)
VALUES ('${data.uid}', ${data.id}, '${data.startTime}', '${data.endTime}', ${data.totalBattleTimes},
${data.totalWinTimes}, '${data.maxFloor}', ${data.totalStar},
${data.isUnlock}, '${data.revealRank}', '${data.defeatRank}', '${data.damageRank}',
'${data.takeDamageRank}', '${data.normalSkillRank}', '${data.energySkillRank}', '${data.floors}',
'${timeNow}')
ON CONFLICT(uid, id) DO UPDATE
SET startTime = '${data.startTime}',
endTime = '${data.endTime}',
totalBattleTimes = ${data.totalBattleTimes},
totalWinTimes = ${data.totalWinTimes},
maxFloor = '${data.maxFloor}',
totalStar = ${data.totalStar},
isUnlock = ${data.isUnlock},
revealRank = '${data.revealRank}',
defeatRank = '${data.defeatRank}',
damageRank = '${data.damageRank}',
takeDamageRank = '${data.takeDamageRank}',
normalSkillRank = '${data.normalSkillRank}',
energySkillRank = '${data.energySkillRank}',
floors = '${data.floors}',
updated = '${timeNow}';
`;
}
/**
* @description 获取深渊数据的插入更新Sql
* @since Beta v0.6.0
* @param {string} uid - 用户UID
* @param {TGApp.Game.Abyss.FullData} data -深渊数据
* @returns {string}
*/
function getInsertSql(uid: string, data: TGApp.Game.Abyss.FullData): string {
const startTime = timestampToDate(Number(data.start_time) * 1000);
const endTime = timestampToDate(Number(data.end_time) * 1000);
const isUnlock = data.is_unlock ? 1 : 0;
const revealRank = transCharacterData(data.reveal_rank);
const defeatRank = transCharacterData(data.defeat_rank);
const damageRank = transCharacterData(data.damage_rank);
const takeDamageRank = transCharacterData(data.take_damage_rank);
const normalSkillRank = transCharacterData(data.normal_skill_rank);
const energySkillRank = transCharacterData(data.energy_skill_rank);
const floors = transFloorData(data.floors);
const timeNow = timestampToDate(new Date().getTime());
return `
INSERT INTO SpiralAbyss (uid, id, startTime, endTime, totalBattleTimes, totalWinTimes,
maxFloor, totalStar, isUnlock, revealRank, defeatRank, damageRank,
takeDamageRank, normalSkillRank, energySkillRank, floors, updated)
VALUES ('${uid}', ${data.schedule_id}, '${startTime}', '${endTime}', ${data.total_battle_times},
${data.total_win_times}, '${data.max_floor}', ${data.total_star},
${isUnlock}, '${revealRank}', '${defeatRank}', '${damageRank}', '${takeDamageRank}',
'${normalSkillRank}', '${energySkillRank}', '${floors}', '${timeNow}')
ON CONFLICT(uid, id) DO UPDATE
SET startTime = '${startTime}',
endTime = '${endTime}',
totalBattleTimes = ${data.total_battle_times},
totalWinTimes = ${data.total_win_times},
maxFloor = '${data.max_floor}',
totalStar = ${data.total_star},
isUnlock = ${isUnlock},
revealRank = '${revealRank}',
defeatRank = '${defeatRank}',
damageRank = '${damageRank}',
takeDamageRank = '${takeDamageRank}',
normalSkillRank = '${normalSkillRank}',
energySkillRank = '${energySkillRank}',
floors = '${floors}',
updated = '${timeNow}';
`;
}
/**
* @description 获取所有有数据的UID
* @since Beta v0.6.0
* @returns {Promise<void>}
*/
async function getAllUid(): Promise<Array<string>> {
const db = await TGSqlite.getDB();
type resType = Array<{ uid: string }>;
const res = await db.select<resType>("SELECT DISTINCT uid FROM SpiralAbyss;");
return res.map((i) => i.uid);
}
/**
* @description 获取深渊数据
* @since Beta v0.6.0
* @param {string} uid - 游戏UID
* @returns {Promise<TGApp.Sqlite.Abyss.SingleTable[]>}
*/
async function getAbyss(uid?: string): Promise<TGApp.Sqlite.Abyss.SingleTable[]> {
const db = await TGSqlite.getDB();
if (uid === undefined) {
return await db.select<TGApp.Sqlite.Abyss.SingleTable[]>(
"SELECT * FROM SpiralAbyss order by id DESC;",
);
}
return await db.select<TGApp.Sqlite.Abyss.SingleTable[]>(
"SELECT * FROM SpiralAbyss WHERE uid = ? order by id DESC;",
[uid],
);
}
/**
* @description 保存深渊数据
* @since Beta v0.6.0
* @param {string} uid - 游戏UID
* @param {TGApp.Game.Abyss.FullData} data - 深渊数据
* @returns {Promise<void>}
*/
async function saveAbyss(uid: string, data: TGApp.Game.Abyss.FullData): Promise<void> {
const db = await TGSqlite.getDB();
await db.execute(getInsertSql(uid, data));
}
/**
* @description 删除指定UID存档的数据
* @since Beta v0.6.0
* @param {string} uid - 游戏UID
* @returns {Promise<void>}
*/
async function delAbyss(uid: string): Promise<void> {
const db = await TGSqlite.getDB();
await db.execute("DELETE FROM SpiralAbyss WHERE uid = ?;", [uid]);
}
/**
* @description 备份深渊数据
* @since Beta v0.6.0
* @param {string} dir - 备份目录
* @returns {Promise<void>}
*/
async function backupAbyss(dir: string): Promise<void> {
if (!(await exists(dir))) {
await mkdir(dir, { recursive: true });
await TGLogger.Warn(`未检测到备份目录,已创建`);
}
const data = await getAbyss();
await writeTextFile(`${dir}${path.sep()}abyss.json`, JSON.stringify(data));
}
/**
* @description 恢复深渊数据
* @since Beta v0.6.0
* @param {string} dir - 备份文件目录
* @returns {Promise<boolean>}
*/
async function restoreAbyss(dir: string): Promise<boolean> {
const filePath = `${dir}${path.sep()}abyss.json`;
if (!(await exists(filePath))) return false;
try {
const data: TGApp.Sqlite.Abyss.SingleTable[] = JSON.parse(await readTextFile(filePath));
const db = await TGSqlite.getDB();
for (const abyss of data) {
await db.execute(getRestoreSql(abyss));
}
return true;
} catch (e) {
await TGLogger.Error(`恢复深渊数据失败${filePath}`);
await TGLogger.Error(`${e}`);
return false;
}
}
const TSUserAbyss = {
getAllUid,
getAbyss,
saveAbyss,
delAbyss,
backupAbyss,
restoreAbyss,
};
export default TSUserAbyss;

View File

@@ -307,14 +307,14 @@ async function restoreUiaf(dir: string): Promise<boolean> {
if (!(await exists(dir))) return false;
const filesRead = await readDir(dir);
const files = filesRead.filter((item) => item.name.includes("UIAF_") && item.isFile);
// 正则匹配 UIAF_xx.json
for (const file of files) {
try {
// todo 完善正则判断
const reg = new RegExp(/(.*)UIAF_d{9}.json/);
const reg = /UIAF_(\d+).json/;
if (!file.name.match(reg)) return false;
const uid: number = Number(file.name.match(reg)![0]);
const data: TGApp.Plugins.UIAF.Achievement[] = JSON.parse(await readTextFile(file.name));
const filePath = `${dir}${path.sep()}${file.name}`;
const data: TGApp.Plugins.UIAF.Achievement[] = JSON.parse(await readTextFile(filePath));
await TSUserAchi.mergeUiaf(data, uid);
} catch (e) {
await TGLogger.Error(`[UIAF][RESTORE] 恢复成就数据${file.name} `);

View File

@@ -56,16 +56,8 @@ create table if not exists GameAccount
primary key (gameBiz, gameUid)
);
-- @brief 名片数据
create table if not exists NameCard
(
name text,
desc text,
type text,
source text,
updated text,
primary key (name, type)
);
-- @brief 移除名片表
drop table if exists NameCard;
-- @brief 创建深渊数据表
create table if not exists SpiralAbyss

View File

@@ -4,8 +4,6 @@
* @since Beta v0.6.0
*/
import { transCharacterData, transFloorData } from "../utils/transAbyssData.js";
import { timeToSecond } from "../utils/transTime.js";
import { transUserRecord } from "../utils/transUserRecord.js";
import { transUserRoles } from "../utils/transUserRoles.js";
@@ -89,86 +87,6 @@ export function insertCharacterData(data: TGApp.App.Character.WikiBriefInfo): st
`;
}
/**
* @description 插入深渊数据
* @since Alpha v0.2.0
* @param {string} uid 用户 uid
* @param {TGApp.User.Abyss} data 深渊数据
* @returns {string} sql
*/
export function insertAbyssData(uid: string, data: TGApp.Game.Abyss.FullData): string {
const startTime = timeToSecond(data.start_time);
const endTime = timeToSecond(data.end_time);
const isUnlock = data.is_unlock ? 1 : 0;
const revealRank = transCharacterData(data.reveal_rank);
const defeatRank = transCharacterData(data.defeat_rank);
const damageRank = transCharacterData(data.damage_rank);
const takeDamageRank = transCharacterData(data.take_damage_rank);
const normalSkillRank = transCharacterData(data.normal_skill_rank);
const energySkillRank = transCharacterData(data.energy_skill_rank);
const floors = transFloorData(data.floors);
return `
INSERT INTO SpiralAbyss (uid, id, startTime, endTime, totalBattleTimes, totalWinTimes,
maxFloor, totalStar, isUnlock, revealRank, defeatRank, damageRank,
takeDamageRank, normalSkillRank, energySkillRank, floors, updated)
VALUES ('${uid}', ${data.schedule_id}, '${startTime}', '${endTime}', ${data.total_battle_times},
${data.total_win_times}, '${data.max_floor}', ${data.total_star},
${isUnlock}, '${revealRank}', '${defeatRank}', '${damageRank}', '${takeDamageRank}',
'${normalSkillRank}', '${energySkillRank}', '${floors}', datetime('now', 'localtime'))
ON CONFLICT(uid, id) DO UPDATE
SET startTime = '${startTime}',
endTime = '${endTime}',
totalBattleTimes = ${data.total_battle_times},
totalWinTimes = ${data.total_win_times},
maxFloor = '${data.max_floor}',
totalStar = ${data.total_star},
isUnlock = ${isUnlock},
revealRank = '${revealRank}',
defeatRank = '${defeatRank}',
damageRank = '${damageRank}',
takeDamageRank = '${takeDamageRank}',
normalSkillRank = '${normalSkillRank}',
energySkillRank = '${energySkillRank}',
floors = '${floors}',
updated = datetime('now', 'localtime');
`;
}
/**
* @description 恢复深渊数据
* @since Alpha v0.2.0
* @param {TGApp.Sqlite.Abyss.SingleTable} data 深渊数据
* @returns {string} sql
*/
export function importAbyssData(data: TGApp.Sqlite.Abyss.SingleTable): string {
return `
INSERT INTO SpiralAbyss (uid, id, startTime, endTime, totalBattleTimes, totalWinTimes,
maxFloor, totalStar, isUnlock, revealRank, defeatRank, damageRank,
takeDamageRank, normalSkillRank, energySkillRank, floors, updated)
VALUES ('${data.uid}', ${data.id}, '${data.startTime}', '${data.endTime}', ${data.totalBattleTimes},
${data.totalWinTimes}, '${data.maxFloor}', ${data.totalStar},
${data.isUnlock}, '${data.revealRank}', '${data.defeatRank}', '${data.damageRank}',
'${data.takeDamageRank}', '${data.normalSkillRank}', '${data.energySkillRank}', '${data.floors}',
datetime('now', 'localtime'))
ON CONFLICT(uid, id) DO UPDATE
SET startTime = '${data.startTime}',
endTime = '${data.endTime}',
totalBattleTimes = ${data.totalBattleTimes},
totalWinTimes = ${data.totalWinTimes},
maxFloor = '${data.maxFloor}',
totalStar = ${data.totalStar},
isUnlock = ${data.isUnlock},
revealRank = '${data.revealRank}',
defeatRank = '${data.defeatRank}',
damageRank = '${data.damageRank}',
takeDamageRank = '${data.takeDamageRank}',
normalSkillRank = '${data.normalSkillRank}',
energySkillRank = '${data.energySkillRank}',
floors = '${data.floors}',
updated = datetime('now', 'localtime');
`;
}
/**
* @description 插入原神战绩数据
* @since Alpha v0.2.0

View File

@@ -1,10 +1,10 @@
/**
* @file plugins/Sqlite/utils/transCharacter.ts
* @description Sqlite 数据转换
* @since Beta v0.3.9
* @since Beta v0.6.0
*/
import { timeToSecond } from "./transTime.js";
import { timestampToDate } from "../../../utils/toolFunc.js";
/**
* @description 将通过 api 获取到的深渊数据转换为数据库中的数据
@@ -66,13 +66,13 @@ function transLevelData(data: TGApp.Game.Abyss.Level): TGApp.Sqlite.Abyss.Level
/**
* @description 将通过 api 获取到的深渊数据转换为数据库中的数据
* @since Alpha v0.2.0
* @since Beta v0.6.0
* @param {TGApp.Game.Abyss.Battle} data 深渊数据
* @returns {TGApp.Sqlite.Abyss.Battle} 转换后的深渊数据
*/
function transBattleData(data: TGApp.Game.Abyss.Battle): TGApp.Sqlite.Abyss.Battle {
return {
time: timeToSecond(data.timestamp),
time: timestampToDate(Number(data.timestamp) * 1000),
characters: data.avatars.map((item) => {
return {
id: item.id,

View File

@@ -1,23 +0,0 @@
/**
* @file plugins Sqlite utils transTime.ts
* @description Sqlite 时间转换工具类
* @author BTMuli <bt-muli@outlook.com>
* @since Alpha v0.2.0
*/
/**
* @description 将时间戳转换为时间字符串
* @since Alpha v0.2.0
* @param {string} timestamp 时间戳 (秒)
* @returns {string} 时间字符串,格式为 YYYY-MM-DD HH:mm:ss
*/
export function timeToSecond(timestamp: string): string {
const date = new Date(Number(timestamp) * 1000);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hour = String(date.getHours()).padStart(2, "0");
const minute = String(date.getMinutes()).padStart(2, "0");
const second = String(date.getSeconds()).padStart(2, "0");
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
}