diff --git a/src/pages/User/Abyss.vue b/src/pages/User/Abyss.vue index cbcc9382..55097073 100644 --- a/src/pages/User/Abyss.vue +++ b/src/pages/User/Abyss.vue @@ -144,6 +144,7 @@ import { generateShareImg } from "@utils/TGShare.js"; import { storeToRefs } from "pinia"; import { onMounted, ref, shallowRef, watch } from "vue"; import { useRouter } from "vue-router"; +import TGHttps from "@utils/TGHttps.js"; const router = useRouter(); const hutaoStore = useHutaoStore(); @@ -426,12 +427,25 @@ async function refreshAvatars( ac: TGApp.Sqlite.Account.Game, ): Promise { await showLoading.update("正在更新角色数据"); - const idxRes = await recordReq.index(ck, ac, 1); - if ("retcode" in idxRes) { - await showLoading.update("角色更新失败"); - showSnackbar.error(`[${idxRes.retcode}] ${idxRes.message}`); - await TGLogger.Error(JSON.stringify(idxRes)); - await showLoading.end(); + // 更新战绩数据 + let indexResp: TGApp.Game.Record.Resp | undefined; + try { + indexResp = await recordReq.index(ck, ac, 1); + if (indexResp.retcode !== 0) { + await showLoading.update("角色更新失败"); + showSnackbar.error(`[${indexResp.retcode}] ${indexResp.message}`); + await TGLogger.Warn(`[Abyss][refreshAvatars] ${indexResp.retcode}-${indexResp.message}`); + await showLoading.end(); + return false; + } + } catch (e) { + let errMsg = String(e); + if (TGHttps.isHttpErr(e)) { + errMsg = e.status ? `[${e.status}] ${e.statusText}` : e.message; + } + showSnackbar.error(`获取战绩数据异常: ${errMsg}`); + await TGLogger.Error(`[Record][refreshRecord] 获取战绩异常`); + await TGLogger.Error(`${e}`); return false; } await showLoading.update("正在更新角色列表"); diff --git a/src/pages/User/Characters.vue b/src/pages/User/Characters.vue index c0491762..41e70e0f 100644 --- a/src/pages/User/Characters.vue +++ b/src/pages/User/Characters.vue @@ -175,6 +175,7 @@ import { storeToRefs } from "pinia"; import { computed, onMounted, ref, shallowRef, triggerRef, watch } from "vue"; import { AppCharacterData } from "@/data/index.js"; +import TGHttps from "@utils/TGHttps.js"; type TabItem = { label: string; value: string }; type OverviewItem = { element: string; cnt: number; label: string }; @@ -420,13 +421,28 @@ async function refresh(): Promise { await TGLogger.Info(`[Character][refresh][${rfAccount.gameUid}] 正在更新角色数据`); await showLoading.start(`正在更新${rfAccount.gameUid}的角色数据`); loadData.value = true; + // 刷新战绩数据 await showLoading.update("正在刷新首页数据"); - const indexRes = await recordReq.index(rfCk!, rfAccount, 1); - console.log(indexRes); - if ("retcode" in indexRes) { - showSnackbar.error(`[${indexRes.retcode}] ${indexRes.message}`); - await TGLogger.Error(JSON.stringify(indexRes)); + let indexResp: TGApp.Game.Record.Resp | undefined; + try { + indexResp = await recordReq.index(rfCk!, rfAccount, 1); + console.debug("recordIndexResp", indexResp); + if (indexResp.retcode !== 0) { + await showLoading.end(); + showSnackbar.error(`[${indexResp.retcode}] ${indexResp.message}`); + await TGLogger.Warn(`[Characters] ${indexResp.retcode}-${indexResp.message}`); + loadData.value = false; + return; + } + } catch (e) { await showLoading.end(); + let errMsg = String(e); + if (TGHttps.isHttpErr(e)) { + errMsg = e.status ? `[${e.status}] ${e.statusText}` : e.message; + } + showSnackbar.error(`获取战绩数据异常: ${errMsg}`); + await TGLogger.Error(`[Characters][refresh] 获取战绩异常`); + await TGLogger.Error(`${e}`); loadData.value = false; return; } diff --git a/src/pages/User/Record.vue b/src/pages/User/Record.vue index 243b9c02..71dca45f 100644 --- a/src/pages/User/Record.vue +++ b/src/pages/User/Record.vue @@ -89,6 +89,7 @@ import TGLogger from "@utils/TGLogger.js"; import { generateShareImg } from "@utils/TGShare.js"; import { storeToRefs } from "pinia"; import { onMounted, ref, shallowRef, watch } from "vue"; +import TGHttps from "@utils/TGHttps.js"; const userStore = useUserStore(); const { account, cookie } = storeToRefs(userStore); @@ -146,22 +147,37 @@ async function refreshRecord(): Promise { await showLoading.start(`正在刷新${rfAccount.gameUid}的战绩数据`); await TGLogger.Info(`[UserRecord][refresh][${rfAccount.gameUid}] 刷新战绩数据`); isRefresh.value = true; - const resp = await recordReq.index(rfCk!, rfAccount); - console.log(resp); - if ("retcode" in resp) { - await showLoading.end(); - showSnackbar.error(`[${resp.retcode}] ${resp.message}`); - await TGLogger.Error(`[UserRecord][refresh][${rfAccount.gameUid}] 获取战绩数据失败`); - await TGLogger.Error( - `[UserRecord][refresh][${rfAccount.gameUid}] ${resp.retcode} ${resp.message}`, - ); + // 刷新战绩数据 + let indexResp: TGApp.Game.Record.Resp | undefined; + try { + indexResp = await recordReq.index(rfCk!, rfAccount); + console.debug("recordIndexResp", indexResp); + if (indexResp.retcode !== 0) { + await showLoading.end(); + showSnackbar.error(`[${indexResp.retcode}] ${indexResp.message}`); + await TGLogger.Warn(`[UserRecord][refresh][${rfAccount.gameUid}] 获取战绩数据失败`); + await TGLogger.Warn( + `[UserRecord][refresh][${rfAccount.gameUid}] ${indexResp.retcode} ${indexResp.message}`, + ); + isRefresh.value = false; + return; + } + } catch (e) { + let errMsg = String(e); + if (TGHttps.isHttpErr(e)) { + errMsg = e.status ? `[${e.status}] ${e.statusText}` : e.message; + } + showSnackbar.error(`获取战绩数据异常: ${errMsg}`); + await TGLogger.Error(`[Record][refreshRecord] 获取战绩异常`); + await TGLogger.Error(`${e}`); isRefresh.value = false; return; } + if (!indexResp) return; await TGLogger.Info(`[UserRecord][refresh][${rfAccount.gameUid}] 获取战绩数据成功`); await TGLogger.Info(`[UserRecord][refresh][${rfAccount.gameUid}]`, false); await showLoading.update("正在保存战绩数据"); - await TSUserRecord.saveRecord(Number(rfAccount.gameUid), resp); + await TSUserRecord.saveRecord(Number(rfAccount.gameUid), indexResp.data); await showLoading.update("正在加载战绩数据"); await loadUid(rfAccount.gameUid); await loadRecord(); diff --git a/src/plugins/Sqlite/utils/transUserRecord.ts b/src/plugins/Sqlite/utils/transUserRecord.ts index 0a79f472..371b4986 100644 --- a/src/plugins/Sqlite/utils/transUserRecord.ts +++ b/src/plugins/Sqlite/utils/transUserRecord.ts @@ -1,11 +1,61 @@ /** * 原神战绩数据转换 - * @since Beta v0.9.9 + * @since Beta v0.10.0 */ import gameEnum from "@enum/game.js"; import { getZhElement } from "@utils/toolFunc.js"; +/** + * 地区特殊资源配置项 + * @since Beta v0.10.0 + */ +type StaticArea = { + /** 地区名称 */ + name: string; + /** 图标 */ + iconLight: string; + /** 背景 */ + bg: string; +}; + +/** + * 地区特殊资源配置列表 + * @since Beta v0.10.0 + */ +const STATIC_AREA: Readonly> = { + 15: { + name: "纳塔", + iconLight: + "https://webstatic.mihoyo.com/app/community-game-records/images/world-logo-15.fd274778.png", + bg: "https://fastcdn.mihoyo.com/static-resource-v2/2024/08/19/8856eafed39be791276a21a6d522426b_6903333123294722705.png", + }, + 16: { + name: "远古圣山", + iconLight: + "https://webstatic.mihoyo.com/app/community-game-records/images/world-logo-16.1c751ac9.png", + bg: "https://fastcdn.mihoyo.com/static-resource-v2/2025/03/17/8ee1648101a8b292ffb37eb49559032e_6583057448168798147.png", + }, + 17: { + name: "挪德卡莱", + iconLight: + "https://webstatic.mihoyo.com/app/community-game-records/images/world-logo-17.dadac5bf.png", + bg: "https://fastcdn.mihoyo.com/static-resource-v2/2025/08/22/ace66cea9c5074b70310ecbbb712cd94_2619077306700596372.png", + }, + 18: { + name: "风息山", + iconLight: + "https://webstatic.mihoyo.com/app/community-game-records/images/world-logo-1.20b81b5f.png", + bg: "https://fastcdn.mihoyo.com/static-resource-v2/2026/04/01/cf1d89b8701d81aee20a56675293d8bf_4405529076047143557.png", + }, + 19: { + name: "空之神殿", + iconLight: + "https://webstatic.mihoyo.com/app/community-game-records/images/world-logo-19.a9df3078.png", + bg: "https://fastcdn.mihoyo.com/static-resource-v2/2026/04/01/e2f71f00220851c475f3babe694b134e_7279058850569905239.png", + }, +}; + /** * 转换战绩口数据 * @since Beta v0.9.1 @@ -98,7 +148,7 @@ function transStat(data: TGApp.Game.Record.Stats): TGApp.Sqlite.Record.Stats { /** * 转换探索信息 - * @since Beta v0.9.1 + * @since Beta v0.10.0 * @param data - 城市探索信息 * @returns 转换后的城市探索信息 */ @@ -122,24 +172,9 @@ function transWorld( }; if (area.type === "Reputation") world.reputation = area.level; if (area.offerings !== undefined && area.offerings.length > 0) world.offerings = area.offerings; - // 对纳塔的特殊处理 - if (area.name === "纳塔") { - world.iconLight = - "https://webstatic.mihoyo.com/app/community-game-records/images/world-logo-15.fd274778.png"; - world.bg = - "https://fastcdn.mihoyo.com/static-resource-v2/2024/08/19/8856eafed39be791276a21a6d522426b_6903333123294722705.png"; - // 对远古圣山的特殊处理 - } else if (area.name === "远古圣山") { - world.iconLight = - "https://webstatic.mihoyo.com/app/community-game-records/images/world-logo-16.1c751ac9.png"; - world.bg = - "https://fastcdn.mihoyo.com/static-resource-v2/2025/03/17/8ee1648101a8b292ffb37eb49559032e_6583057448168798147.png"; - // 对挪德卡莱的特殊处理 - } else if (area.name === "挪德卡莱") { - world.iconLight = - "https://webstatic.mihoyo.com/app/community-game-records/images/world-logo-17.dadac5bf.png"; - world.bg = - "https://fastcdn.mihoyo.com/static-resource-v2/2025/08/22/ace66cea9c5074b70310ecbbb712cd94_2619077306700596372.png"; + if (area.id in STATIC_AREA && STATIC_AREA[area.id].name === area.name) { + world.iconLight = STATIC_AREA[area.id].iconLight; + world.bg = STATIC_AREA[area.id].bg; } const children = areaChild.filter((i) => i.parent_id === area.id); for (const child of children) { diff --git a/src/request/recordReq.ts b/src/request/recordReq.ts index 4ca658b3..f5b583d8 100644 --- a/src/request/recordReq.ts +++ b/src/request/recordReq.ts @@ -1,10 +1,11 @@ /** * TakumiRecordGenshinApi 相关请求 - * @since Beta v0.9.6 + * @since Beta v0.10.0 */ import gameEnum from "@enum/game.js"; import { getRequestHeader } from "@utils/getRequestHeader.js"; +import TGHttps from "@utils/TGHttps.js"; import TGHttp from "@utils/TGHttp.js"; // TakumiRecordGenshinApiBaseUrl => trgAbu @@ -57,7 +58,7 @@ async function characterList( /** * 获取首页信息 - * @since Beta v0.6.3 + * @since Beta v0.10.0 * @param cookie - Cookie * @param user - 用户 * @param listType - 列表类型 @@ -67,15 +68,13 @@ async function index( cookie: TGApp.App.Account.Cookie, user: TGApp.Sqlite.Account.Game, listType: number = 0, -): Promise { +): Promise { const ck = { account_id: cookie.account_id, cookie_token: cookie.cookie_token }; const params = { avatar_list_type: listType, role_id: user.gameUid, server: user.region }; - const resp = await TGHttp(`${trgAbu}index`, { - method: "GET", + const resp = await TGHttps.get(`${trgAbu}index`, { headers: getRequestHeader(ck, "GET", params), query: params, }); - if (resp.retcode !== 0) return resp; return resp.data; } diff --git a/src/types/Game/Record.d.ts b/src/types/Game/Record.d.ts index 34ed0bda..99c5e05c 100644 --- a/src/types/Game/Record.d.ts +++ b/src/types/Game/Record.d.ts @@ -1,6 +1,6 @@ /** * 原神战绩相关类型定义文件 - * @since Beta v0.9.9 + * @since Beta v0.10.0 */ declare namespace TGApp.Game.Record { @@ -13,7 +13,6 @@ declare namespace TGApp.Game.Record { /** * 原神战绩返回数据 - * * @since Beta v0.7.2 */ type FullData = { @@ -63,9 +62,12 @@ declare namespace TGApp.Game.Record { image: string; /** 角色名称 */ name: string; - /** 角色元素 */ + /** + * 角色元素 + * @example Dendro + */ element: string; - /** 羁绊等级 */ + /** 好感等级 */ fetter: number; /** 角色等级 */ level: number; @@ -106,7 +108,16 @@ declare namespace TGApp.Game.Record { electroculus_number: number; /** 精致宝箱数量 */ exquisite_chest_number: number; - /** 数据对应链接的 map,用不到设为 unknown */ + /** + * 数据对应链接的 map,用不到设为 unknown + * @example + * ```json + * "dendroculus_number": { + * "link": "https://act.mihoyo.com/ys/app/interactive-map/index.html?bbs_presentation_style=no_header&lang=zh-cn&utm_source=gamerecord&utm_medium=ys&utm_campaign=icon&_markerFps=24#/map/2?shown_types=403¢er=2008.50,-1084.00&zoom=-3.00", + * "backup_link": "" + * }, + * ``` + */ field_ext_map: unknown; /** 满好感角色数 */ full_fetter_avatar_num: number; @@ -136,7 +147,7 @@ declare namespace TGApp.Game.Record { /** * 幻想真境剧诗数据类型 - * @since Beta v0.5.0 + * @since Beta v0.10.0 */ type CombatStats = { /** 是否解锁 */ @@ -147,6 +158,10 @@ declare namespace TGApp.Game.Record { has_data: boolean; /** 是否有详细数据 */ has_detail_data: boolean; + /** 圣牌解锁数量 */ + tarot_finished_cnt: number; + /** 困难等级 */ + difficulty_id: TGApp.Game.Combat.DiffEnum; }; /** @@ -166,19 +181,28 @@ declare namespace TGApp.Game.Record { /** * 世界探索信息类型 - * @since Beta v0.8.1 + * @todo 类型更新 + * @since Beta v0.10.0 */ type WorldExplore = { /** 声望等级 */ level: number; /** 探索千分比 */ exploration_percentage: number; - /** 图标 */ + /** + * 图标 + * @remarks 可能为空 + */ icon: string; /** 名称 */ name: string; - /** 类型 - * @example Reputation: 声望, Offering: 奉献 + /** + * 类型 + * @todo 转为枚举类 + * @example + * - Reputation: 声望 + * - Offering: 奉献 + * - TypeUnknown: 未知类型 */ type: string; /** 奉献物品 */ @@ -191,16 +215,25 @@ declare namespace TGApp.Game.Record { map_url: string; /** 攻略 URL */ strategy_url: string; - /** 背景图片 URL */ + /** + * 背景图片 URL + * @remarks 可能为空 + */ background_image: string; - /** 内部图标 URL */ + /** + * 内部图标 URL + * @remarks 可能为空 + */ inner_icon: string; - /** 封面 URL */ + /** + * 封面 URL + * @remarks 可能为空 + */ cover: string; /** 区域探索列表 */ area_exploration_list: Array; /** Boss 列表 */ - boss_list: Array; + boss_list: Array; /** 是否热门 */ is_hot: boolean; /** 索引激活 */ @@ -209,7 +242,10 @@ declare namespace TGApp.Game.Record { detail_active: boolean; /** 七天神像等级 */ seven_status_level: number; - /** 纳塔声望 */ + /** + * 纳塔声望 + * @remarks 用于标识纳塔地区声望,其余地区该值为 null + */ nata_reputation: NataReputation | null; /** 世界类型 */ world_type: number; @@ -217,7 +253,7 @@ declare namespace TGApp.Game.Record { /** * 奉献物品类型 - * @since Alpha v0.2.0 + * @since Beta v0.10.0 */ type WorldOffering = { /** 名称 */ @@ -226,6 +262,14 @@ declare namespace TGApp.Game.Record { level: number; /** 图标 */ icon: string; + /** + * 开启状态 + * @todo 枚举 + * @example + * - OfferingOpenStateUnknow + * - OfferingOpenStateUnlocked + */ + open_state: string; }; /** @@ -239,6 +283,17 @@ declare namespace TGApp.Game.Record { exploration_percentage: number; }; + /** + * 区域Boss类型 + * @since Beta v0.10.0 + */ + type AreaBoss = { + /** 名称 */ + name: string; + /** 击杀数 */ + kill_num: number; + }; + /** * 纳塔声望类型 * @since Beta v0.7.2 @@ -250,12 +305,19 @@ declare namespace TGApp.Game.Record { /** * 部落列表类型 + * @since Beta v0.10.0 */ - type NataOffering = WorldOffering & { - /** ID */ - id: number; + type NataOffering = { + /** 图标 */ + icon: string; /** 图片 */ image: string; + /** 名称 */ + name: string; + /** ID */ + id: number; + /** 等级 */ + level: number; }; /** diff --git a/src/utils/TGHttps.ts b/src/utils/TGHttps.ts index ab468f23..be224dcc 100644 --- a/src/utils/TGHttps.ts +++ b/src/utils/TGHttps.ts @@ -125,9 +125,7 @@ async function request( } catch (error) { // 清除超时定时器 clearTimeout(timeoutId); - let httpError: TGApp.App.Response.HttpErr; - if (typeof error === "object" && error !== null && "message" in error) { httpError = error; } else if (error instanceof Error) { @@ -135,8 +133,6 @@ async function request( } else { httpError = createHttpError(String(error), { cause: error }); } - // 记录错误日志 TODO:根据实际情况调整日志 - // await TGLogger.Error(`[TGHttps] Request failed: ${httpError.message}`); throw httpError; } }