mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-04-22 21:59:49 +08:00
♻️ 重构战绩数据请求逻辑,完善错误处理,适配新版本地图数据
This commit is contained in:
@@ -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("正在更新角色列表");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
100
src/types/Game/Record.d.ts
vendored
100
src/types/Game/Record.d.ts
vendored
@@ -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<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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user