Skip to content

Commit 3f70932

Browse files
committed
fix(kun): handle wrapped API responses
1 parent 580abd8 commit 3f70932

4 files changed

Lines changed: 82 additions & 47 deletions

File tree

src-tauri/src/database/repository/games_repository.rs

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ use crate::database::dto::{
77
BatchOperationError, BatchOperationResult, InsertGameData, UpdateGameData,
88
};
99
use crate::entity::prelude::*;
10-
use crate::entity::{
11-
bgm_data::BgmData, games, savedata, vndb_data::VndbData, ymgal_data::YmgalData,
12-
};
10+
use crate::entity::{games, savedata};
1311
use sea_orm::*;
1412
use serde::{Deserialize, Serialize};
1513
use std::collections::HashSet;
@@ -75,35 +73,48 @@ impl GamesRepository {
7573
.map(ToOwned::to_owned)
7674
}
7775

78-
fn resolve_source_date(
79-
bgm_data: Option<&BgmData>,
80-
vndb_data: Option<&VndbData>,
81-
ymgal_data: Option<&YmgalData>,
82-
) -> Option<String> {
83-
bgm_data
84-
.and_then(|data| Self::clean_source_date(data.date.as_deref()))
85-
.or_else(|| vndb_data.and_then(|data| Self::clean_source_date(data.date.as_deref())))
86-
.or_else(|| ymgal_data.and_then(|data| Self::clean_source_date(data.date.as_deref())))
76+
fn resolve_source_date<'a>(dates: impl IntoIterator<Item = Option<&'a str>>) -> Option<String> {
77+
dates.into_iter().find_map(Self::clean_source_date)
78+
}
79+
80+
fn pick_next_data<'a, T>(
81+
update: &'a Option<Option<T>>,
82+
current: &'a Option<T>,
83+
) -> Option<&'a T> {
84+
update.as_ref().map_or(current.as_ref(), Option::as_ref)
85+
}
86+
87+
fn has_source_data_update(updates: &UpdateGameData) -> bool {
88+
[
89+
updates.bgm_data.is_some(),
90+
updates.vndb_data.is_some(),
91+
updates.ymgal_data.is_some(),
92+
updates.kun_data.is_some(),
93+
]
94+
.into_iter()
95+
.any(|updated| updated)
8796
}
8897

8998
fn normalize_insert_date(mut game: InsertGameData) -> InsertGameData {
9099
if game.date.is_none() {
91-
game.date = Self::resolve_source_date(
92-
game.bgm_data.as_ref(),
93-
game.vndb_data.as_ref(),
94-
game.ymgal_data.as_ref(),
95-
);
100+
game.date = Self::resolve_source_date([
101+
game.bgm_data.as_ref().and_then(|data| data.date.as_deref()),
102+
game.vndb_data
103+
.as_ref()
104+
.and_then(|data| data.date.as_deref()),
105+
game.ymgal_data
106+
.as_ref()
107+
.and_then(|data| data.date.as_deref()),
108+
game.kun_data.as_ref().and_then(|data| data.date.as_deref()),
109+
]);
96110
}
97111

98112
game
99113
}
100114

101115
fn should_normalize_update_date(updates: &UpdateGameData) -> bool {
102116
matches!(updates.date, Some(None))
103-
|| (updates.date.is_none()
104-
&& (updates.bgm_data.is_some()
105-
|| updates.vndb_data.is_some()
106-
|| updates.ymgal_data.is_some()))
117+
|| (updates.date.is_none() && Self::has_source_data_update(updates))
107118
}
108119

109120
async fn normalize_update_date<C>(
@@ -123,24 +134,16 @@ impl GamesRepository {
123134
.await?
124135
.ok_or_else(|| DbErr::RecordNotFound(format!("game {} not found", game_id)))?;
125136

126-
let next_bgm_data = match &updates.bgm_data {
127-
Some(data) => data.as_ref(),
128-
None => current.bgm_data.as_ref(),
129-
};
130-
let next_vndb_data = match &updates.vndb_data {
131-
Some(data) => data.as_ref(),
132-
None => current.vndb_data.as_ref(),
133-
};
134-
let next_ymgal_data = match &updates.ymgal_data {
135-
Some(data) => data.as_ref(),
136-
None => current.ymgal_data.as_ref(),
137-
};
138-
139-
updates.date = Some(Self::resolve_source_date(
140-
next_bgm_data,
141-
next_vndb_data,
142-
next_ymgal_data,
143-
));
137+
updates.date = Some(Self::resolve_source_date([
138+
Self::pick_next_data(&updates.bgm_data, &current.bgm_data)
139+
.and_then(|data| data.date.as_deref()),
140+
Self::pick_next_data(&updates.vndb_data, &current.vndb_data)
141+
.and_then(|data| data.date.as_deref()),
142+
Self::pick_next_data(&updates.ymgal_data, &current.ymgal_data)
143+
.and_then(|data| data.date.as_deref()),
144+
Self::pick_next_data(&updates.kun_data, &current.kun_data)
145+
.and_then(|data| data.date.as_deref()),
146+
]));
144147

145148
Ok(updates)
146149
}

src-tauri/src/entity/kun_data.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,24 @@ use serde::{Deserialize, Serialize};
88

99
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, FromJsonQueryResult)]
1010
pub struct KunData {
11+
#[serde(skip_serializing_if = "Option::is_none")]
1112
pub image: Option<String>,
13+
#[serde(skip_serializing_if = "Option::is_none")]
1214
pub name: Option<String>,
15+
#[serde(skip_serializing_if = "Option::is_none")]
1316
pub name_cn: Option<String>,
17+
#[serde(skip_serializing_if = "Option::is_none")]
1418
pub all_titles: Option<Vec<String>>,
19+
#[serde(skip_serializing_if = "Option::is_none")]
1520
pub aliases: Option<Vec<String>>,
21+
#[serde(skip_serializing_if = "Option::is_none")]
1622
pub summary: Option<String>,
23+
#[serde(skip_serializing_if = "Option::is_none")]
1724
pub tags: Option<Vec<String>>,
25+
#[serde(skip_serializing_if = "Option::is_none")]
1826
pub developer: Option<String>,
27+
#[serde(skip_serializing_if = "Option::is_none")]
1928
pub nsfw: Option<bool>,
29+
#[serde(skip_serializing_if = "Option::is_none")]
30+
pub date: Option<String>,
2031
}

