optimize puzzle gallery access

This commit is contained in:
kdletters
2026-05-16 18:14:00 +08:00
parent 49ffa6b901
commit 7f16e88e57
7 changed files with 508 additions and 101 deletions

View File

@@ -31,7 +31,9 @@ use module_runtime::visible_runtime_profile_user_tags;
use serde_json::from_str as json_from_str;
use serde_json::json;
use serde_json::to_string as json_to_string;
use spacetimedb::{ProcedureContext, SpacetimeType, Table, Timestamp, TxContext};
use spacetimedb::{
AnonymousViewContext, ProcedureContext, SpacetimeType, Table, Timestamp, TxContext,
};
use crate::auth::user_account;
@@ -112,6 +114,33 @@ pub struct PuzzleWorkProfileRow {
point_incentive_claimed_points: u64,
}
/// 拼图广场公开列表投影。
///
/// `puzzle_work_profile` 是私有真相表HTTP gallery 只订阅这个 view
/// 避免每次请求回到 procedure 重新扫表、组装列表和跨层 JSON 往返。
#[spacetimedb::view(accessor = puzzle_gallery_view, public)]
pub fn puzzle_gallery_view(ctx: &AnonymousViewContext) -> Vec<PuzzleWorkProfile> {
let mut items = ctx
.db
.puzzle_work_profile()
.by_puzzle_work_publication_status()
.filter(PuzzlePublicationStatus::Published)
.filter_map(|row| match build_puzzle_work_profile_from_row_without_recent_count(&row) {
Ok(profile) => Some(profile),
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));
items
}
/// 拼图创作事件类型。
///
/// 事件表只广播跨层订阅需要的轻量事实,作品真相仍以
@@ -187,12 +216,12 @@ pub fn create_puzzle_agent_session(
match ctx.try_with_tx(|tx| create_puzzle_agent_session_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
session_json: Some(serialize_json(&session)),
session: Some(session),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
session_json: None,
session: None,
error_message: Some(message),
},
}
@@ -206,12 +235,12 @@ pub fn get_puzzle_agent_session(
match ctx.try_with_tx(|tx| get_puzzle_agent_session_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
session_json: Some(serialize_json(&session)),
session: Some(session),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
session_json: None,
session: None,
error_message: Some(message),
},
}
@@ -225,12 +254,12 @@ pub fn submit_puzzle_agent_message(
match ctx.try_with_tx(|tx| submit_puzzle_agent_message_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
session_json: Some(serialize_json(&session)),
session: Some(session),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
session_json: None,
session: None,
error_message: Some(message),
},
}
@@ -244,12 +273,12 @@ pub fn finalize_puzzle_agent_message_turn(
match ctx.try_with_tx(|tx| finalize_puzzle_agent_message_turn_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
session_json: Some(serialize_json(&session)),
session: Some(session),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
session_json: None,
session: None,
error_message: Some(message),
},
}
@@ -263,12 +292,12 @@ pub fn compile_puzzle_agent_draft(
match ctx.try_with_tx(|tx| compile_puzzle_agent_draft_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
session_json: Some(serialize_json(&session)),
session: Some(session),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
session_json: None,
session: None,
error_message: Some(message),
},
}
@@ -284,12 +313,12 @@ pub fn save_puzzle_form_draft(
match ctx.try_with_tx(|tx| save_puzzle_form_draft_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
session_json: Some(serialize_json(&session)),
session: Some(session),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
session_json: None,
session: None,
error_message: Some(message),
},
}
@@ -303,12 +332,12 @@ pub fn save_puzzle_generated_images(
match ctx.try_with_tx(|tx| save_puzzle_generated_images_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
session_json: Some(serialize_json(&session)),
session: Some(session),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
session_json: None,
session: None,
error_message: Some(message),
},
}
@@ -322,12 +351,12 @@ pub fn save_puzzle_ui_background(
match ctx.try_with_tx(|tx| save_puzzle_ui_background_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
session_json: Some(serialize_json(&session)),
session: Some(session),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
session_json: None,
session: None,
error_message: Some(message),
},
}
@@ -341,12 +370,12 @@ pub fn select_puzzle_cover_image(
match ctx.try_with_tx(|tx| select_puzzle_cover_image_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
session_json: Some(serialize_json(&session)),
session: Some(session),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
session_json: None,
session: None,
error_message: Some(message),
},
}
@@ -360,12 +389,12 @@ pub fn publish_puzzle_work(
match ctx.try_with_tx(|tx| publish_puzzle_work_tx(tx, input.clone())) {
Ok(item) => PuzzleWorkProcedureResult {
ok: true,
item_json: Some(serialize_json(&item)),
item: Some(item),
error_message: None,
},
Err(message) => PuzzleWorkProcedureResult {
ok: false,
item_json: None,
item: None,
error_message: Some(message),
},
}
@@ -379,12 +408,12 @@ pub fn list_puzzle_works(
match ctx.try_with_tx(|tx| list_puzzle_works_tx(tx, input.clone())) {
Ok(items) => PuzzleWorksProcedureResult {
ok: true,
items_json: Some(serialize_json(&items)),
items,
error_message: None,
},
Err(message) => PuzzleWorksProcedureResult {
ok: false,
items_json: None,
items: Vec::new(),
error_message: Some(message),
},
}
@@ -398,12 +427,12 @@ pub fn get_puzzle_work_detail(
match ctx.try_with_tx(|tx| get_puzzle_work_detail_tx(tx, input.clone())) {
Ok(item) => PuzzleWorkProcedureResult {
ok: true,
item_json: Some(serialize_json(&item)),
item: Some(item),
error_message: None,
},
Err(message) => PuzzleWorkProcedureResult {
ok: false,
item_json: None,
item: None,
error_message: Some(message),
},
}
@@ -417,12 +446,12 @@ pub fn update_puzzle_work(
match ctx.try_with_tx(|tx| update_puzzle_work_tx(tx, input.clone())) {
Ok(item) => PuzzleWorkProcedureResult {
ok: true,
item_json: Some(serialize_json(&item)),
item: Some(item),
error_message: None,
},
Err(message) => PuzzleWorkProcedureResult {
ok: false,
item_json: None,
item: None,
error_message: Some(message),
},
}
@@ -436,12 +465,12 @@ pub fn delete_puzzle_work(
match ctx.try_with_tx(|tx| delete_puzzle_work_tx(tx, input.clone())) {
Ok(items) => PuzzleWorksProcedureResult {
ok: true,
items_json: Some(serialize_json(&items)),
items,
error_message: None,
},
Err(message) => PuzzleWorksProcedureResult {
ok: false,
items_json: None,
items: Vec::new(),
error_message: Some(message),
},
}
@@ -452,12 +481,12 @@ pub fn list_puzzle_gallery(ctx: &mut ProcedureContext) -> PuzzleWorksProcedureRe
match ctx.try_with_tx(|tx| list_puzzle_gallery_tx(tx)) {
Ok(items) => PuzzleWorksProcedureResult {
ok: true,
items_json: Some(serialize_json(&items)),
items,
error_message: None,
},
Err(message) => PuzzleWorksProcedureResult {
ok: false,
items_json: None,
items: Vec::new(),
error_message: Some(message),
},
}
@@ -471,12 +500,12 @@ pub fn get_puzzle_gallery_detail(
match ctx.try_with_tx(|tx| get_puzzle_gallery_detail_tx(tx, input.clone())) {
Ok(item) => PuzzleWorkProcedureResult {
ok: true,
item_json: Some(serialize_json(&item)),
item: Some(item),
error_message: None,
},
Err(message) => PuzzleWorkProcedureResult {
ok: false,
item_json: None,
item: None,
error_message: Some(message),
},
}
@@ -490,12 +519,12 @@ pub fn record_puzzle_work_like(
match ctx.try_with_tx(|tx| record_puzzle_work_like_tx(tx, input.clone())) {
Ok(item) => PuzzleWorkProcedureResult {
ok: true,
item_json: Some(serialize_json(&item)),
item: Some(item),
error_message: None,
},
Err(message) => PuzzleWorkProcedureResult {
ok: false,
item_json: None,
item: None,
error_message: Some(message),
},
}
@@ -509,12 +538,12 @@ pub fn remix_puzzle_work(
match ctx.try_with_tx(|tx| remix_puzzle_work_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
session_json: Some(serialize_json(&session)),
session: Some(session),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
session_json: None,
session: None,
error_message: Some(message),
},
}
@@ -528,12 +557,12 @@ pub fn start_puzzle_run(
match ctx.try_with_tx(|tx| start_puzzle_run_tx(tx, input.clone())) {
Ok(run) => PuzzleRunProcedureResult {
ok: true,
run_json: Some(serialize_json(&run)),
run: Some(run),
error_message: None,
},
Err(message) => PuzzleRunProcedureResult {
ok: false,
run_json: None,
run: None,
error_message: Some(message),
},
}
@@ -547,12 +576,12 @@ pub fn get_puzzle_run(
match ctx.try_with_tx(|tx| get_puzzle_run_tx(tx, input.clone())) {
Ok(run) => PuzzleRunProcedureResult {
ok: true,
run_json: Some(serialize_json(&run)),
run: Some(run),
error_message: None,
},
Err(message) => PuzzleRunProcedureResult {
ok: false,
run_json: None,
run: None,
error_message: Some(message),
},
}
@@ -566,12 +595,12 @@ pub fn swap_puzzle_pieces(
match ctx.try_with_tx(|tx| swap_puzzle_pieces_tx(tx, input.clone())) {
Ok(run) => PuzzleRunProcedureResult {
ok: true,
run_json: Some(serialize_json(&run)),
run: Some(run),
error_message: None,
},
Err(message) => PuzzleRunProcedureResult {
ok: false,
run_json: None,
run: None,
error_message: Some(message),
},
}
@@ -585,12 +614,12 @@ pub fn drag_puzzle_piece_or_group(
match ctx.try_with_tx(|tx| drag_puzzle_piece_or_group_tx(tx, input.clone())) {
Ok(run) => PuzzleRunProcedureResult {
ok: true,
run_json: Some(serialize_json(&run)),
run: Some(run),
error_message: None,
},
Err(message) => PuzzleRunProcedureResult {
ok: false,
run_json: None,
run: None,
error_message: Some(message),
},
}
@@ -604,12 +633,12 @@ pub fn advance_puzzle_next_level(
match ctx.try_with_tx(|tx| advance_puzzle_next_level_tx(tx, input.clone())) {
Ok(run) => PuzzleRunProcedureResult {
ok: true,
run_json: Some(serialize_json(&run)),
run: Some(run),
error_message: None,
},
Err(message) => PuzzleRunProcedureResult {
ok: false,
run_json: None,
run: None,
error_message: Some(message),
},
}
@@ -623,12 +652,12 @@ pub fn update_puzzle_run_pause(
match ctx.try_with_tx(|tx| update_puzzle_run_pause_tx(tx, input.clone())) {
Ok(run) => PuzzleRunProcedureResult {
ok: true,
run_json: Some(serialize_json(&run)),
run: Some(run),
error_message: None,
},
Err(message) => PuzzleRunProcedureResult {
ok: false,
run_json: None,
run: None,
error_message: Some(message),
},
}
@@ -642,12 +671,12 @@ pub fn use_puzzle_runtime_prop(
match ctx.try_with_tx(|tx| use_puzzle_runtime_prop_tx(tx, input.clone())) {
Ok(run) => PuzzleRunProcedureResult {
ok: true,
run_json: Some(serialize_json(&run)),
run: Some(run),
error_message: None,
},
Err(message) => PuzzleRunProcedureResult {
ok: false,
run_json: None,
run: None,
error_message: Some(message),
},
}
@@ -661,12 +690,12 @@ pub fn claim_puzzle_work_point_incentive(
match ctx.try_with_tx(|tx| claim_puzzle_work_point_incentive_tx(tx, input.clone())) {
Ok(item) => PuzzleWorkProcedureResult {
ok: true,
item_json: Some(serialize_json(&item)),
item: Some(item),
error_message: None,
},
Err(message) => PuzzleWorkProcedureResult {
ok: false,
item_json: None,
item: None,
error_message: Some(message),
},
}
@@ -680,12 +709,12 @@ pub fn submit_puzzle_leaderboard_entry(
match ctx.try_with_tx(|tx| submit_puzzle_leaderboard_entry_tx(tx, input.clone())) {
Ok(run) => PuzzleRunProcedureResult {
ok: true,
run_json: Some(serialize_json(&run)),
run: Some(run),
error_message: None,
},
Err(message) => PuzzleRunProcedureResult {
ok: false,
run_json: None,
run: None,
error_message: Some(message),
},
}
@@ -1264,8 +1293,8 @@ fn list_puzzle_works_tx(
let mut items = ctx
.db
.puzzle_work_profile()
.iter()
.filter(|row| row.owner_user_id == input.owner_user_id)
.by_puzzle_work_owner_user_id()
.filter(&input.owner_user_id)
.map(|row| build_puzzle_work_profile_from_row(&row))
.collect::<Result<Vec<_>, _>>()?;
items.sort_by(|left, right| right.updated_at_micros.cmp(&left.updated_at_micros));
@@ -1446,8 +1475,8 @@ fn delete_puzzle_work_tx(
for message in ctx
.db
.puzzle_agent_message()
.iter()
.filter(|message| message.session_id == *session_id)
.by_puzzle_agent_message_session_id()
.filter(session_id)
.collect::<Vec<_>>()
{
ctx.db
@@ -1459,10 +1488,9 @@ fn delete_puzzle_work_tx(
for run in ctx
.db
.puzzle_runtime_run()
.iter()
.filter(|run| {
run.owner_user_id == input.owner_user_id && run.entry_profile_id == input.profile_id
})
.by_puzzle_runtime_run_owner_user_id()
.filter(&input.owner_user_id)
.filter(|run| run.entry_profile_id == input.profile_id)
.collect::<Vec<_>>()
{
ctx.db.puzzle_runtime_run().run_id().delete(&run.run_id);
@@ -1481,8 +1509,8 @@ fn list_puzzle_gallery_tx(ctx: &TxContext) -> Result<Vec<PuzzleWorkProfile>, Str
let rows = ctx
.db
.puzzle_work_profile()
.iter()
.filter(|row| row.publication_status == PuzzlePublicationStatus::Published)
.by_puzzle_work_publication_status()
.filter(PuzzlePublicationStatus::Published)
.collect::<Vec<_>>();
let profile_ids = rows
.iter()
@@ -2542,8 +2570,8 @@ fn list_session_messages(ctx: &TxContext, session_id: &str) -> Vec<PuzzleAgentMe
let mut items = ctx
.db
.puzzle_agent_message()
.iter()
.filter(|message| message.session_id == session_id)
.by_puzzle_agent_message_session_id()
.filter(&session_id.to_string())
.map(|message| PuzzleAgentMessageSnapshot {
message_id: message.message_id.clone(),
session_id: message.session_id.clone(),
@@ -3152,8 +3180,8 @@ fn replace_generated_candidate(
fn list_published_puzzle_profiles(ctx: &TxContext) -> Result<Vec<PuzzleWorkProfile>, String> {
ctx.db
.puzzle_work_profile()
.iter()
.filter(|row| row.publication_status == PuzzlePublicationStatus::Published)
.by_puzzle_work_publication_status()
.filter(PuzzlePublicationStatus::Published)
.map(|row| build_puzzle_work_profile_from_row(&row))
.collect()
}
@@ -3319,8 +3347,8 @@ fn list_puzzle_leaderboard_entries(
let mut rows = ctx
.db
.puzzle_leaderboard_entry()
.iter()
.filter(|row| row.profile_id == profile_id && row.grid_size == grid_size)
.by_puzzle_leaderboard_profile_grid()
.filter((profile_id, grid_size))
.collect::<Vec<_>>();
rows.sort_by(|left, right| {
left.best_elapsed_ms