From 1718b87adfd016c26174078e4fd8f5ac78b7b4e4 Mon Sep 17 00:00:00 2001 From: BTMuli Date: Tue, 1 Jul 2025 16:21:13 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8C=B1=20=E5=B9=BD=E5=A2=83=E5=8D=B1?= =?UTF-8?q?=E6=88=98=E6=95=B0=E6=8D=AE=E5=BA=93=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #157 --- src/pages/User/Challenge.vue | 239 +++++++++++++++++++- src/pages/User/Combat.vue | 22 +- src/plugins/Sqlite/index.ts | 4 +- src/plugins/Sqlite/modules/userChallenge.ts | 177 +++++++++++++++ src/plugins/Sqlite/modules/userCombat.ts | 49 ++-- src/plugins/Sqlite/sql/createTable.sql | 37 ++- src/plugins/Sqlite/utils/transUserCombat.ts | 27 --- src/request/recordReq.ts | 22 ++ src/types/Game/Challenge.d.ts | 193 ++++++++++++++++ src/types/Sqlite/Challenge.d.ts | 55 +++++ src/utils/dataBS.ts | 15 +- 11 files changed, 754 insertions(+), 86 deletions(-) create mode 100644 src/plugins/Sqlite/modules/userChallenge.ts delete mode 100644 src/plugins/Sqlite/utils/transUserCombat.ts create mode 100644 src/types/Sqlite/Challenge.d.ts diff --git a/src/pages/User/Challenge.vue b/src/pages/User/Challenge.vue index 7114841d..571e222f 100644 --- a/src/pages/User/Challenge.vue +++ b/src/pages/User/Challenge.vue @@ -37,7 +37,7 @@ label="服务器" width="200px" density="compact" - :disabled="isReq" + :disabled="reqPop" /> @@ -72,14 +72,52 @@ +
+ + +
+ {{ item.name }} + {{ item.startTime.slice(0, 10) }} ~ {{ item.endTime.slice(0, 10) }} +
+
+
+ + +
+ {{ JSON.stringify(item, null, 2) }} +
+
+
+
+ empty + 暂无数据,请尝试刷新 +
+
diff --git a/src/pages/User/Combat.vue b/src/pages/User/Combat.vue index c3abe3d8..9e9748c8 100644 --- a/src/pages/User/Combat.vue +++ b/src/pages/User/Combat.vue @@ -135,14 +135,15 @@ onMounted(async () => { await TGLogger.Info("[UserCombat][onMounted] 打开真境剧诗页面"); await showLoading.update("正在加载UID列表"); uidList.value = await TSUserCombat.getAllUid(); - if (uidList.value.includes(account.value.gameUid)) uidCur.value = account.value.gameUid; - else if (uidList.value.length > 0) { - uidCur.value = uidList.value[0]; + if (uidList.value.length === 0) { + uidCur.value = ""; + } else { + if (uidList.value.includes(account.value.gameUid)) uidCur.value = account.value.gameUid; + else uidCur.value = uidList.value[0]; await showLoading.update(`正在加载UID${uidCur.value}的剧诗数据`); - } else uidCur.value = ""; + } await loadCombat(); await showLoading.end(); - console.log("UserCombat", localCombat.value); }); watch(() => uidCur.value, loadCombat); @@ -150,6 +151,7 @@ watch(() => uidCur.value, loadCombat); async function toAbyss(): Promise { await router.push({ name: "深境螺旋" }); } + async function toChallenge(): Promise { await router.push({ name: "幽境危战" }); } @@ -174,7 +176,7 @@ async function loadWiki(): Promise { async function refreshCombat(): Promise { if (!cookie.value) { showSnackbar.error("未登录"); - await TGLogger.Warn("[UserCombat][getAbyssData] 未登录"); + await TGLogger.Warn("[UserCombat][refreshCombat] 未登录"); return; } if (uidCur.value && uidCur.value !== account.value.gameUid) { @@ -196,7 +198,7 @@ async function refreshCombat(): Promise { return; } } - await TGLogger.Info("[UserCombat][getCombatData] 更新剧诗数据"); + await TGLogger.Info("[UserCombat][refreshCombat] 更新剧诗数据"); await showLoading.start(`正在获取${account.value.gameUid}的剧诗数据`); const res = await recordReq.roleCombat(cookie.value, account.value); if (res === false) { @@ -207,8 +209,8 @@ async function refreshCombat(): Promise { if ("retcode" in res) { await showLoading.end(); showSnackbar.error(`[${res.retcode}]${res.message}`); - await TGLogger.Error(`[UserCombat][getCombatData] 获取${account.value.gameUid}的剧诗数据失败`); - await TGLogger.Error(`[UserCombat][getCombatData] ${res.retcode} ${res.message}`); + await TGLogger.Error(`[UserCombat][refreshCombat] 获取${account.value.gameUid}的剧诗数据失败`); + await TGLogger.Error(`[UserCombat][refreshCombat] ${res.retcode} ${res.message}`); return; } await showLoading.update("正在保存剧诗数据"); @@ -225,7 +227,7 @@ async function refreshCombat(): Promise { async function shareCombat(): Promise { await TGLogger.Info(`[UserCombat][shareCombat][${userTab.value}] 生成剧诗数据分享图片`); - const fileName = `【剧诗数据】${userTab.value}-${account.value.gameUid}.png`; + const fileName = `【真境剧诗】${userTab.value}-${uidCur.value}.png`; const shareDom = document.querySelector(`#user-combat-${userTab.value}`); if (shareDom === null) { showSnackbar.error("未找到分享数据"); diff --git a/src/plugins/Sqlite/index.ts b/src/plugins/Sqlite/index.ts index f67eda23..90a819b2 100644 --- a/src/plugins/Sqlite/index.ts +++ b/src/plugins/Sqlite/index.ts @@ -1,7 +1,7 @@ /** * @file plugins/Sqlite/index.ts * @description Sqlite 数据库操作类 - * @since Beta v0.6.1 + * @since Beta v0.8.0 */ import { app } from "@tauri-apps/api"; @@ -19,6 +19,8 @@ class Sqlite { "GachaRecords", "GameAccount", "SpiralAbyss", + "RoleCombat", + "HardChallenge", "UFCollection", "UFMap", "UFPost", diff --git a/src/plugins/Sqlite/modules/userChallenge.ts b/src/plugins/Sqlite/modules/userChallenge.ts new file mode 100644 index 00000000..b2db0b86 --- /dev/null +++ b/src/plugins/Sqlite/modules/userChallenge.ts @@ -0,0 +1,177 @@ +/** + * @file sqlite/modules/userChallenge.ts + * @description 幽境危战模块 + * @since Beta v0.8.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"; + +/** + * @description 将通过 api 获取到的数据转换为数据库中的数据 + * @since Beta v0.8.0 + * @param {TGApp.Game.Challenge.ChallengeItem} data - 挑战数据 + * @returns {TGApp.Sqlite.Challenge.SingleTable} 转换后的数据 + */ +function transUserChallenge( + data: TGApp.Game.Challenge.ChallengeItem, +): TGApp.Sqlite.Challenge.SingleTable { + return { + uid: "", + id: Number(data.schedule.schedule_id), + startTime: timestampToDate(Number(data.schedule.start_time) * 1000), + endTime: timestampToDate(Number(data.schedule.end_time) * 1000), + name: data.schedule.name, + single: data.single, + mp: data.mp, + updated: timestampToDate(new Date().getTime()), + }; +} + +/** + * @description 直接插入数据 + * @since Beta v0.8.0 + * @param {TGApp.Sqlite.Challenge.SingleTable} data - 挑战数据 + * @param {string} [uid] - 用户UID + * @returns {string} - 插入 SQL 语句 + */ +function getInsertSql(data: TGApp.Sqlite.Challenge.SingleTable, uid?: string): string { + const timeNow = timestampToDate(new Date().getTime()); + return ` + INSERT INTO HardChallenge(uid, id, startTime, endTime, name, single, mp, updated) + VALUES ('${uid ?? data.uid}', ${data.id}, '${data.startTime}', '${data.endTime}', '${data.name}', + '${JSON.stringify(data.single)}', '${JSON.stringify(data.mp)}', '${timeNow}') ON CONFLICT(uid,id) DO + UPDATE + SET startTime = '${data.startTime}', + endTime = '${data.endTime}', + name = '${data.name}', + single = '${JSON.stringify(data.single)}', + mp = '${JSON.stringify(data.mp)}', + updated = '${timeNow}' + `; +} + +/** + * @description 获取所有数据的UID + * @since Beta v0.8.0 + * @return {Promise} - 所有数据的UID + */ +async function getAllUid(): Promise> { + const db = await TGSqlite.getDB(); + type resType = Array<{ uid: string }>; + const res = await db.select("SELECT DISTINCT uid FROM HardChallenge;"); + return res.map((i) => i.uid); +} + +/** + * @description 获取挑战数据 + * @since Beta v0.8.0 + * @param {string} [uid] - 游戏UID + * @return {Promise>} - 挑战数据 + */ +async function getChallenge(uid?: string): Promise> { + const db = await TGSqlite.getDB(); + let resR: Array; + if (uid === undefined) { + resR = await db.select>( + "SELECT * FROM HardChallenge ORDER BY id DESC;", + ); + } else { + resR = await db.select>( + `SELECT * + FROM HardChallenge + WHERE uid = ?;`, + [uid], + ); + } + const res: Array = []; + for (const raw of resR) { + res.push({ + uid: raw.uid, + id: raw.id, + startTime: raw.startTime, + endTime: raw.endTime, + name: raw.name, + single: JSON.parse(raw.single), + mp: JSON.parse(raw.mp), + updated: raw.updated, + }); + } + return res; +} + +/** + * @description 保存挑战数据 + * @since Beta v0.8.0 + * @param {string} uid - 游戏UID + * @param {TGApp.Game.Challenge.ChallengeItem} data - 挑战数据 + * @return {Promise} + */ +async function saveChallenge(uid: string, data: TGApp.Game.Challenge.ChallengeItem): Promise { + const db = await TGSqlite.getDB(); + await db.execute(getInsertSql(transUserChallenge(data), uid)); +} + +/** + * @description 删除指定UID的挑战数据 + * @since Beta v0.8.0 + * @param {string} uid - 游戏UID + * @return {Promise} + */ +async function delChallenge(uid: string): Promise { + const db = await TGSqlite.getDB(); + await db.execute("DELETE FROM HardChallenge WHERE uid = ?;", [uid]); +} + +/** + * @description 备份挑战数据 + * @since Beta v0.8.0 + * @param {string} dir - 备份目录 + * @return {Promise} + */ +async function backupChallenge(dir: string): Promise { + if (!(await exists(dir))) { + await mkdir(dir, { recursive: true }); + await TGLogger.Warn(`[TSUserChallenge][Backup] 未检测到备份目录,已创建`); + } + const data = await getChallenge(); + await writeTextFile(`${dir}${path.sep()}challenge.json`, JSON.stringify(data)); +} + +/** + * @description 恢复挑战数据 + * @since Beta v0.8.0 + * @param {string} dir - 备份目录 + * @return {Promise} + */ +async function restoreChallenge(dir: string): Promise { + const filePath = `${dir}${path.sep()}challenge.json`; + if (!(await exists(filePath))) return false; + try { + const data: Array = JSON.parse( + await readTextFile(filePath), + ); + const db = await TGSqlite.getDB(); + for (const challenge of data) await db.execute(getInsertSql(challenge)); + return true; + } catch (e) { + await TGLogger.Error(`[TSUserChallenge][Restore] 恢复挑战数据失败: `); + await TGLogger.Error(`${e}`); + return false; + } +} + +const TSUserChallenge = { + getAllUid, + getChallenge, + saveChallenge, + delChallenge, + backupChallenge, + restoreChallenge, +}; + +export default TSUserChallenge; diff --git a/src/plugins/Sqlite/modules/userCombat.ts b/src/plugins/Sqlite/modules/userCombat.ts index 2296d92c..254071eb 100644 --- a/src/plugins/Sqlite/modules/userCombat.ts +++ b/src/plugins/Sqlite/modules/userCombat.ts @@ -1,7 +1,7 @@ /** * @file plugins/Sqlite/modules/userCombat.ts * @description Sqlite-幻想真境剧诗模块 - * @since Beta v0.6.3 + * @since Beta v0.8.0 */ import { path } from "@tauri-apps/api"; @@ -10,13 +10,32 @@ import TGLogger from "@utils/TGLogger.js"; import { timestampToDate } from "@utils/toolFunc.js"; import TGSqlite from "../index.js"; -import { transUserCombat } from "../utils/transUserCombat.js"; + +/** + * @description 将通过 api 获取到的数据转换为数据库中的数据 + * @since Beta v0.8.0 + * @param {TGApp.Game.Combat.Combat} data - 剧诗数据 + * @returns {TGApp.Sqlite.Combat.SingleTable} 转换后端数据 + */ +function transUserCombat(data: TGApp.Game.Combat.Combat): TGApp.Sqlite.Combat.SingleTable { + return { + uid: "", + detail: data.detail, + endTime: timestampToDate(Number(data.schedule.end_time) * 1000), + hasData: data.has_data, + hasDetailData: data.has_detail_data, + id: data.schedule.schedule_id, + startTime: timestampToDate(Number(data.schedule.start_time) * 1000), + stat: data.stat, + updated: timestampToDate(new Date().getTime()), + }; +} /** * @description 直接插入数据 * @since Beta v0.6.3 - * @param {string} uid 用户UID * @param {TGApp.Sqlite.Combat.SingleTable} data 剧诗数据 + * @param {string} [uid] 用户UID * @returns {string} */ function getInsertSql(data: TGApp.Sqlite.Combat.SingleTable, uid?: string): string { @@ -41,7 +60,7 @@ function getInsertSql(data: TGApp.Sqlite.Combat.SingleTable, uid?: string): stri /** * @description 获取所有有数据的UID * @since Beta v0.6.3 - * @returns {Promise} + * @returns {Promise>} */ async function getAllUid(): Promise> { const db = await TGSqlite.getDB(); @@ -52,20 +71,20 @@ async function getAllUid(): Promise> { /** * @description 获取剧诗数据 - * @since Beta v0.6.3 - * @param {string} uid - 游戏UID + * @since Beta v0.8.0 + * @param {string} [uid] - 游戏UID * @returns {Promise} */ async function getCombat(uid?: string): Promise { const db = await TGSqlite.getDB(); - let resR: TGApp.Sqlite.Combat.RawTable[]; + let resR: Array; if (uid === undefined) { - resR = await db.select( - "SELECT * FROM RoleCombat order by id DESC;", + resR = await db.select>( + "SELECT * FROM RoleCombat ORDER BY id DESC;", ); } else { - resR = await db.select( - "SELECT * FROM RoleCombat WHERE uid = ? order by id DESC;", + resR = await db.select>( + "SELECT * FROM RoleCombat WHERE uid = ?;", [uid], ); } @@ -118,7 +137,7 @@ async function delCombat(uid: string): Promise { async function backupCombat(dir: string): Promise { if (!(await exists(dir))) { await mkdir(dir, { recursive: true }); - await TGLogger.Warn(`未检测到备份目录,已创建`); + await TGLogger.Warn(`[TSUserCombat][Backup] 未检测到备份目录,已创建`); } const data = await getCombat(); await writeTextFile(`${dir}${path.sep()}combat.json`, JSON.stringify(data)); @@ -126,7 +145,7 @@ async function backupCombat(dir: string): Promise { /** * @description 恢复剧诗数据 - * @since Beta v0.6.3 + * @since Beta v0.8.0 * @param {string} dir - 备份文件目录 * @returns {Promise} */ @@ -134,12 +153,12 @@ async function restoreCombat(dir: string): Promise { const filePath = `${dir}${path.sep()}combat.json`; if (!(await exists(filePath))) return false; try { - const data: TGApp.Sqlite.Combat.SingleTable[] = JSON.parse(await readTextFile(filePath)); + const data: Array = JSON.parse(await readTextFile(filePath)); const db = await TGSqlite.getDB(); for (const abyss of data) await db.execute(getInsertSql(abyss)); return true; } catch (e) { - await TGLogger.Error(`恢复剧诗数据失败${filePath}`); + await TGLogger.Error(`[TSUserCombat][Restore] 恢复剧诗数据失败${filePath}`); await TGLogger.Error(`${e}`); return false; } diff --git a/src/plugins/Sqlite/sql/createTable.sql b/src/plugins/Sqlite/sql/createTable.sql index a19d7b81..fadf548b 100644 --- a/src/plugins/Sqlite/sql/createTable.sql +++ b/src/plugins/Sqlite/sql/createTable.sql @@ -1,6 +1,6 @@ -- @file plugins/Sqlite/sql/createTable.sql -- @brief sqlite数据库创建表语句 --- @since Beta v0.7.2 +-- @since Beta v0.8.0 -- @brief 创建成就数据表 create table if not exists Achievements @@ -47,27 +47,6 @@ create table if not exists GameAccount primary key (uid, gameBiz, gameUid) ); --- @brief 创建临时游戏账号数据表用于迁移数据 -create table GameAccountTemp -( - uid text, - gameBiz text, - gameUid text, - isChosen boolean, - isOfficial boolean, - level integer, - nickname text, - region text, - regionName text, - updated text, - primary key (uid, gameBiz, gameUid) -); - ---- @brief 迁移数据 -insert into GameAccountTemp select * from GameAccount; -drop table GameAccount; -alter table GameAccountTemp rename to GameAccount; - -- @brief 创建深渊数据表 create table if not exists SpiralAbyss ( @@ -107,6 +86,20 @@ create table if not exists RoleCombat primary key (uid, id) ); +-- @brief 创建幽境危战数据表 +create table if not exists HardChallenge +( + uid text, + id integer, + startTime text, + endTime text, + name text, + single text, + mp text, + updated text, + primary key (uid, id) +); + -- @brief 创建战绩数据表 create table if not exists UserRecord ( diff --git a/src/plugins/Sqlite/utils/transUserCombat.ts b/src/plugins/Sqlite/utils/transUserCombat.ts deleted file mode 100644 index 12053d6d..00000000 --- a/src/plugins/Sqlite/utils/transUserCombat.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @file plugins/Sqlite/utils/transUserCombat.ts - * @description Sqlite 数据转换-幻想真境剧诗 - * @since Beta v0.6.3 - */ - -import { timestampToDate } from "@utils/toolFunc.js"; - -/** - * @description 将通过 api 获取到的数据转换为数据库中的数据 - * @since Beta v0.6.3 - * @param {TGApp.Game.Combat.Combat} data - 剧诗数据 - * @returns {TGApp.Sqlite.Combat.SingleTable} 转换后端数据 - */ -export function transUserCombat(data: TGApp.Game.Combat.Combat): TGApp.Sqlite.Combat.SingleTable { - return { - uid: "", - detail: data.detail, - endTime: timestampToDate(Number(data.schedule.end_time) * 1000), - hasData: data.has_data, - hasDetailData: data.has_detail_data, - id: data.schedule.schedule_id, - startTime: timestampToDate(Number(data.schedule.start_time) * 1000), - stat: data.stat, - updated: timestampToDate(new Date().getTime()), - }; -} diff --git a/src/request/recordReq.ts b/src/request/recordReq.ts index 326b7a65..4bfd5e3a 100644 --- a/src/request/recordReq.ts +++ b/src/request/recordReq.ts @@ -166,12 +166,34 @@ async function hardChallengePopularity( return resp; } +/** + * @description 获取挑战数据 + * @since Beta v0.8.0 + * @param {TGApp.App.Account.Cookie} cookie Cookie + * @param {TGApp.Sqlite.Account.Game} user 用户 + * @returns {Promise} + */ +async function hardChallengeDetail( + cookie: TGApp.App.Account.Cookie, + user: TGApp.Sqlite.Account.Game, +): Promise { + const ck = { account_id: cookie.account_id, cookie_token: cookie.cookie_token }; + const params = { need_detail: true, role_id: user.gameUid, server: user.region }; + const resp = await TGHttp( + `${trgAbu}hard_challenge`, + { method: "GET", headers: getRequestHeader(ck, "GET", params), query: params }, + ); + if (resp.retcode !== 0) return resp; + return resp.data; +} + const recordReq = { index: index, character: { list: characterList, detail: characterDetail }, roleCombat: roleCombat, spiralAbyss: spiralAbyss, challenge: { + detail: hardChallengeDetail, pop: hardChallengePopularity, }, }; diff --git a/src/types/Game/Challenge.d.ts b/src/types/Game/Challenge.d.ts index 42acaf14..98b098b1 100644 --- a/src/types/Game/Challenge.d.ts +++ b/src/types/Game/Challenge.d.ts @@ -38,4 +38,197 @@ declare namespace TGApp.Game.Challenge { image: string; rarity: number; }; + + /** + * @description 挑战数据返回响应(详细) + * @since Beta v0.8.0 + * @interface ChallengeResp + * @extends TGApp.BBS.Response.BaseWithData + */ + type ChallengeResp = TGApp.BBS.Response.BaseWithData; + + /** + * @description 挑战数据返回(详细) + * @since Beta v0.8.0 + * @interface ChallengeRes + * @property {Array} data - 挑战数据列表 + * @property {boolean} is_unlock - 是否解锁挑战 + * @property {ChallengeLink} link - 挑战链接信息 + */ + type ChallengeRes = { data: Array; is_unlock: boolean; link: ChallengeLink }; + + /** + * @description 挑战链接信息 + * @since Beta v0.8.0 + * @interface ChallengeLink + * @property {string} lineup_link - 队伍配置链接 + * @property {string} play_link - 挑战链接 + */ + type ChallengeLink = { lineup_link: string; play_link: string }; + + /** + * @description 挑战数据项 + * @since Beta v0.8.0 + * @interface ChallengeItem + * @property {ChallengeSchedule} schedule - 挑战周期信息 + * @property {ChallengeSingle} single - 单次挑战数据 + * @property {ChallengeMp} mp - 多人挑战数据 + * @property {Array} blings - 挑战相关的其他数据(如成就等) + */ + type ChallengeItem = { + schedule: ChallengeSchedule; + single: ChallengeSingle; + mp: ChallengeMp; + blings: Array; + }; + + /** + * @description 多人挑战数据 + * @since Beta v0.8.0 + * @interface ChallengeMp + * @todo 待测试 + */ + type ChallengeMp = ChallengeSingle; + + /** + * @description 时间对象 + * @since Beta v0.8.0 + * @interface DateTime + * @property {number} year - 年份 + * @property {number} month - 月份(1-12) + * @property {number} day - 日(1-31) + * @property {number} hour - 小时(0-23) + * @property {number} minute - 分钟(0-59) + * @property {number} second - 秒(0-59) + */ + type DateTime = { + year: number; + month: number; + day: number; + hour: number; + minute: number; + second: number; + }; + + /** + * @description 挑战周期信息 + * @since Beta v0.8.0 + * @interface ChallengeSchedule + * @property {string} schedule_id - 挑战周期 ID + * @property {string} start_time - 挑战开始时间(秒级时间戳) + * @property {string} end_time - 挑战结束时间(秒级时间戳) + * @property {DateTime} start_date_time - 挑战开始时间(DateTime 对象) + * @property {DateTime} end_date_time - 挑战结束时间(DateTime 对象) + * @property {boolean} is_valid - 是否有效 + * @property {string} name - 挑战名称 + */ + type ChallengeSchedule = { + schedule_id: string; + start_time: string; + end_time: string; + start_date_time: DateTime; + end_date_time: DateTime; + is_valid: boolean; + name: string; + }; + + /** + * @description 单次挑战数据 + * @since Beta v0.8.0 + * @interface ChallengeSingle + * @property {SingleBest} best - 最佳挑战数据 + * @property {Array} challenge - 挑战列表 + * @property {boolean} has_data - 是否有数据 + */ + type ChallengeSingle = { best: SingleBest; challenge: Array; has_data: boolean }; + + /** + * @description 单次挑战最佳数据 + * @since Beta v0.8.0 + * @interface SingleBest + * @property {number} difficulty - 挑战难度 + * @property {number} second - 挑战用时(秒) + * @property {string} icon - 挑战图标名称 + */ + type SingleBest = { difficulty: number; second: number; icon: string }; + + /** + * @description 单次挑战数据项 + * @since Beta v0.8.0 + * @interface SingleChallenge + * @property {string} name - 怪物名称 + * @property {number} seconds - 挑战用时(秒) + * @property {Array} teams - 挑战队伍列表 + * @property {Array} best_avatar - 最佳角色列表 + * @property {SingleMonster} monster - 挑战怪物数据 + */ + type SingleChallenge = { + name: string; + seconds: number; + teams: Array; + best_avatar: Array; + monster: SingleMonster; + }; + + /** + * @description 单次挑战队伍数据 + * @since Beta v0.8.0 + * @interface SingleTeam + * @property {number} avatar_id - 角色 ID + * @property {string} name - 角色名称 + * @property {string} element - 角色元素 + * @property {string} image - 角色头像图片 URL + * @property {number} level - 角色等级 + * @property {number} rarity - 角色稀有度 + * @property {number} rank - 角色命座 + */ + type SingleTeam = { + avatar_id: number; + name: string; + element: string; + image: string; + level: number; + rarity: number; + rank: number; + }; + + /** + * @description 单次挑战最佳角色数据 + * @since Beta v0.8.0 + * @interface SingleAvatar + * @property {number} avatar_id - 角色 ID + * @property {string} side_icon - 角色侧边图标 URL + * @property {string} dps - 角色 DPS 数据 + * @property {string} type - 1-最强一击,2-最高总伤害 + */ + type SingleAvatar = { avatar_id: number; side_icon: string; dps: string; type: string }; + + /** + * @description 单次挑战怪物数据 + * @since Beta v0.8.0 + * @interface SingleMonster + * @property {string} name - 怪物名称 + * @property {number} level - 怪物等级 + * @property {string} icon - 怪物图标 URL + * @property {Array} desc - 怪物描述列表 + * @property {Array} tags - 怪物标签列表 + * @property {string} monster_id - 怪物 ID + */ + type SingleMonster = { + name: string; + level: number; + icon: string; + desc: Array; + tags: Array; + monster_id: string; + }; + + /** + * @description 怪物标签 + * @since Beta v0.8.0 + * @interface MonsterTag + * @property {string} type - 标签类型 + * @property {string} desc - 标签描述 + */ + type MonsterTag = { type: string; desc: string }; } diff --git a/src/types/Sqlite/Challenge.d.ts b/src/types/Sqlite/Challenge.d.ts new file mode 100644 index 00000000..099c03f4 --- /dev/null +++ b/src/types/Sqlite/Challenge.d.ts @@ -0,0 +1,55 @@ +/** + * @file types/Sqlite/Challenge.d.ts + * @description 幽境危战类型定义文件 + * @since Beta v0.8.0 + */ + +declare namespace TGApp.Sqlite.Challenge { + /** + * @description 数据库-幽境危战表(原始数据) + * @since Beta v0.8.0 + * @interface RawTable + * @property {string} uid - 用户 UID + * @property {number} id - 挑战 ID + * @property {string} startTime - 开始时间 + * @property {string} endTime - 结束时间 + * @property {string} name - 挑战名称 + * @property {string} single - 挑战单个数据(JSON 字符串) + * @property {string} mp - 挑战多人数据(JSON 字符串) + * @property {string} updated - 更新时间 + */ + type RawTable = { + uid: string; + id: number; + startTime: string; + endTime: string; + name: string; + single: string; // JSON 字符串 + mp: string; // JSON 字符串 + updated: string; + }; + + /** + * @description 数据库-幽境危战表 + * @since Beta v0.8.0 + * @interface SingleTable + * @property {string} uid - 用户 UID + * @property {number} id - 挑战 ID + * @property {string} startTime - 开始时间 + * @property {string} endTime - 结束时间 + * @property {string} name - 挑战名称 + * @property {TGApp.Game.Challenge.ChallengeSingle} single - 挑战单个数据 + * @property {TGApp.Game.Challenge.ChallengeMp} mp - 挑战多人数据 + * @property {string} updated - 更新时间 + */ + type SingleTable = { + uid: string; + id: number; + startTime: string; + endTime: string; + name: string; + single: TGApp.Game.Challenge.ChallengeSingle; + mp: TGApp.Game.Challenge.ChallengeMp; + updated: string; + }; +} diff --git a/src/utils/dataBS.ts b/src/utils/dataBS.ts index 5283a6fa..0bd51b66 100644 --- a/src/utils/dataBS.ts +++ b/src/utils/dataBS.ts @@ -1,13 +1,14 @@ /** * @file utils/dataBS.ts * @description 用户数据的备份、恢复、迁移 - * @since Beta v0.6.7 + * @since Beta v0.8.0 */ import showLoading from "@comp/func/loading.js"; import showSnackbar from "@comp/func/snackbar.js"; import TSUserAbyss from "@Sqlm/userAbyss.js"; import TSUserAccount from "@Sqlm/userAccount.js"; import TSUserAchi from "@Sqlm/userAchi.js"; +import TSUserChallenge from "@Sqlm/userChallenge.js"; import TSUserCombat from "@Sqlm/userCombat.js"; import TSUserGacha from "@Sqlm/userGacha.js"; import { exists, mkdir } from "@tauri-apps/plugin-fs"; @@ -16,7 +17,7 @@ import TGLogger from "./TGLogger.js"; /** * @description 备份用户数据 - * @since Beta v0.6.7 + * @since Beta v0.8.0 * @param {string} dir 备份目录路径 * @returns {Promise} */ @@ -33,13 +34,15 @@ export async function backUpUserData(dir: string): Promise { await TSUserAbyss.backupAbyss(dir); await showLoading.update("正在备份真境剧诗数据"); await TSUserCombat.backupCombat(dir); + await showLoading.update("正在备份幽境危战数据"); + await TSUserChallenge.backupChallenge(dir); await showLoading.update("正在备份UIGF祈愿数据"); await TSUserGacha.backUpUigf(dir); } /** * @description 恢复用户数据 - * @since Beta v0.6.7 + * @since Beta v0.8.0 * @param {string} dir 备份目录路径 * @returns {Promise} */ @@ -73,6 +76,12 @@ export async function restoreUserData(dir: string): Promise { showSnackbar.error("真境剧诗数据恢复失败"); errNum++; } + await showLoading.update("正在恢复幽境危战数据"); + const restoreChallenge = await TSUserChallenge.restoreChallenge(dir); + if (!restoreChallenge) { + showSnackbar.error("幽境危战数据恢复失败"); + errNum++; + } await showLoading.update("正在恢复祈愿数据"); const restoreGacha = await TSUserGacha.restoreUigf(dir); if (!restoreGacha) {