perf: cache public gallery views

This commit is contained in:
kdletters
2026-05-17 01:19:12 +08:00
parent d9c8473504
commit 81fe3dcf28
39 changed files with 2124 additions and 298 deletions

View File

@@ -26,6 +26,65 @@ use module_square_hole::{
};
use serde::Serialize;
use serde::de::DeserializeOwned;
use spacetimedb::AnonymousViewContext;
/// 方洞挑战公开广场列表投影。
///
/// HTTP gallery 通过 `spacetime-client` 订阅该 view 后读本地 cache
/// 不再在每个公开列表请求里调用 `list_square_hole_works` procedure。
#[spacetimedb::view(accessor = square_hole_gallery_view, public)]
pub fn square_hole_gallery_view(ctx: &AnonymousViewContext) -> Vec<SquareHoleGalleryViewRow> {
let mut items = ctx
.db
.square_hole_work_profile()
.by_square_hole_work_publication_status()
.filter(SQUARE_HOLE_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 SquareHoleGalleryViewRow {
pub work_id: String,
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 twist_rule: String,
pub summary_text: String,
pub tags: Vec<String>,
pub cover_image_src: String,
pub background_prompt: String,
pub background_image_src: String,
pub shape_options: Vec<SquareHoleShapeOptionSnapshot>,
pub hole_options: Vec<SquareHoleHoleOptionSnapshot>,
pub shape_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>,
}
#[spacetimedb::procedure]
pub fn create_square_hole_agent_session(
@@ -880,6 +939,38 @@ fn build_work_snapshot(row: &SquareHoleWorkProfileRow) -> Result<SquareHoleWorkS
})
}
fn build_gallery_view_row(
row: &SquareHoleWorkProfileRow,
) -> Result<SquareHoleGalleryViewRow, String> {
let config = parse_config(&row.config_json)?;
Ok(SquareHoleGalleryViewRow {
work_id: row.work_id.clone(),
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(),
twist_rule: row.twist_rule.clone(),
summary_text: row.summary_text.clone(),
tags: parse_tags(&row.tags_json)?,
cover_image_src: row.cover_image_src.clone(),
background_prompt: config.background_prompt,
background_image_src: config.background_image_src,
shape_options: config.shape_options,
hole_options: config.hole_options,
shape_count: row.shape_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()),
})
}
fn refresh_run_row(
ctx: &ReducerContext,
row: SquareHoleRuntimeRunRow,

View File

@@ -222,7 +222,7 @@ pub struct SquareHoleCreatorConfigSnapshot {
pub background_image_src: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SquareHoleShapeOptionSnapshot {
pub option_id: String,
@@ -235,7 +235,7 @@ pub struct SquareHoleShapeOptionSnapshot {
pub image_src: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SquareHoleHoleOptionSnapshot {
pub hole_id: String,