perf: cache public gallery views
This commit is contained in:
@@ -19,6 +19,62 @@ use module_match3d::{
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_json::Value;
|
||||
use spacetimedb::AnonymousViewContext;
|
||||
|
||||
/// 抓大鹅公开广场列表投影。
|
||||
///
|
||||
/// `match3d_work_profile` 是玩法源表,HTTP gallery 只订阅这个轻量 view,
|
||||
/// 避免每个公开列表请求重新调用 procedure 扫描和组装全量列表。
|
||||
#[spacetimedb::view(accessor = match3d_gallery_view, public)]
|
||||
pub fn match3d_gallery_view(ctx: &AnonymousViewContext) -> Vec<Match3DGalleryViewRow> {
|
||||
let mut items = ctx
|
||||
.db
|
||||
.match3d_work_profile()
|
||||
.by_match3d_work_publication_status()
|
||||
.filter(MATCH3D_PUBLICATION_PUBLISHED)
|
||||
.filter_map(|row| match build_gallery_view_row(&row) {
|
||||
Ok(item) => Some(item),
|
||||
Err(error) => {
|
||||
log::warn!(
|
||||
"抓大鹅公开广场 view 跳过损坏的作品投影 profile_id={}: {}",
|
||||
row.profile_id,
|
||||
error
|
||||
);
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
items.sort_by(|left, right| {
|
||||
right
|
||||
.updated_at_micros
|
||||
.cmp(&left.updated_at_micros)
|
||||
.then_with(|| left.profile_id.cmp(&right.profile_id))
|
||||
});
|
||||
items
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)]
|
||||
pub struct Match3DGalleryViewRow {
|
||||
pub profile_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub source_session_id: String,
|
||||
pub author_display_name: String,
|
||||
pub game_name: String,
|
||||
pub theme_text: String,
|
||||
pub summary_text: String,
|
||||
pub tags: Vec<String>,
|
||||
pub cover_image_src: String,
|
||||
pub cover_asset_id: String,
|
||||
pub reference_image_src: Option<String>,
|
||||
pub clear_count: u32,
|
||||
pub difficulty: u32,
|
||||
pub publication_status: String,
|
||||
pub publish_ready: bool,
|
||||
pub play_count: u32,
|
||||
pub updated_at_micros: i64,
|
||||
pub published_at_micros: Option<i64>,
|
||||
pub generated_item_assets_json: Option<String>,
|
||||
}
|
||||
|
||||
#[spacetimedb::procedure]
|
||||
pub fn create_match3d_agent_session(
|
||||
@@ -1004,6 +1060,35 @@ fn build_work_snapshot(row: &Match3DWorkProfileRow) -> Result<Match3DWorkSnapsho
|
||||
})
|
||||
}
|
||||
|
||||
fn build_gallery_view_row(row: &Match3DWorkProfileRow) -> Result<Match3DGalleryViewRow, String> {
|
||||
let config = parse_config(&row.config_json)?;
|
||||
Ok(Match3DGalleryViewRow {
|
||||
profile_id: row.profile_id.clone(),
|
||||
owner_user_id: row.owner_user_id.clone(),
|
||||
source_session_id: row.source_session_id.clone(),
|
||||
author_display_name: row.author_display_name.clone(),
|
||||
game_name: row.game_name.clone(),
|
||||
theme_text: row.theme_text.clone(),
|
||||
summary_text: row.summary_text.clone(),
|
||||
tags: parse_tags(&row.tags_json)?,
|
||||
cover_image_src: row.cover_image_src.clone(),
|
||||
cover_asset_id: row.cover_asset_id.clone(),
|
||||
reference_image_src: config.reference_image_src,
|
||||
clear_count: row.clear_count,
|
||||
difficulty: row.difficulty,
|
||||
publication_status: row.publication_status.clone(),
|
||||
publish_ready: is_work_publish_ready(row),
|
||||
play_count: row.play_count,
|
||||
updated_at_micros: row.updated_at.to_micros_since_unix_epoch(),
|
||||
published_at_micros: row
|
||||
.published_at
|
||||
.map(|value| value.to_micros_since_unix_epoch()),
|
||||
generated_item_assets_json: normalize_generated_item_assets_json(
|
||||
row.generated_item_assets_json.as_deref(),
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn build_initial_run_snapshot(
|
||||
run_id: &str,
|
||||
work: &Match3DWorkProfileRow,
|
||||
@@ -1908,8 +1993,7 @@ mod tests {
|
||||
};
|
||||
|
||||
let row_json = to_json_string(&draft);
|
||||
let restored =
|
||||
parse_json::<Match3DDraftSnapshot>(&row_json, "match3d draft_json").unwrap();
|
||||
let restored = parse_json::<Match3DDraftSnapshot>(&row_json, "match3d draft_json").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
restored.generated_item_assets_json.as_deref(),
|
||||
|
||||
Reference in New Issue
Block a user