src/api/kun.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ export interface GalgameOfficialItem {
4747

4848
export interface GalgameDetailResponse {
4949
id: number;
50-
code: number;
5150
vndbId?: string;
5251
name: Partial<KunLanguage>;
5352
banner?: string;
@@ -58,12 +57,25 @@ export interface GalgameDetailResponse {
5857
alias?: string[];
5958
official?: GalgameOfficialItem[];
6059
tag?: GalgameDetailTag[];
60+
releaseDate?: string | null;
6161
}
6262

6363
export interface SearchResultGalgame {
6464
id: number;
6565
name: KunLanguage;
6666
banner: string;
67+
releaseDate?: string | null;
68+
}
69+
70+
export interface KunApiResponse<T> {
71+
code: number;
72+
message: string;
73+
data?: T;
74+
}
75+
76+
export interface KunPaginatedData<T> {
77+
items: T[];
78+
total: number;
6779
}
6880

6981
type KunLocaleKey = keyof KunLanguage;
@@ -210,6 +222,7 @@ const transformKunData = (
210222
tags: kunData.vndbId ? undefined : extractKunTags(kunData.tag),
211223
developer: extractDeveloper(kunData.official),
212224
nsfw: computeNsfw(kunData),
225+
date: kunData.releaseDate ?? undefined,
213226
};
214227

215228
const result: GameCandidateData = {
@@ -237,7 +250,7 @@ export async function fetchGalgameById(
237250
const { enrichVndb = true, signal } = options;
238251
const url = `${KUN_API_BASE}/galgame/${id}`;
239252

240-
const resp = await http.get<GalgameDetailResponse>(
253+
const resp = await http.get<KunApiResponse<GalgameDetailResponse>>(
241254
url,
242255
buildKunRateLimitedOptions({
243256
params: {
@@ -247,14 +260,16 @@ export async function fetchGalgameById(
247260
}),
248261
);
249262

250-
if (!resp.data || resp.data.code === 233) {
263+
const kunData = resp.data?.data;
264+
265+
if (!kunData || resp.data.code === 233) {
251266
throw new AppError({
252267
code: "metadata_not_found",
253268
message: `Kungal game not found: ${id}`,
254269
});
255270
}
256271

257-
const kunResult = transformKunData(resp.data);
272+
const kunResult = transformKunData(kunData);
258273

259274
if (!enrichVndb || !kunResult.vndb_id) {
260275
return kunResult;
@@ -281,7 +296,7 @@ export async function fetchGalgameById(
281296
kun_data: {
282297
...kunResult.kun_data,
283298
// VNDB 不可用时,保留鲲源自身 tags,避免 kun 源整体失效。
284-
tags: extractKunTags(resp.data.tag),
299+
tags: extractKunTags(kunData.tag),
285300
},
286301
};
287302
}
@@ -302,7 +317,9 @@ export async function searchGalgame(
302317
fetchDetailById = false,
303318
options: KunFetchOptions = {},
304319
): Promise<GameCandidateData[]> {
305-
const resp = await http.get<SearchResultGalgame[]>(
320+
const resp = await http.get<
321+
KunApiResponse<KunPaginatedData<SearchResultGalgame>>
322+
>(
306323
`${KUN_API_BASE}/search`,
307324
buildKunRateLimitedOptions({
308325
params: {
@@ -315,19 +332,22 @@ export async function searchGalgame(
315332
}),
316333
);
317334

318-
if (!Array.isArray(resp.data)) {
335+
const items = resp.data?.data?.items;
336+
337+
if (!Array.isArray(items)) {
319338
throw new AppError({
320339
code: "metadata_not_found",
321340
message: `Kungal search failed for: ${keywords}`,
322341
});
323342
}
324343

325-
const results = resp.data.map((item) => ({
344+
const results = items.map((item) => ({
326345
kun_id: String(item.id),
327346
id_type: "kun",
328347
kun_data: {
329348
name: pickLocalizedText(item.name),
330349
image: item.banner,
350+
date: item.releaseDate ?? undefined,
331351
},
332352
}));
333353

src/types/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ export interface KunData {
129129
tags?: string[];
130130
developer?: string;
131131
nsfw?: boolean;
132+
date?: string;
132133
}
133134

134135
/**

0 commit comments

Comments
 (0)