mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-04-15 21:03:29 +08:00
✨ 重构UIGF导入导出备份恢复,支持UIGF4.2
This commit is contained in:
@@ -1,63 +1,59 @@
|
||||
/**
|
||||
* 用户祈愿模块
|
||||
* @since Beta v0.9.1
|
||||
* @since Beta v0.9.5
|
||||
*/
|
||||
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import { path } from "@tauri-apps/api";
|
||||
import { exists, mkdir, readDir } from "@tauri-apps/plugin-fs";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { getWikiBrief, timestampToDate } from "@utils/toolFunc.js";
|
||||
import { exportUigf4Data, readUigf4Data, readUigfData, verifyUigfData } from "@utils/UIGF.js";
|
||||
import { getUtc8Time, getWikiBrief, timestampToDate } from "@utils/toolFunc.js";
|
||||
|
||||
import TGSqlite from "../index.js";
|
||||
|
||||
/**
|
||||
* 获取导入 Sql
|
||||
* @since Beta v.6.0
|
||||
* 导入物品
|
||||
* @since Beta v0.9.5
|
||||
* @param uid - UID
|
||||
* @param gacha - UIGF数据
|
||||
* @returns sql
|
||||
* @param item - UIGF数据
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
function getInsertSql(uid: string, gacha: TGApp.Plugins.UIGF.GachaItem): string {
|
||||
return `
|
||||
INSERT INTO GachaRecords (uid, gachaType, itemId, count, time, name, type, rank, id, uigfType, updated)
|
||||
VALUES ('${uid}', '${gacha.gacha_type}', '${gacha.item_id ?? null}', '${gacha.count ?? null}', '${gacha.time}',
|
||||
'${gacha.name}', '${gacha.item_type ?? null}', '${gacha.rank_type ?? null}', '${gacha.id}',
|
||||
'${gacha.uigf_gacha_type}', datetime('now', 'localtime')) ON CONFLICT (id)
|
||||
DO
|
||||
UPDATE
|
||||
SET uid = '${uid}',
|
||||
gachaType = '${gacha.gacha_type}',
|
||||
uigfType = '${gacha.uigf_gacha_type}',
|
||||
time = '${gacha.time}',
|
||||
itemId = '${gacha.item_id ?? null}',
|
||||
count = '${gacha.count ?? null}',
|
||||
name = '${gacha.name}',
|
||||
type = '${gacha.item_type ?? null}',
|
||||
rank = '${gacha.rank_type ?? null}',
|
||||
updated = datetime('now', 'localtime');
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 传入时间字符串跟对应时区,转成utc8时间字符串
|
||||
* @since Beta v0.7.5
|
||||
* @param time - 时间字符串
|
||||
* @param timezone - 时区
|
||||
* @returns 转换后的时间戳
|
||||
*/
|
||||
function getUtc8Time(time: string, timezone: number): string {
|
||||
const date = new Date(time);
|
||||
const diffTimezone = -timezone + 8;
|
||||
const realDate = new Date(date.getTime() + diffTimezone * 60 * 60 * 1000);
|
||||
return timestampToDate(realDate.getTime());
|
||||
async function insertGachaItem(uid: string, item: TGApp.Plugins.UIGF.GachaItem): Promise<void> {
|
||||
const db = await TGSqlite.getDB();
|
||||
const updateTime = timestampToDate(Date.now());
|
||||
await db.execute(
|
||||
`INSERT INTO GachaRecords(uid, gachaType, itemId, count, time,
|
||||
name, type, rank, id, uigfType, updated)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||
ON CONFLICT DO UPDATE
|
||||
SET uid = $1,
|
||||
gachaType = $2,
|
||||
itemId = $3,
|
||||
count = $4,
|
||||
time = $5,
|
||||
name = $6,
|
||||
type = $7,
|
||||
rank = $8,
|
||||
uigfType = $10,
|
||||
updated = $11;
|
||||
`,
|
||||
[
|
||||
uid,
|
||||
item.gacha_type,
|
||||
item.item_id ?? null,
|
||||
item.count ?? 1,
|
||||
item.time,
|
||||
item.name,
|
||||
item.item_type ?? null,
|
||||
item.rank_type ?? null,
|
||||
item.id,
|
||||
item.uigf_gacha_type,
|
||||
updateTime,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换祈愿数据,防止多语言
|
||||
* @since Beta v0.7.5
|
||||
* @since Beta v0.9.5
|
||||
* @param gacha - UIGF数据
|
||||
* @param timezone - 时区
|
||||
* @returns 转换后的数据
|
||||
@@ -67,7 +63,7 @@ function transGacha(
|
||||
timezone: number = 8,
|
||||
): TGApp.Plugins.UIGF.GachaItem {
|
||||
const find = getWikiBrief(gacha.item_id);
|
||||
if (!find) return gacha;
|
||||
if (!find) return { ...gacha, time: getUtc8Time(gacha.time, timezone) };
|
||||
return {
|
||||
gacha_type: gacha.gacha_type,
|
||||
item_id: gacha.item_id,
|
||||
@@ -246,7 +242,7 @@ async function cleanGachaRecords(
|
||||
|
||||
/**
|
||||
* 合并祈愿数据
|
||||
* @since Beta v0.9.0
|
||||
* @since Beta v0.9.5
|
||||
* @param uid - UID
|
||||
* @param data - UIGF数据
|
||||
* @param showProgress - 是否显示进度
|
||||
@@ -257,7 +253,6 @@ async function mergeUIGF(
|
||||
data: Array<TGApp.Plugins.UIGF.GachaItem>,
|
||||
showProgress: boolean = false,
|
||||
): Promise<void> {
|
||||
const db = await TGSqlite.getDB();
|
||||
let cnt = 0;
|
||||
const len = data.length;
|
||||
let progress = 0;
|
||||
@@ -272,9 +267,7 @@ async function mergeUIGF(
|
||||
}, 1000);
|
||||
}
|
||||
for (const gacha of data) {
|
||||
const trans = transGacha(gacha);
|
||||
const sql = getInsertSql(uid, trans);
|
||||
await db.execute(sql);
|
||||
await insertGachaItem(uid, transGacha(gacha));
|
||||
cnt++;
|
||||
}
|
||||
if (timer) {
|
||||
@@ -285,8 +278,8 @@ async function mergeUIGF(
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并祈愿数据(v4.0)
|
||||
* @since Beta v0.9.0
|
||||
* 合并祈愿数据(v4.x)
|
||||
* @since Beta v0.9.5
|
||||
* @param data - UIGF数据
|
||||
* @param showProgress - 是否显示进度
|
||||
* @returns 无返回值
|
||||
@@ -295,7 +288,6 @@ async function mergeUIGF4(
|
||||
data: TGApp.Plugins.UIGF.GachaHk4e,
|
||||
showProgress: boolean = false,
|
||||
): Promise<void> {
|
||||
const db = await TGSqlite.getDB();
|
||||
let cnt: number = 0;
|
||||
const len = data.list.length;
|
||||
let progress: number = 0;
|
||||
@@ -304,15 +296,13 @@ async function mergeUIGF4(
|
||||
timer = setInterval(async () => {
|
||||
progress = Math.round((cnt / len) * 100 * 100) / 100;
|
||||
const current = data.list[cnt]?.time ?? "";
|
||||
const name = data.list[cnt]?.name ?? "";
|
||||
const name = data.list[cnt]?.name ?? data.list[cnt]?.item_id;
|
||||
const rank = data.list[cnt]?.rank_type ?? "0";
|
||||
await showLoading.update(`[${progress}%][${current}] ${"⭐".repeat(Number(rank))}-${name}`);
|
||||
}, 1000);
|
||||
}
|
||||
for (const gacha of data.list) {
|
||||
const trans = transGacha(gacha, data.timezone);
|
||||
const sql = getInsertSql(data.uid.toString(), trans);
|
||||
await db.execute(sql);
|
||||
await insertGachaItem(data.uid.toString(), transGacha(gacha, data.timezone));
|
||||
cnt++;
|
||||
}
|
||||
if (timer) {
|
||||
@@ -322,85 +312,6 @@ async function mergeUIGF4(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 备份祈愿数据
|
||||
* @since Beta v0.9.0
|
||||
* @param dir - 备份目录
|
||||
* @returns 无返回值
|
||||
*/
|
||||
async function backUpUigf(dir: string): Promise<void> {
|
||||
if (!(await exists(dir))) {
|
||||
await TGLogger.Warn("不存在指定的祈愿备份目录,即将创建");
|
||||
await mkdir(dir, { recursive: true });
|
||||
}
|
||||
const uidList = await getUidList();
|
||||
const savePath = `${dir}${path.sep()}UIGF4.json`;
|
||||
await exportUigf4Data(uidList, savePath);
|
||||
await TGLogger.Info("祈愿数据备份完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复祈愿数据
|
||||
* @since Beta v0.9.0
|
||||
* @param dir - 备份目录
|
||||
* @returns 是否恢复成功
|
||||
*/
|
||||
async function restoreUigf(dir: string): Promise<boolean> {
|
||||
if (!(await exists(dir))) {
|
||||
await TGLogger.Warn("不存在指定的祈愿备份目录");
|
||||
return false;
|
||||
}
|
||||
let cnt = 0;
|
||||
// 旧版备份文件 UIGF_{{uid}}
|
||||
const legacyReg = /^UIGF_\d+\.json$/;
|
||||
const legacyFiles = (await readDir(dir)).filter(
|
||||
(item) => item.isFile && legacyReg.test(item.name),
|
||||
);
|
||||
if (legacyFiles.length > 0) {
|
||||
try {
|
||||
for (const file of legacyFiles) {
|
||||
await showLoading.update(`祈愿文件:${file.name}`);
|
||||
const filePath = `${dir}${path.sep()}${file.name}`;
|
||||
const check = await verifyUigfData(filePath);
|
||||
if (!check) {
|
||||
await showLoading.update(`UIGF数据校验失败`);
|
||||
await TGLogger.Warn(`UIGF数据校验失败${filePath}`);
|
||||
continue;
|
||||
}
|
||||
const data = await readUigfData(filePath);
|
||||
const uid = data.info.uid;
|
||||
await showLoading.update(`正在导入${data.info.uid}的${data.list.length}条祈愿数据`);
|
||||
await mergeUIGF(uid, data.list, true);
|
||||
cnt++;
|
||||
}
|
||||
} catch (e) {
|
||||
await TGLogger.Error(`恢复祈愿数据失败${dir}`);
|
||||
await TGLogger.Error(typeof e === "string" ? e : JSON.stringify(e));
|
||||
}
|
||||
}
|
||||
const filePath = `${dir}${path.sep()}UIGF4.json`;
|
||||
if (!(await exists(filePath))) {
|
||||
await TGLogger.Warn(`未检测到UIGF4备份文件`);
|
||||
} else {
|
||||
try {
|
||||
const check = await verifyUigfData(filePath, true);
|
||||
if (!check) {
|
||||
await TGLogger.Warn(`UIGF数据校验失败${filePath}`);
|
||||
} else {
|
||||
const data = await readUigf4Data(filePath);
|
||||
for (const uidData of data.hk4e) {
|
||||
await showLoading.update(`正在导入${uidData.uid}的${uidData.list.length}条祈愿记录`);
|
||||
await mergeUIGF4(uidData, true);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
await TGLogger.Error(`恢复祈愿数据失败${dir}`);
|
||||
await TGLogger.Error(typeof e === "string" ? e : JSON.stringify(e));
|
||||
}
|
||||
}
|
||||
return cnt > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新单条数据ID
|
||||
* @since Beta v0.9.0
|
||||
@@ -420,8 +331,6 @@ const TSUserGacha = {
|
||||
cleanGachaRecords,
|
||||
mergeUIGF,
|
||||
mergeUIGF4,
|
||||
backUpUigf,
|
||||
restoreUigf,
|
||||
update: {
|
||||
itemId: updateItemIdById,
|
||||
},
|
||||
|
||||
@@ -1,56 +1,132 @@
|
||||
/**
|
||||
* 千星奇域祈愿模块
|
||||
* @since Beta v0.8.4
|
||||
* @since Beta v0.9.5
|
||||
*/
|
||||
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import TGSqlite from "@Sql/index.js";
|
||||
import { exists, mkdir } from "@tauri-apps/plugin-fs";
|
||||
import TGLogger from "@utils/TGLogger.js";
|
||||
import { getUtc8Time, timestampToDate } from "@utils/toolFunc.js";
|
||||
|
||||
import { AppGachaBData } from "@/data/index.js";
|
||||
|
||||
/**
|
||||
* 获取导入 Sql
|
||||
* @since Beta v0.8.4
|
||||
* @param gacha - 抽卡记录数据
|
||||
* @returns sql
|
||||
* 插入颂愿数据
|
||||
* @since Beta v0.9.5
|
||||
* @param uid - UID
|
||||
* @param item - 颂愿数据
|
||||
* @returns 无返回值
|
||||
*/
|
||||
function getInsertSql(gacha: TGApp.Game.Gacha.GachaBItem): string {
|
||||
return `
|
||||
INSERT INTO GachaBRecords(id, uid, region, scheduleId, gachaType,
|
||||
opGachaType, time, itemId, name, type,
|
||||
rank, isUp, updated)
|
||||
VALUES ('${gacha.id}', '${gacha.uid}', '${gacha.region}', '${gacha.schedule_id}',
|
||||
'${gacha.op_gacha_type === "1000" ? "1000" : "2000"}', '${gacha.op_gacha_type}', '${gacha.time}',
|
||||
'${gacha.item_id}', '${gacha.item_name}', '${gacha.item_type}',
|
||||
'${gacha.rank_type}', '${gacha.is_up}', datetime('now', 'localtime'))
|
||||
ON CONFLICT (id)
|
||||
DO UPDATE
|
||||
SET uid = '${gacha.uid}',
|
||||
region = '${gacha.region}',
|
||||
scheduleId = '${gacha.schedule_id}',
|
||||
gachaType = '${gacha.op_gacha_type === "1000" ? "1000" : "2000"}',
|
||||
opGachaType = '${gacha.op_gacha_type}',
|
||||
time = '${gacha.time}',
|
||||
itemId = '${gacha.item_id}',
|
||||
name = '${gacha.item_name}',
|
||||
type = '${gacha.item_type}',
|
||||
rank = '${gacha.rank_type}',
|
||||
isUp = '${gacha.is_up}',
|
||||
updated = datetime('now', 'localtime');
|
||||
`;
|
||||
async function insertGachaItem(uid: string, item: TGApp.Plugins.UIGF.GachaItemB): Promise<void> {
|
||||
const db = await TGSqlite.getDB();
|
||||
const gachaType = item.op_gacha_type === "1000" ? "1000" : "2000";
|
||||
const updateTime = timestampToDate(Date.now());
|
||||
await db.execute(
|
||||
`
|
||||
INSERT INTO GachaBRecords(id, uid, scheduleId, gachaType, opGachaType, time,
|
||||
itemId, name, type, rank, updated)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||
ON CONFLICT (id)
|
||||
DO UPDATE
|
||||
SET uid = $2,
|
||||
scheduleId = $3,
|
||||
gachaType = $4,
|
||||
opGachaType = $5,
|
||||
time = $6,
|
||||
itemId = $7,
|
||||
name = $8,
|
||||
type = $9,
|
||||
rank = $10,
|
||||
updated = $11;
|
||||
`,
|
||||
[
|
||||
item.id,
|
||||
uid,
|
||||
item.schedule_id,
|
||||
gachaType,
|
||||
item.op_gacha_type,
|
||||
item.time,
|
||||
item.item_id,
|
||||
item.item_name,
|
||||
item.item_type,
|
||||
item.rank_type,
|
||||
updateTime,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入列表数据
|
||||
* @since Beta v0.8.4
|
||||
* @since Beta v0.9.5
|
||||
* @param uid - UID
|
||||
* @param list - 抽卡记录列表
|
||||
* @returns 无返回值
|
||||
*/
|
||||
async function insertGachaList(list: Array<TGApp.Game.Gacha.GachaBItem>): Promise<void> {
|
||||
const db = await TGSqlite.getDB();
|
||||
async function insertGachaList(
|
||||
uid: string,
|
||||
list: Array<TGApp.Plugins.UIGF.GachaItemB>,
|
||||
): Promise<void> {
|
||||
for (const gacha of list) {
|
||||
const sql = getInsertSql(gacha);
|
||||
await db.execute(sql);
|
||||
await insertGachaItem(uid, gacha);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换颂愿数据,防止多语言
|
||||
* @since Beta v0.9.5
|
||||
* @param gacha - 颂愿数据
|
||||
* @param timezone - 时区
|
||||
* @returns 转换后的数据
|
||||
*/
|
||||
function transGacha(
|
||||
gacha: TGApp.Plugins.UIGF.GachaItemB,
|
||||
timezone: number = 8,
|
||||
): TGApp.Plugins.UIGF.GachaItemB {
|
||||
const find = AppGachaBData.find((i) => i.id === gacha.item_id);
|
||||
if (!find) return gacha;
|
||||
return {
|
||||
id: gacha.id,
|
||||
item_id: gacha.item_id,
|
||||
item_name: find.name,
|
||||
item_type: find.type,
|
||||
op_gacha_type: gacha.op_gacha_type,
|
||||
rank_type: find.rank.toString(),
|
||||
schedule_id: gacha.schedule_id,
|
||||
time: getUtc8Time(gacha.time, timezone),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入UIGF4数据
|
||||
* @since Beta v0.9.5
|
||||
* @param data - UIGF数据
|
||||
* @param showProgress - 是否显示进度
|
||||
* @returns 无返回值
|
||||
*/
|
||||
async function mergeUIGF4(
|
||||
data: TGApp.Plugins.UIGF.GachaUgc,
|
||||
showProgress: boolean = false,
|
||||
): Promise<void> {
|
||||
let cnt: number = 0;
|
||||
const len = data.list.length;
|
||||
let progress: number = 0;
|
||||
let timer: NodeJS.Timeout | null = null;
|
||||
if (showProgress) {
|
||||
timer = setInterval(async () => {
|
||||
progress = Math.round((cnt / len) * 100 * 100) / 100;
|
||||
const current = data.list[cnt].time ?? "";
|
||||
const name = data.list[cnt].item_name ?? "";
|
||||
const rank = data.list[cnt].rank_type ?? "0";
|
||||
await showLoading.update(`[${progress}%][${current}] ${"⭐".repeat(Number(rank))}-${name}`);
|
||||
}, 1000);
|
||||
}
|
||||
for (const gacha of data.list) {
|
||||
await insertGachaItem(data.uid.toString(), transGacha(gacha, data.timezone));
|
||||
cnt++;
|
||||
}
|
||||
if (timer) {
|
||||
clearInterval(timer);
|
||||
progress = 100;
|
||||
await showLoading.update(`[${progress}%] 完成`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,35 +181,6 @@ async function getGachaRecords(
|
||||
return await db.select("SELECT * FROM GachaBRecords WHERE uid = ?;", [uid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 备份祈愿数据
|
||||
* @since Beta v0.8.4
|
||||
* @param dir - 备份目录
|
||||
* @remarks 等UIGF标准最终确定后与TSUserGacha合并
|
||||
*/
|
||||
async function backUpUigf(dir: string): Promise<void> {
|
||||
if (!(await exists(dir))) {
|
||||
await TGLogger.Warn("不存在指定的祈愿备份目录,即将创建");
|
||||
await mkdir(dir, { recursive: true });
|
||||
}
|
||||
showSnackbar.error(`千星奇域祈愿数据备份功能尚未实现,请耐心等待后续版本更新。`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复祈愿数据
|
||||
* @since Beta v0.8.4
|
||||
* @param dir - 恢复目录
|
||||
* @remarks 等UIGF标准最终确定后与TSUserGacha合并
|
||||
* @returns 是否恢复成功
|
||||
*/
|
||||
async function restoreUigf(dir: string): Promise<boolean> {
|
||||
if (!(await exists(dir))) {
|
||||
await TGLogger.Warn("不存在指定的祈愿备份目录");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除用户祈愿数据
|
||||
* @since Beta v0.8.4
|
||||
@@ -145,18 +192,13 @@ async function deleteRecords(uid: string): Promise<void> {
|
||||
await db.execute("DELETE FROM GachaBRecords WHERE uid = ?;", [uid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 千星奇域祈愿模块
|
||||
* @since Beta v0.8.4
|
||||
*/
|
||||
const TSUserGachaB = {
|
||||
getUidList,
|
||||
getGachaCheck,
|
||||
getGachaRecords,
|
||||
insertGachaList,
|
||||
backUpUigf,
|
||||
restoreUigf,
|
||||
deleteRecords,
|
||||
mergeUIGF4,
|
||||
};
|
||||
|
||||
export default TSUserGachaB;
|
||||
|
||||
@@ -182,12 +182,12 @@ create table if not exists GachaRecords
|
||||
updated text
|
||||
);
|
||||
|
||||
-- @brief 创建千星奇域祈愿数据表
|
||||
-- @brief 创建颂愿数据表
|
||||
create table if not exists GachaBRecords
|
||||
(
|
||||
id text primary key not null,
|
||||
uid text,
|
||||
region text,
|
||||
region text, -- @deprecated
|
||||
scheduleId text,
|
||||
gachaType text,
|
||||
opGachaType text,
|
||||
@@ -196,7 +196,7 @@ create table if not exists GachaBRecords
|
||||
name text,
|
||||
type text,
|
||||
rank text,
|
||||
isUp text,
|
||||
isUp text, -- @deprecated
|
||||
updated text
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user