♻️ 重构战绩数据请求逻辑,完善错误处理,适配新版本地图数据

This commit is contained in:
BTMuli
2026-04-09 01:39:56 +08:00
parent 6d34b7cb65
commit 2e8ec962b6
7 changed files with 208 additions and 70 deletions

View File

@@ -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<boolean> {
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("正在更新角色列表");

View File

@@ -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<void> {
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;
}

View File

@@ -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<void> {
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();

View File

@@ -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<Record<number, StaticArea>> = {
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) {

View File

@@ -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<TGApp.Game.Record.FullData | TGApp.BBS.Response.Base> {
): Promise<TGApp.Game.Record.Resp> {
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<TGApp.Game.Record.Resp | TGApp.BBS.Response.Base>(`${trgAbu}index`, {
method: "GET",
const resp = await TGHttps.get<TGApp.Game.Record.Resp>(`${trgAbu}index`, {
headers: getRequestHeader(ck, "GET", params),
query: params,
});
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
return resp.data;
}

View File

@@ -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&center=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<AreaExploration>;
/** Boss 列表 */
boss_list: Array<unknown>;
boss_list: Array<AreaBoss>;
/** 是否热门 */
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;
};
/**

View File

@@ -125,9 +125,7 @@ async function request<T>(
} catch (error) {
// 清除超时定时器
clearTimeout(timeoutId);
let httpError: TGApp.App.Response.HttpErr;
if (typeof error === "object" && error !== null && "message" in error) {
httpError = <TGApp.App.Response.HttpErr>error;
} else if (error instanceof Error) {
@@ -135,8 +133,6 @@ async function request<T>(
} else {
httpError = createHttpError(String(error), { cause: error });
}
// 记录错误日志 TODO根据实际情况调整日志
// await TGLogger.Error(`[TGHttps] Request failed: ${httpError.message}`);
throw httpError;
}
}