媒体公开读授权按 objectKey 反解 gameId 改 O(1) 查找
- 从 GAME_DISTRIBUTION_MEDIA_PREFIX 后的首段反解 game_id,按主键读那一行核对媒体归属 - 不再逐行扫描全部游戏及其 screenshots_json - 补充 key 布局(非本前缀、缺 gameId、缺文件名、多级路径)的单测
This commit is contained in:
@@ -4208,23 +4208,48 @@ fn game_distribution_game_is_publicly_visible(game: &GameDistributionGame) -> bo
|
||||
&& game.deleted_at.is_none()
|
||||
}
|
||||
|
||||
/// 从发布媒体 objectKey 里反解 game_id;布局不符(含非本前缀的 key)返回 `None`。
|
||||
///
|
||||
/// 媒体 key 由 api-server 按 `{prefix}{game_id}/{kind}-{uuid}.{ext}` 生成,这里只做字符串切分,
|
||||
/// 不信任调用方传入的任意 key。
|
||||
fn game_distribution_media_object_key_game_id(object_key: &str) -> Option<&str> {
|
||||
let rest = object_key
|
||||
.strip_prefix(shared_contracts::game_distribution::GAME_DISTRIBUTION_MEDIA_PREFIX)?;
|
||||
let (game_id, file_name) = rest.split_once('/')?;
|
||||
if game_id.is_empty() || file_name.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(game_id)
|
||||
}
|
||||
|
||||
/// 已公开游戏的封面/截图 OSS objectKey 允许匿名读者换取签名读地址。
|
||||
///
|
||||
/// 媒体 key 自带 game_id,直接按主键读那一行再核对 key 是否属于它的封面/截图,避免逐行扫描全部游戏。
|
||||
pub(crate) fn game_distribution_media_has_public_read_grant(
|
||||
ctx: &ReducerContext,
|
||||
object_key: &str,
|
||||
) -> bool {
|
||||
ctx.db.game_distribution_game().iter().any(|game| {
|
||||
if !game_distribution_game_is_publicly_visible(&game) {
|
||||
return false;
|
||||
}
|
||||
if game.cover_object_key.as_deref() == Some(object_key) {
|
||||
return true;
|
||||
}
|
||||
game.screenshots_json
|
||||
.as_deref()
|
||||
.and_then(|json| serde_json::from_str::<Vec<String>>(json).ok())
|
||||
.is_some_and(|screenshots| screenshots.iter().any(|key| key == object_key))
|
||||
})
|
||||
let Some(game_id) = game_distribution_media_object_key_game_id(object_key) else {
|
||||
return false;
|
||||
};
|
||||
let Some(game) = ctx
|
||||
.db
|
||||
.game_distribution_game()
|
||||
.game_id()
|
||||
.find(&game_id.to_string())
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
if !game_distribution_game_is_publicly_visible(&game) {
|
||||
return false;
|
||||
}
|
||||
if game.cover_object_key.as_deref() == Some(object_key) {
|
||||
return true;
|
||||
}
|
||||
game.screenshots_json
|
||||
.as_deref()
|
||||
.and_then(|json| serde_json::from_str::<Vec<String>>(json).ok())
|
||||
.is_some_and(|screenshots| screenshots.iter().any(|key| key == object_key))
|
||||
}
|
||||
|
||||
fn game_distribution_version_snapshot(
|
||||
@@ -4488,4 +4513,33 @@ mod tests {
|
||||
apply_game_distribution_frozen_metadata(&mut game, &priced);
|
||||
assert_eq!(game.price_mud_points, 100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn media_object_key_game_id_is_derived_without_scanning_all_games() {
|
||||
const PREFIX: &str = shared_contracts::game_distribution::GAME_DISTRIBUTION_MEDIA_PREFIX;
|
||||
assert_eq!(
|
||||
game_distribution_media_object_key_game_id(&format!("{PREFIX}game_abc/cover-1.png")),
|
||||
Some("game_abc")
|
||||
);
|
||||
assert_eq!(
|
||||
game_distribution_media_object_key_game_id(&format!(
|
||||
"{PREFIX}game_abc/screenshot-2.webp"
|
||||
)),
|
||||
Some("game_abc")
|
||||
);
|
||||
// 非本前缀、缺 gameId、缺文件名、以及多级路径都不认。
|
||||
assert_eq!(
|
||||
game_distribution_media_object_key_game_id("generated/assets/cover.png"),
|
||||
None
|
||||
);
|
||||
assert_eq!(game_distribution_media_object_key_game_id(PREFIX), None);
|
||||
assert_eq!(
|
||||
game_distribution_media_object_key_game_id(&format!("{PREFIX}game_abc/")),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
game_distribution_media_object_key_game_id(&format!("{PREFIX}/cover-1.png")),
|
||||
None
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user