1+ /**
2+ * @file 混合数据获取 API 封装
3+ * @description 同时从 Bangumi 和 VNDB 获取游戏信息并合并数据
4+ * @module src/api/mixed
5+ * @author ReinaManager
6+ * @copyright AGPL-3.0
7+ * * 逻辑说明:
8+ * 1. 根据传入的 ID 类型智能选择获取策略:
9+ * - 只有 BGM ID:先获取 BGM 数据,再用其名称搜索 VNDB
10+ * - 只有 VNDB ID:先获取 VNDB 数据,再用其名称搜索 BGM
11+ * - 两个 ID 都有:并行获取两个数据源
12+ * - 没有 ID:用名称搜索,优先 BGM,失败则尝试 VNDB
13+ * 2. 使用安全模式避免单个数据源失败导致整体失败
14+ * 3. 合并数据时以 BGM 数据为主,VNDB 数据为补充
15+ * 4. 确保至少有一个数据源成功才返回结果
16+ *
17+ * 主要导出:
18+ * - fetchMixedData:混合获取游戏数据的主函数
19+ */
20+
121import { fetchFromBgm } from "./bgm" ;
222import { fetchFromVNDB } from "./vndb" ;
23+ import type { GameData } from "../types" ;
324
4- const fetchMixedData = async ( name : string , BGM_TOKEN : string , bgm_id ?: string , vndb_id ?: string ) => {
25+ const fetchMixedData = async (
26+ name : string ,
27+ BGM_TOKEN : string ,
28+ bgm_id ?: string ,
29+ vndb_id ?: string
30+ ) : Promise < GameData | string > => {
531 try {
6- let BGMdata = null ;
7- let VNDBdata = null ;
8-
9- if ( bgm_id ) {
10- BGMdata = await fetchFromBgm ( name , BGM_TOKEN , bgm_id ) ;
32+ let BGMdata : GameData | null = null ;
33+ let VNDBdata : GameData | null = null ;
34+
35+ // 根据传入的ID类型决定获取策略
36+ if ( bgm_id && ! vndb_id ) {
37+ // 只有 Bangumi ID:先获取 BGM 数据,再用 BGM 名称搜索 VNDB
38+ BGMdata = await getBangumiData ( name , BGM_TOKEN , bgm_id ) ;
39+ VNDBdata = await getVNDBDataSafely ( BGMdata . name ) ;
40+ } else if ( vndb_id && ! bgm_id ) {
41+ // 只有 VNDB ID:先获取 VNDB 数据,再用 VNDB 名称搜索 BGM
42+ VNDBdata = await getVNDBData ( name , vndb_id ) ;
43+ BGMdata = await getBangumiDataSafely ( VNDBdata . name , BGM_TOKEN ) ;
44+ } else if ( bgm_id && vndb_id ) {
45+ // 有两个 ID:直接分别获取
46+ const [ bgmResult , vndbResult ] = await Promise . allSettled ( [
47+ getBangumiData ( name , BGM_TOKEN , bgm_id ) ,
48+ getVNDBData ( name , vndb_id )
49+ ] ) ;
50+
51+ if ( bgmResult . status === 'fulfilled' ) BGMdata = bgmResult . value ;
52+ if ( vndbResult . status === 'fulfilled' ) VNDBdata = vndbResult . value ;
1153 } else {
12- BGMdata = await fetchFromBgm ( name , BGM_TOKEN ) ;
13- }
14-
15- if ( ! BGMdata || typeof BGMdata === "string" ) {
16- throw new Error ( `Bangumi 数据获取失败: ${ BGMdata } ` ) ;
54+ // 没有 ID:用名称搜索,优先 BGM
55+ BGMdata = await getBangumiDataSafely ( name , BGM_TOKEN ) ;
56+ if ( BGMdata ) {
57+ VNDBdata = await getVNDBDataSafely ( BGMdata . name ) ;
58+ } else {
59+ // 如果 BGM 搜索失败,尝试 VNDB
60+ VNDBdata = await getVNDBDataSafely ( name ) ;
61+ }
1762 }
1863
19- if ( vndb_id ) {
20- VNDBdata = await fetchFromVNDB ( BGMdata . name , vndb_id ) ;
21- } else {
22- VNDBdata = await fetchFromVNDB ( BGMdata . name ) ;
64+ // 确保至少有一个数据源成功
65+ if ( ! BGMdata && ! VNDBdata ) {
66+ throw new Error ( "无法从任何数据源获取游戏信息" ) ;
2367 }
68+
69+ // 合并数据
70+ return mergeData ( BGMdata , VNDBdata ) ;
71+
72+ } catch ( error ) {
73+ console . error ( "Mixed API 调用失败:" , error instanceof Error ? error . message : error ) ;
74+ return "获取数据失败,请稍后重试" ;
75+ }
76+ } ;
2477
25- if ( ! VNDBdata || typeof VNDBdata === "string" ) {
26- throw new Error ( `VNDB 数据获取失败: ${ VNDBdata } ` ) ;
27- }
78+ // 辅助函数:获取Bangumi数据(严格模式,失败时抛出异常)
79+ async function getBangumiData ( name : string , BGM_TOKEN : string , bgm_id ?: string ) : Promise < GameData > {
80+ const BGMdata = await fetchFromBgm ( name , BGM_TOKEN , bgm_id ) ;
81+
82+ if ( ! BGMdata || typeof BGMdata === "string" ) {
83+ throw new Error ( `Bangumi 数据获取失败: ${ BGMdata } ` ) ;
84+ }
85+
86+ return BGMdata ;
87+ }
2888
29- return {
30- bgm_id : BGMdata . bgm_id ,
31- vndb_id : VNDBdata . vndb_id ,
32- id_type : "mixed" ,
33- date : BGMdata . date || VNDBdata . date ,
34- image : BGMdata . image || VNDBdata . image ,
35- summary : BGMdata . summary || VNDBdata . summary ,
36- name : BGMdata . name || VNDBdata . name ,
37- name_cn : BGMdata . name_cn || VNDBdata . name_cn ,
38- all_titles : VNDBdata . all_titles || [ ] ,
39- tags : BGMdata . tags || VNDBdata . tags ,
40- rank : BGMdata . rank || undefined ,
41- score : BGMdata . score || VNDBdata . score ,
42- developer : BGMdata . developer || VNDBdata . developer ,
43- aveage_hours : VNDBdata . aveage_hours || undefined ,
44- } ;
45- } catch ( error ) {
46- Promise . reject ( new Error ( "Mixed API 调用失败" ) ) ;
47- if ( error instanceof Error ) {
48- console . error ( "错误消息:" , error . message ) ;
49- }
50- return "获取数据失败,请稍后重试" ;
89+ // 辅助函数:获取Bangumi数据(安全模式,失败时返回null)
90+ async function getBangumiDataSafely ( name : string , BGM_TOKEN : string , bgm_id ?: string ) : Promise < GameData | null > {
91+ try {
92+ return await getBangumiData ( name , BGM_TOKEN , bgm_id ) ;
93+ } catch {
94+ return null ;
95+ }
96+ }
97+
98+ // 辅助函数:获取VNDB数据(严格模式,失败时抛出异常)
99+ async function getVNDBData ( searchName : string , vndb_id ?: string ) : Promise < GameData > {
100+ const VNDBdata = await fetchFromVNDB ( searchName , vndb_id ) ;
101+
102+ if ( ! VNDBdata || typeof VNDBdata === "string" ) {
103+ throw new Error ( `VNDB 数据获取失败: ${ VNDBdata } ` ) ;
104+ }
105+
106+ return VNDBdata ;
107+ }
108+
109+ // 辅助函数:获取VNDB数据(安全模式,失败时返回null)
110+ async function getVNDBDataSafely ( searchName : string , vndb_id ?: string ) : Promise < GameData | null > {
111+ try {
112+ return await getVNDBData ( searchName , vndb_id ) ;
113+ } catch {
114+ return null ;
115+ }
116+ }
117+
118+ // 辅助函数:合并数据
119+ function mergeData ( BGMdata : GameData | null , VNDBdata : GameData | null ) : GameData {
120+ // 如果只有一个数据源,直接返回
121+ if ( ! BGMdata && VNDBdata ) return VNDBdata ;
122+ if ( BGMdata && ! VNDBdata ) return BGMdata ;
123+ if ( ! BGMdata && ! VNDBdata ) {
124+ throw new Error ( "没有可用的数据进行合并" ) ;
125+ }
126+
127+ // 两个数据源都有,进行合并(BGM 数据优先)
128+ return {
129+ bgm_id : BGMdata ?. bgm_id || null ,
130+ vndb_id : VNDBdata ?. vndb_id || null ,
131+ id_type : "mixed" ,
132+ date : BGMdata ?. date || VNDBdata ?. date ,
133+ image : BGMdata ?. image || VNDBdata ?. image ,
134+ summary : BGMdata ?. summary || VNDBdata ?. summary ,
135+ name : BGMdata ?. name || VNDBdata ?. name || '' ,
136+ name_cn : BGMdata ?. name_cn || VNDBdata ?. name_cn ,
137+ all_titles : VNDBdata ?. all_titles || BGMdata ?. all_titles || [ ] ,
138+ tags : BGMdata ?. tags || VNDBdata ?. tags ,
139+ rank : BGMdata ?. rank || undefined ,
140+ score : BGMdata ?. score || VNDBdata ?. score ,
141+ developer : BGMdata ?. developer || VNDBdata ?. developer ,
142+ aveage_hours : VNDBdata ?. aveage_hours || undefined ,
143+ } ;
51144}
52- } ;
53145
54146export default fetchMixedData ;
0 commit comments