perf: cache public gallery views
This commit is contained in:
@@ -8,9 +8,42 @@ use crate::runtime::{
|
||||
};
|
||||
use crate::*;
|
||||
use module_big_fish::{EvaluateBigFishPublishReadinessCommand, evaluate_publish_readiness};
|
||||
use spacetimedb::AnonymousViewContext;
|
||||
|
||||
const INITIAL_BIG_FISH_CREATION_PROGRESS_PERCENT: u32 = 0;
|
||||
|
||||
/// 大鱼吃小鱼公开广场列表投影。
|
||||
///
|
||||
/// 公开列表从已发布 creation session 生成卡片字段;7 日播放数由
|
||||
/// `api-server` 订阅 `public_work_play_daily_stat` 后在本地聚合。
|
||||
#[spacetimedb::view(accessor = big_fish_gallery_view, public)]
|
||||
pub fn big_fish_gallery_view(ctx: &AnonymousViewContext) -> Vec<BigFishWorkSummarySnapshot> {
|
||||
let mut items = ctx
|
||||
.db
|
||||
.big_fish_creation_session()
|
||||
.by_big_fish_session_stage()
|
||||
.filter(BigFishCreationStage::Published)
|
||||
.filter_map(|row| match build_big_fish_gallery_view_row(ctx, &row) {
|
||||
Ok(snapshot) => Some(snapshot),
|
||||
Err(error) => {
|
||||
log::warn!(
|
||||
"大鱼吃小鱼公开广场 view 跳过损坏的作品投影 session_id={}: {}",
|
||||
row.session_id,
|
||||
error
|
||||
);
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
items.sort_by(|left, right| {
|
||||
right
|
||||
.updated_at_micros
|
||||
.cmp(&left.updated_at_micros)
|
||||
.then_with(|| left.source_session_id.cmp(&right.source_session_id))
|
||||
});
|
||||
items
|
||||
}
|
||||
|
||||
#[spacetimedb::procedure]
|
||||
pub fn create_big_fish_session(
|
||||
ctx: &mut ProcedureContext,
|
||||
@@ -988,6 +1021,16 @@ pub(crate) fn build_big_fish_work_summary(
|
||||
ctx: &ReducerContext,
|
||||
row: &BigFishCreationSession,
|
||||
now_micros: i64,
|
||||
) -> Result<BigFishWorkSummarySnapshot, String> {
|
||||
let mut summary = build_big_fish_work_summary_without_recent_count(ctx, row)?;
|
||||
summary.recent_play_count_7d =
|
||||
count_recent_public_work_plays(ctx, "big-fish", &row.session_id, now_micros);
|
||||
Ok(summary)
|
||||
}
|
||||
|
||||
fn build_big_fish_work_summary_without_recent_count(
|
||||
ctx: &ReducerContext,
|
||||
row: &BigFishCreationSession,
|
||||
) -> Result<BigFishWorkSummarySnapshot, String> {
|
||||
let draft = row
|
||||
.draft_json
|
||||
@@ -1052,12 +1095,7 @@ pub(crate) fn build_big_fish_work_summary(
|
||||
play_count: row.play_count,
|
||||
remix_count: row.remix_count,
|
||||
like_count: row.like_count,
|
||||
recent_play_count_7d: count_recent_public_work_plays(
|
||||
ctx,
|
||||
"big-fish",
|
||||
&row.session_id,
|
||||
now_micros,
|
||||
),
|
||||
recent_play_count_7d: 0,
|
||||
published_at_micros: row
|
||||
.published_at
|
||||
.or_else(|| (row.stage == BigFishCreationStage::Published).then_some(row.updated_at))
|
||||
@@ -1065,6 +1103,113 @@ pub(crate) fn build_big_fish_work_summary(
|
||||
})
|
||||
}
|
||||
|
||||
fn build_big_fish_gallery_view_row(
|
||||
ctx: &AnonymousViewContext,
|
||||
row: &BigFishCreationSession,
|
||||
) -> Result<BigFishWorkSummarySnapshot, String> {
|
||||
let draft = row
|
||||
.draft_json
|
||||
.as_deref()
|
||||
.map(deserialize_draft)
|
||||
.transpose()
|
||||
.map_err(|error| format!("big_fish.draft_json 非法: {error}"))?;
|
||||
let asset_slots = list_big_fish_asset_slots_for_view(ctx, &row.session_id);
|
||||
let coverage = build_asset_coverage(draft.as_ref(), &asset_slots);
|
||||
let cover_image_src = asset_slots
|
||||
.iter()
|
||||
.find(|slot| slot.asset_kind == BigFishAssetKind::StageBackground)
|
||||
.and_then(|slot| slot.asset_url.clone())
|
||||
.or_else(|| {
|
||||
asset_slots
|
||||
.iter()
|
||||
.find(|slot| slot.asset_kind == BigFishAssetKind::LevelMainImage)
|
||||
.and_then(|slot| slot.asset_url.clone())
|
||||
});
|
||||
let title = draft
|
||||
.as_ref()
|
||||
.map(|value| value.title.clone())
|
||||
.filter(|value| !value.trim().is_empty())
|
||||
.unwrap_or_else(|| "未命名大鱼草稿".to_string());
|
||||
let subtitle = draft
|
||||
.as_ref()
|
||||
.map(|value| value.subtitle.clone())
|
||||
.filter(|value| !value.trim().is_empty())
|
||||
.unwrap_or_else(|| "等待整理玩法草稿".to_string());
|
||||
let summary = draft
|
||||
.as_ref()
|
||||
.map(|value| value.core_fun.clone())
|
||||
.filter(|value| !value.trim().is_empty())
|
||||
.unwrap_or_else(|| {
|
||||
row.last_assistant_reply
|
||||
.clone()
|
||||
.unwrap_or_else(|| "继续补齐锚点后即可生成玩法草稿。".to_string())
|
||||
});
|
||||
|
||||
Ok(BigFishWorkSummarySnapshot {
|
||||
work_id: format!("big-fish-work-{}", row.session_id),
|
||||
source_session_id: row.session_id.clone(),
|
||||
owner_user_id: row.owner_user_id.clone(),
|
||||
title,
|
||||
subtitle,
|
||||
summary,
|
||||
cover_image_src,
|
||||
status: if row.stage == BigFishCreationStage::Published {
|
||||
"published".to_string()
|
||||
} else {
|
||||
"draft".to_string()
|
||||
},
|
||||
updated_at_micros: row.updated_at.to_micros_since_unix_epoch(),
|
||||
publish_ready: coverage.publish_ready,
|
||||
level_count: draft
|
||||
.as_ref()
|
||||
.map(|value| value.runtime_params.level_count)
|
||||
.unwrap_or(BIG_FISH_DEFAULT_LEVEL_COUNT),
|
||||
level_main_image_ready_count: coverage.level_main_image_ready_count,
|
||||
level_motion_ready_count: coverage.level_motion_ready_count,
|
||||
background_ready: coverage.background_ready,
|
||||
play_count: row.play_count,
|
||||
remix_count: row.remix_count,
|
||||
like_count: row.like_count,
|
||||
recent_play_count_7d: 0,
|
||||
published_at_micros: row
|
||||
.published_at
|
||||
.or_else(|| (row.stage == BigFishCreationStage::Published).then_some(row.updated_at))
|
||||
.map(|value| value.to_micros_since_unix_epoch()),
|
||||
})
|
||||
}
|
||||
|
||||
fn list_big_fish_asset_slots_for_view(
|
||||
ctx: &AnonymousViewContext,
|
||||
session_id: &str,
|
||||
) -> Vec<BigFishAssetSlotSnapshot> {
|
||||
let mut slots = ctx
|
||||
.db
|
||||
.big_fish_asset_slot()
|
||||
.by_big_fish_asset_session_id()
|
||||
.filter(session_id)
|
||||
.map(|slot| BigFishAssetSlotSnapshot {
|
||||
slot_id: slot.slot_id,
|
||||
session_id: slot.session_id,
|
||||
asset_kind: slot.asset_kind,
|
||||
level: slot.level,
|
||||
motion_key: slot.motion_key,
|
||||
status: slot.status,
|
||||
asset_url: slot.asset_url,
|
||||
prompt_snapshot: slot.prompt_snapshot,
|
||||
updated_at_micros: slot.updated_at.to_micros_since_unix_epoch(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
slots.sort_by_key(|slot| {
|
||||
(
|
||||
slot.level.unwrap_or(0),
|
||||
slot.asset_kind.as_str().to_string(),
|
||||
slot.motion_key.clone().unwrap_or_default(),
|
||||
slot.slot_id.clone(),
|
||||
)
|
||||
});
|
||||
slots
|
||||
}
|
||||
|
||||
fn build_public_big_fish_gallery_list_input() -> BigFishWorksListInput {
|
||||
BigFishWorksListInput {
|
||||
// 中文注释:published_only 分支不会按 owner 过滤;非空占位用于兼容旧部署模块的前置校验。
|
||||
|
||||
@@ -2,7 +2,8 @@ use crate::*;
|
||||
|
||||
#[spacetimedb::table(
|
||||
accessor = big_fish_creation_session,
|
||||
index(accessor = by_big_fish_session_owner_user_id, btree(columns = [owner_user_id]))
|
||||
index(accessor = by_big_fish_session_owner_user_id, btree(columns = [owner_user_id])),
|
||||
index(accessor = by_big_fish_session_stage, btree(columns = [stage]))
|
||||
)]
|
||||
pub struct BigFishCreationSession {
|
||||
#[primary_key]
|
||||
|
||||
Reference in New Issue
Block a user