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

@@ -1,6 +1,7 @@
use crate::*;
use serde::Serialize;
use serde::de::DeserializeOwned;
use spacetimedb::AnonymousViewContext;
pub const VISUAL_NOVEL_SOURCE_IDEA: &str = "idea";
pub const VISUAL_NOVEL_SOURCE_DOCUMENT: &str = "document";
@@ -166,6 +167,58 @@ pub struct VisualNovelRuntimeEvent {
pub(crate) occurred_at: Timestamp,
}
/// 视觉小说公开广场列表投影。
///
/// 该 view 只暴露已发布作品卡片需要的公开字段HTTP gallery 订阅后
/// 从本地 cache 读取,避免每个列表请求调用 `list_visual_novel_works` procedure。
#[spacetimedb::view(accessor = visual_novel_gallery_view, public)]
pub fn visual_novel_gallery_view(ctx: &AnonymousViewContext) -> Vec<VisualNovelGalleryViewRow> {
let mut items = ctx
.db
.visual_novel_work_profile()
.by_visual_novel_work_publication_status()
.filter(VISUAL_NOVEL_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 VisualNovelGalleryViewRow {
pub work_id: String,
pub profile_id: String,
pub owner_user_id: String,
pub source_session_id: Option<String>,
pub author_display_name: String,
pub work_title: String,
pub work_description: String,
pub tags: Vec<String>,
pub cover_image_src: Option<String>,
pub source_asset_ids: Vec<String>,
pub publication_status: String,
pub publish_ready: bool,
pub play_count: u32,
pub created_at_micros: i64,
pub updated_at_micros: i64,
pub published_at_micros: Option<i64>,
}
#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)]
pub struct VisualNovelAgentSessionCreateInput {
pub session_id: String,
@@ -1445,6 +1498,31 @@ fn build_work_snapshot(row: &VisualNovelWorkProfileRow) -> Result<VisualNovelWor
})
}
fn build_gallery_view_row(
row: &VisualNovelWorkProfileRow,
) -> Result<VisualNovelGalleryViewRow, String> {
Ok(VisualNovelGalleryViewRow {
work_id: row.work_id.clone(),
profile_id: row.profile_id.clone(),
owner_user_id: row.owner_user_id.clone(),
source_session_id: empty_to_none(&row.source_session_id),
author_display_name: row.author_display_name.clone(),
work_title: row.work_title.clone(),
work_description: row.work_description.clone(),
tags: parse_string_vec_or_empty(&row.tags_json)?,
cover_image_src: empty_to_none(&row.cover_image_src),
source_asset_ids: parse_string_vec_or_empty(&row.source_asset_ids_json)?,
publication_status: row.publication_status.clone(),
publish_ready: row.publish_ready,
play_count: row.play_count,
created_at_micros: row.created_at.to_micros_since_unix_epoch(),
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 build_run_snapshot(
ctx: &ReducerContext,
row: &VisualNovelRuntimeRunRow,