迁移后端认证与拆分 Spacetime 客户端
This commit is contained in:
@@ -21,9 +21,11 @@ use module_quest::{
|
||||
pub(crate) use serde_json::{Map as JsonMap, Value as JsonValue, json};
|
||||
pub(crate) use shared_kernel::format_timestamp_micros;
|
||||
pub use spacetimedb::{ProcedureContext, ReducerContext, SpacetimeType, Table, Timestamp};
|
||||
use std::collections::HashSet;
|
||||
|
||||
mod ai;
|
||||
mod asset_metadata;
|
||||
mod auth;
|
||||
mod big_fish;
|
||||
mod domain_types;
|
||||
mod entry;
|
||||
@@ -32,6 +34,7 @@ mod runtime;
|
||||
|
||||
pub use ai::*;
|
||||
pub use asset_metadata::*;
|
||||
pub use auth::*;
|
||||
pub use big_fish::*;
|
||||
pub use domain_types::*;
|
||||
pub use entry::*;
|
||||
@@ -2733,10 +2736,12 @@ fn list_custom_world_work_snapshots(
|
||||
validate_custom_world_works_list_input(&input).map_err(|error| error.to_string())?;
|
||||
|
||||
let mut items = Vec::new();
|
||||
let mut active_agent_session_ids = HashSet::new();
|
||||
|
||||
for session in ctx.db.custom_world_agent_session().iter().filter(|row| {
|
||||
row.owner_user_id == input.owner_user_id && row.stage != RpgAgentStage::Published
|
||||
}) {
|
||||
active_agent_session_ids.insert(session.session_id.clone());
|
||||
let gate = build_custom_world_publish_gate_from_session(&session);
|
||||
let draft_profile = parse_optional_session_object(session.draft_profile_json.as_deref());
|
||||
let title = resolve_session_work_title(&session, draft_profile.as_ref());
|
||||
@@ -2780,6 +2785,7 @@ fn list_custom_world_work_snapshots(
|
||||
.custom_world_profile()
|
||||
.iter()
|
||||
.filter(|row| row.owner_user_id == input.owner_user_id && row.deleted_at.is_none())
|
||||
.filter(|row| should_include_custom_world_profile_work(row, &active_agent_session_ids))
|
||||
{
|
||||
items.push(CustomWorldWorkSummarySnapshot {
|
||||
work_id: format!("published:{}", profile.profile_id),
|
||||
@@ -2834,6 +2840,24 @@ fn list_custom_world_work_snapshots(
|
||||
Ok(items)
|
||||
}
|
||||
|
||||
fn should_include_custom_world_profile_work(
|
||||
row: &CustomWorldProfile,
|
||||
active_agent_session_ids: &HashSet<String>,
|
||||
) -> bool {
|
||||
// 已发布 profile 是正式作品;即使来源会话还存在,也必须保留独立入口。
|
||||
if row.publication_status == CustomWorldPublicationStatus::Published {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 未发布 profile 若来源于仍可继续聊天的 Agent 会话,只是同一草稿的编译产物,
|
||||
// works 里保留 agent_session 即可,避免草稿分组显示两份同名作品。
|
||||
row.source_agent_session_id
|
||||
.as_ref()
|
||||
.map_or(true, |session_id| {
|
||||
!active_agent_session_ids.contains(session_id)
|
||||
})
|
||||
}
|
||||
|
||||
fn get_custom_world_agent_card_detail_tx(
|
||||
ctx: &ReducerContext,
|
||||
input: CustomWorldAgentCardDetailGetInput,
|
||||
@@ -5996,6 +6020,113 @@ mod tests {
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_world_works_hides_compiled_draft_profile_when_agent_session_is_active() {
|
||||
fn build_test_custom_world_profile(
|
||||
profile_id: &str,
|
||||
source_agent_session_id: Option<&str>,
|
||||
publication_status: CustomWorldPublicationStatus,
|
||||
) -> CustomWorldProfile {
|
||||
CustomWorldProfile {
|
||||
profile_id: profile_id.to_string(),
|
||||
owner_user_id: "user-1".to_string(),
|
||||
public_work_code: if publication_status == CustomWorldPublicationStatus::Published {
|
||||
Some("CW-00000001".to_string())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
author_public_user_code: None,
|
||||
source_agent_session_id: source_agent_session_id.map(str::to_string),
|
||||
publication_status,
|
||||
world_name: "潮雾列岛".to_string(),
|
||||
subtitle: String::new(),
|
||||
summary_text: String::new(),
|
||||
theme_mode: CustomWorldThemeMode::Mythic,
|
||||
cover_image_src: None,
|
||||
profile_payload_json: "{}".to_string(),
|
||||
playable_npc_count: 0,
|
||||
landmark_count: 0,
|
||||
author_display_name: "玩家".to_string(),
|
||||
published_at: if publication_status == CustomWorldPublicationStatus::Published {
|
||||
Some(Timestamp::from_micros_since_unix_epoch(2))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
deleted_at: None,
|
||||
created_at: Timestamp::from_micros_since_unix_epoch(1),
|
||||
updated_at: Timestamp::from_micros_since_unix_epoch(1),
|
||||
}
|
||||
}
|
||||
|
||||
let draft_profile = build_test_custom_world_profile(
|
||||
"profile-1",
|
||||
Some("session-1"),
|
||||
CustomWorldPublicationStatus::Draft,
|
||||
);
|
||||
let orphan_draft_profile = build_test_custom_world_profile(
|
||||
"profile-2",
|
||||
Some("session-2"),
|
||||
CustomWorldPublicationStatus::Draft,
|
||||
);
|
||||
let published_profile = build_test_custom_world_profile(
|
||||
"profile-3",
|
||||
Some("session-1"),
|
||||
CustomWorldPublicationStatus::Published,
|
||||
);
|
||||
let mut active_agent_session_ids = HashSet::new();
|
||||
active_agent_session_ids.insert("session-1".to_string());
|
||||
|
||||
assert!(!should_include_custom_world_profile_work(
|
||||
&draft_profile,
|
||||
&active_agent_session_ids,
|
||||
));
|
||||
assert!(should_include_custom_world_profile_work(
|
||||
&orphan_draft_profile,
|
||||
&active_agent_session_ids,
|
||||
));
|
||||
assert!(should_include_custom_world_profile_work(
|
||||
&published_profile,
|
||||
&active_agent_session_ids,
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_world_works_keeps_compiled_draft_profile_without_active_agent_session() {
|
||||
let draft_profile = CustomWorldProfile {
|
||||
profile_id: "profile-1".to_string(),
|
||||
owner_user_id: "user-1".to_string(),
|
||||
public_work_code: None,
|
||||
author_public_user_code: None,
|
||||
source_agent_session_id: Some("session-1".to_string()),
|
||||
publication_status: CustomWorldPublicationStatus::Draft,
|
||||
world_name: "潮雾列岛".to_string(),
|
||||
subtitle: String::new(),
|
||||
summary_text: String::new(),
|
||||
theme_mode: CustomWorldThemeMode::Mythic,
|
||||
cover_image_src: None,
|
||||
profile_payload_json: "{}".to_string(),
|
||||
playable_npc_count: 0,
|
||||
landmark_count: 0,
|
||||
author_display_name: "玩家".to_string(),
|
||||
published_at: None,
|
||||
deleted_at: None,
|
||||
created_at: Timestamp::from_micros_since_unix_epoch(1),
|
||||
updated_at: Timestamp::from_micros_since_unix_epoch(1),
|
||||
};
|
||||
let mut active_agent_session_ids = HashSet::new();
|
||||
|
||||
assert!(should_include_custom_world_profile_work(
|
||||
&draft_profile,
|
||||
&active_agent_session_ids,
|
||||
));
|
||||
|
||||
active_agent_session_ids.insert("session-2".to_string());
|
||||
assert!(should_include_custom_world_profile_work(
|
||||
&draft_profile,
|
||||
&active_agent_session_ids,
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn summarize_publish_gate_accepts_current_agent_result_schema() {
|
||||
let draft_profile = serde_json::from_str::<JsonValue>(
|
||||
|
||||
Reference in New Issue
Block a user