From 8e96c8a67c345ca2686a8f6117235d63a047a104 Mon Sep 17 00:00:00 2001 From: kdletters <61648117+kdletters@users.noreply.github.com> Date: Wed, 27 May 2026 19:30:10 +0800 Subject: [PATCH] feat: add visible flag for works --- ...架构】统一公开作品ReadModel设计-2026-05-26.md | 3 ++ ...】server-rs与SpacetimeDB数据契约-2026-05-15.md | 10 ++++++ .../bark_battle_published_config_row_type.rs | 3 ++ .../big_fish_creation_session_type.rs | 3 ++ .../custom_world_gallery_entry_type.rs | 3 ++ .../custom_world_profile_type.rs | 3 ++ .../jump_hop_work_profile_row_type.rs | 3 ++ .../match_3_d_work_profile_row_type.rs | 3 ++ .../puzzle_work_profile_row_type.rs | 3 ++ .../square_hole_work_profile_row_type.rs | 3 ++ .../visual_novel_work_profile_row_type.rs | 3 ++ .../wooden_fish_work_profile_row_type.rs | 3 ++ .../spacetime-module/src/bark_battle.rs | 2 ++ .../src/bark_battle/tables.rs | 5 +++ .../spacetime-module/src/big_fish/assets.rs | 2 ++ .../spacetime-module/src/big_fish/session.rs | 11 ++++++ .../spacetime-module/src/big_fish/tables.rs | 5 +++ .../spacetime-module/src/custom_world.rs | 34 +++++++++++++++++-- .../crates/spacetime-module/src/jump_hop.rs | 3 ++ .../spacetime-module/src/jump_hop/tables.rs | 5 +++ .../crates/spacetime-module/src/match3d.rs | 10 ++++++ .../spacetime-module/src/match3d/tables.rs | 5 +++ .../crates/spacetime-module/src/migration.rs | 26 ++++++++++++++ .../crates/spacetime-module/src/puzzle.rs | 15 ++++++++ .../spacetime-module/src/square_hole.rs | 4 +++ .../src/square_hole/tables.rs | 5 +++ .../spacetime-module/src/visual_novel.rs | 27 +++++++++------ .../spacetime-module/src/wooden_fish.rs | 3 ++ .../src/wooden_fish/tables.rs | 5 +++ 29 files changed, 198 insertions(+), 12 deletions(-) diff --git a/docs/technical/【后端架构】统一公开作品ReadModel设计-2026-05-26.md b/docs/technical/【后端架构】统一公开作品ReadModel设计-2026-05-26.md index faa08e20..9a7f7321 100644 --- a/docs/technical/【后端架构】统一公开作品ReadModel设计-2026-05-26.md +++ b/docs/technical/【后端架构】统一公开作品ReadModel设计-2026-05-26.md @@ -39,6 +39,8 @@ - `sort_time_micros` - `detail_payload_json` +作品源表新增 `visible` 可见性字段,默认 `true`。`visible` 属于源表 / source view 过滤条件,不作为统一公开契约默认返回字段;当 `visible=false` 时,对应作品不得进入 `public_work_gallery_entry` 和 `public_work_detail_entry`。 + 其中 `detail_payload_json` 只承载平台详情页展示扩展,不承载正式 runtime 配置、玩法规则或草稿真相。 ## 来源与兼容 @@ -63,6 +65,7 @@ - 旧 view 保留,不删除。 - 旧 view 退到底层 source / 兼容职责。 - 新 `public_work_*` view 是 `api-server` 公开列表 / 详情的统一主读模型。 +- 各玩法 source view 只暴露 `visible=true` 的已发布作品;旧数据迁移默认补 `visible=true`,避免历史作品被误隐藏。 - 旧 `/api/runtime//gallery` 响应 shape 保持兼容,由 BFF mapper 把统一 cache 再映射回当前 DTO。 - 旧详情 / runtime / 点赞 / 游玩 / Remix 仍走玩法专用路径。 diff --git a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md index 206c98b5..3d2ae208 100644 --- a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md +++ b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md @@ -252,6 +252,7 @@ npm run check:server-rs-ddd - Rust 结构体:`BarkBattlePublishedConfigRow` - 源码:`server-rs/crates/spacetime-module/src/bark_battle/tables.rs` +- 字段变更:`visible` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。 ### `bark_battle_runtime_run` @@ -288,6 +289,7 @@ npm run check:server-rs-ddd - Rust 结构体:`BigFishCreationSession` - 源码:`server-rs/crates/spacetime-module/src/big_fish/tables.rs` - 索引:`by_big_fish_session_owner_user_id`、`by_big_fish_session_stage`。公开广场 view 使用 `by_big_fish_session_stage` 读取已发布会话,避免扫整表。 +- 字段变更:`visible` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。 ### `big_fish_event` @@ -351,11 +353,13 @@ npm run check:server-rs-ddd - Rust 结构体:`CustomWorldGalleryEntry` - 源码:`server-rs/crates/spacetime-module/src/custom_world.rs` - 作用:自定义世界公开 source 读模型。统一公开列表 / 详情主路径通过 `public_work_gallery_entry` / `public_work_detail_entry` 消费该投影并映射成跨玩法契约;`/api/runtime/custom-world-gallery` 保留旧 HTTP shape,并从统一 public cache 映射回旧 DTO。旧 procedure 只用于兼容旧库缺少 gallery 读模型行时的一次性同步兜底。 +- 字段变更:`visible` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。 ### `custom_world_profile` - Rust 结构体:`CustomWorldProfile` - 源码:`server-rs/crates/spacetime-module/src/custom_world.rs` +- 字段变更:`visible` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。 ### `custom_world_session` @@ -410,6 +414,7 @@ npm run check:server-rs-ddd - 返回类型:`Vec` - 源码:`server-rs/crates/spacetime-module/src/jump_hop.rs` - 说明:跳一跳公开详情兼容投影,包含作品、路径和素材字段;统一公开详情主路径通过 `public_work_detail_entry` 消费该 view,只保留平台详情页展示摘要。 +- 字段变更:`visible` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。 ### `wooden_fish_agent_session` @@ -445,6 +450,7 @@ npm run check:server-rs-ddd - 返回类型:`Vec` - 源码:`server-rs/crates/spacetime-module/src/wooden_fish.rs` - 说明:敲木鱼公开详情兼容投影,包含敲击物图案、背景环境图、主题返回按钮图、敲击音效和飘字配置;统一公开详情主路径通过 `public_work_detail_entry` 消费该 view,只保留平台详情页展示摘要。 +- 字段变更:`visible` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。 ### `match3d_agent_message` @@ -472,6 +478,7 @@ npm run check:server-rs-ddd - 返回类型:`Vec` - 源码:`server-rs/crates/spacetime-module/src/match3d.rs` - 说明:抓大鹅公开 source 投影,只暴露 `publication_status = published` 的作品卡片字段;统一公开列表 / 详情主路径通过 `public_work_gallery_entry` / `public_work_detail_entry` 消费该 view 并映射成跨玩法契约。个人作品列表、详情、发布、点赞、游玩记录和 Remix 仍按原有 procedure / reducer 路径处理。 +- 字段变更:`visible` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。 ### `npc_state` @@ -658,6 +665,7 @@ RPG 创作入口的配置 ID 是 `rpg`,当前 `visible=true`、`open=true`; 结构化创作和 RPG 的 LLM JSON 链路默认不启用 Responses `web_search`;只有在明确需要联网增强时,才通过 `GENARRATIVE_RPG_LLM_WEB_SEARCH_ENABLED` 或 `GENARRATIVE_CREATION_AGENT_LLM_WEB_SEARCH_ENABLED` 显式打开。否则未开通工具的上游会先吐自然语言再返回 `ToolNotOpen`,这类失败要按上游工具不可用处理,不要误判成模型返回结果解析失败。 统一公开作品 BFF 路由是 `GET /api/public-works` 与 `GET /api/public-works/{publicWorkCode}`,响应契约由 `shared-contracts::public_work` 和 `packages/shared/src/contracts/publicWork.ts` 共同维护。前端首期仍走 BFF HTTP,不直接订阅 SpacetimeDB;后续若允许浏览器直连订阅,也只能订阅 `public_work_gallery_entry` / `public_work_detail_entry` 这类稳定公开 read model,不能订阅 `puzzle_work_profile`、`custom_world_profile` 等源表后自行拼装列表。设计细节见 `docs/technical/【后端架构】统一公开作品ReadModel设计-2026-05-26.md`。 +- 字段变更:`visible` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。 ### `quest_log` @@ -710,6 +718,7 @@ RPG 创作入口的配置 ID 是 `rpg`,当前 `visible=true`、`open=true`; - 返回类型:`Vec` - 源码:`server-rs/crates/spacetime-module/src/square_hole.rs` - 说明:方洞挑战公开 source 投影,只暴露 `publication_status = published` 的作品卡片字段;统一公开列表 / 详情主路径通过 `public_work_gallery_entry` / `public_work_detail_entry` 消费该 view 并映射成跨玩法契约。个人作品列表、详情、发布、点赞、游玩记录和 Remix 仍按原有 procedure / reducer 路径处理。 +- 字段变更:`visible` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。 ### `story_event` @@ -785,3 +794,4 @@ RPG 创作入口的配置 ID 是 `rpg`,当前 `visible=true`、`open=true`; - 返回类型:`Vec` - 源码:`server-rs/crates/spacetime-module/src/visual_novel.rs` - 说明:视觉小说公开 source 投影,只暴露 `publication_status = published` 的作品卡片字段,不把完整 `draft` 暴露给公开列表订阅;统一公开列表 / 详情主路径通过 `public_work_gallery_entry` / `public_work_detail_entry` 消费该 view 并映射成跨玩法契约。个人历史、详情、运行态和发布仍按原有 procedure / reducer 路径处理。 +- 字段变更:`visible` 控制是否进入公开列表 / 详情,默认 `true`;旧迁移数据由 `migration.rs` 补默认值。 diff --git a/server-rs/crates/spacetime-client/src/module_bindings/bark_battle_published_config_row_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/bark_battle_published_config_row_type.rs index c442c6bc..64f11bac 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/bark_battle_published_config_row_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/bark_battle_published_config_row_type.rs @@ -19,6 +19,7 @@ pub struct BarkBattlePublishedConfigRow { pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, pub published_at: __sdk::Timestamp, + pub visible: bool, } impl __sdk::InModule for BarkBattlePublishedConfigRow { @@ -41,6 +42,7 @@ pub struct BarkBattlePublishedConfigRowCols { pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, pub published_at: __sdk::__query_builder::Col, + pub visible: __sdk::__query_builder::Col, } impl __sdk::__query_builder::HasCols for BarkBattlePublishedConfigRow { @@ -65,6 +67,7 @@ impl __sdk::__query_builder::HasCols for BarkBattlePublishedConfigRow { created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), published_at: __sdk::__query_builder::Col::new(table_name, "published_at"), + visible: __sdk::__query_builder::Col::new(table_name, "visible"), } } } diff --git a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_creation_session_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_creation_session_type.rs index d87690de..25aaff8c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/big_fish_creation_session_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/big_fish_creation_session_type.rs @@ -26,6 +26,7 @@ pub struct BigFishCreationSession { pub remix_count: u32, pub like_count: u32, pub published_at: Option<__sdk::Timestamp>, + pub visible: bool, } impl __sdk::InModule for BigFishCreationSession { @@ -53,6 +54,7 @@ pub struct BigFishCreationSessionCols { pub remix_count: __sdk::__query_builder::Col, pub like_count: __sdk::__query_builder::Col, pub published_at: __sdk::__query_builder::Col>, + pub visible: __sdk::__query_builder::Col, } impl __sdk::__query_builder::HasCols for BigFishCreationSession { @@ -82,6 +84,7 @@ impl __sdk::__query_builder::HasCols for BigFishCreationSession { remix_count: __sdk::__query_builder::Col::new(table_name, "remix_count"), like_count: __sdk::__query_builder::Col::new(table_name, "like_count"), published_at: __sdk::__query_builder::Col::new(table_name, "published_at"), + visible: __sdk::__query_builder::Col::new(table_name, "visible"), } } } diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_entry_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_entry_type.rs index 971fd3b2..4e8a9b2a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_entry_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_gallery_entry_type.rs @@ -26,6 +26,7 @@ pub struct CustomWorldGalleryEntry { pub like_count: u32, pub published_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, + pub visible: bool, } impl __sdk::InModule for CustomWorldGalleryEntry { @@ -53,6 +54,7 @@ pub struct CustomWorldGalleryEntryCols { pub like_count: __sdk::__query_builder::Col, pub published_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, + pub visible: __sdk::__query_builder::Col, } impl __sdk::__query_builder::HasCols for CustomWorldGalleryEntry { @@ -82,6 +84,7 @@ impl __sdk::__query_builder::HasCols for CustomWorldGalleryEntry { like_count: __sdk::__query_builder::Col::new(table_name, "like_count"), published_at: __sdk::__query_builder::Col::new(table_name, "published_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + visible: __sdk::__query_builder::Col::new(table_name, "visible"), } } } diff --git a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_type.rs index 4ad1e730..bbed3f0d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/custom_world_profile_type.rs @@ -32,6 +32,7 @@ pub struct CustomWorldProfile { pub deleted_at: Option<__sdk::Timestamp>, pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, + pub visible: bool, } impl __sdk::InModule for CustomWorldProfile { @@ -65,6 +66,7 @@ pub struct CustomWorldProfileCols { pub deleted_at: __sdk::__query_builder::Col>, pub created_at: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, + pub visible: __sdk::__query_builder::Col, } impl __sdk::__query_builder::HasCols for CustomWorldProfile { @@ -105,6 +107,7 @@ impl __sdk::__query_builder::HasCols for CustomWorldProfile { deleted_at: __sdk::__query_builder::Col::new(table_name, "deleted_at"), created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + visible: __sdk::__query_builder::Col::new(table_name, "visible"), } } } diff --git a/server-rs/crates/spacetime-client/src/module_bindings/jump_hop_work_profile_row_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/jump_hop_work_profile_row_type.rs index b7bbd776..660ea530 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/jump_hop_work_profile_row_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/jump_hop_work_profile_row_type.rs @@ -31,6 +31,7 @@ pub struct JumpHopWorkProfileRow { pub play_count: u32, pub updated_at: __sdk::Timestamp, pub published_at: Option<__sdk::Timestamp>, + pub visible: bool, } impl __sdk::InModule for JumpHopWorkProfileRow { @@ -65,6 +66,7 @@ pub struct JumpHopWorkProfileRowCols { pub play_count: __sdk::__query_builder::Col, pub updated_at: __sdk::__query_builder::Col, pub published_at: __sdk::__query_builder::Col>, + pub visible: __sdk::__query_builder::Col, } impl __sdk::__query_builder::HasCols for JumpHopWorkProfileRow { @@ -104,6 +106,7 @@ impl __sdk::__query_builder::HasCols for JumpHopWorkProfileRow { play_count: __sdk::__query_builder::Col::new(table_name, "play_count"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), published_at: __sdk::__query_builder::Col::new(table_name, "published_at"), + visible: __sdk::__query_builder::Col::new(table_name, "visible"), } } } diff --git a/server-rs/crates/spacetime-client/src/module_bindings/match_3_d_work_profile_row_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/match_3_d_work_profile_row_type.rs index 03ff94b9..aafe2065 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/match_3_d_work_profile_row_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/match_3_d_work_profile_row_type.rs @@ -25,6 +25,7 @@ pub struct Match3DWorkProfileRow { pub updated_at: __sdk::Timestamp, pub published_at: Option<__sdk::Timestamp>, pub generated_item_assets_json: Option, + pub visible: bool, } impl __sdk::InModule for Match3DWorkProfileRow { @@ -54,6 +55,7 @@ pub struct Match3DWorkProfileRowCols { pub published_at: __sdk::__query_builder::Col>, pub generated_item_assets_json: __sdk::__query_builder::Col>, + pub visible: __sdk::__query_builder::Col, } impl __sdk::__query_builder::HasCols for Match3DWorkProfileRow { @@ -84,6 +86,7 @@ impl __sdk::__query_builder::HasCols for Match3DWorkProfileRow { table_name, "generated_item_assets_json", ), + visible: __sdk::__query_builder::Col::new(table_name, "visible"), } } } diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_profile_row_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_profile_row_type.rs index a2ebd389..db43f53a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_profile_row_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_work_profile_row_type.rs @@ -33,6 +33,7 @@ pub struct PuzzleWorkProfileRow { pub like_count: u32, pub point_incentive_total_half_points: u64, pub point_incentive_claimed_points: u64, + pub visible: bool, } impl __sdk::InModule for PuzzleWorkProfileRow { @@ -68,6 +69,7 @@ pub struct PuzzleWorkProfileRowCols { pub like_count: __sdk::__query_builder::Col, pub point_incentive_total_half_points: __sdk::__query_builder::Col, pub point_incentive_claimed_points: __sdk::__query_builder::Col, + pub visible: __sdk::__query_builder::Col, } impl __sdk::__query_builder::HasCols for PuzzleWorkProfileRow { @@ -107,6 +109,7 @@ impl __sdk::__query_builder::HasCols for PuzzleWorkProfileRow { table_name, "point_incentive_claimed_points", ), + visible: __sdk::__query_builder::Col::new(table_name, "visible"), } } } diff --git a/server-rs/crates/spacetime-client/src/module_bindings/square_hole_work_profile_row_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/square_hole_work_profile_row_type.rs index b61c6f20..9798d7da 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/square_hole_work_profile_row_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/square_hole_work_profile_row_type.rs @@ -25,6 +25,7 @@ pub struct SquareHoleWorkProfileRow { pub play_count: u32, pub updated_at: __sdk::Timestamp, pub published_at: Option<__sdk::Timestamp>, + pub visible: bool, } impl __sdk::InModule for SquareHoleWorkProfileRow { @@ -54,6 +55,7 @@ pub struct SquareHoleWorkProfileRowCols { pub updated_at: __sdk::__query_builder::Col, pub published_at: __sdk::__query_builder::Col>, + pub visible: __sdk::__query_builder::Col, } impl __sdk::__query_builder::HasCols for SquareHoleWorkProfileRow { @@ -81,6 +83,7 @@ impl __sdk::__query_builder::HasCols for SquareHoleWorkProfileRow { play_count: __sdk::__query_builder::Col::new(table_name, "play_count"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), published_at: __sdk::__query_builder::Col::new(table_name, "published_at"), + visible: __sdk::__query_builder::Col::new(table_name, "visible"), } } } diff --git a/server-rs/crates/spacetime-client/src/module_bindings/visual_novel_work_profile_row_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/visual_novel_work_profile_row_type.rs index 941c07c6..d4556acb 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/visual_novel_work_profile_row_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/visual_novel_work_profile_row_type.rs @@ -24,6 +24,7 @@ pub struct VisualNovelWorkProfileRow { pub created_at: __sdk::Timestamp, pub updated_at: __sdk::Timestamp, pub published_at: Option<__sdk::Timestamp>, + pub visible: bool, } impl __sdk::InModule for VisualNovelWorkProfileRow { @@ -52,6 +53,7 @@ pub struct VisualNovelWorkProfileRowCols { pub updated_at: __sdk::__query_builder::Col, pub published_at: __sdk::__query_builder::Col>, + pub visible: __sdk::__query_builder::Col, } impl __sdk::__query_builder::HasCols for VisualNovelWorkProfileRow { @@ -81,6 +83,7 @@ impl __sdk::__query_builder::HasCols for VisualNovelWorkProfileRow { created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), published_at: __sdk::__query_builder::Col::new(table_name, "published_at"), + visible: __sdk::__query_builder::Col::new(table_name, "visible"), } } } diff --git a/server-rs/crates/spacetime-client/src/module_bindings/wooden_fish_work_profile_row_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/wooden_fish_work_profile_row_type.rs index 456316a8..5803a598 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/wooden_fish_work_profile_row_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/wooden_fish_work_profile_row_type.rs @@ -29,6 +29,7 @@ pub struct WoodenFishWorkProfileRow { pub published_at: Option<__sdk::Timestamp>, pub background_asset_json: Option, pub back_button_asset_json: Option, + pub visible: bool, } impl __sdk::InModule for WoodenFishWorkProfileRow { @@ -65,6 +66,7 @@ pub struct WoodenFishWorkProfileRowCols { __sdk::__query_builder::Col>, pub back_button_asset_json: __sdk::__query_builder::Col>, + pub visible: __sdk::__query_builder::Col, } impl __sdk::__query_builder::HasCols for WoodenFishWorkProfileRow { @@ -114,6 +116,7 @@ impl __sdk::__query_builder::HasCols for WoodenFishWorkProfileRow { table_name, "back_button_asset_json", ), + visible: __sdk::__query_builder::Col::new(table_name, "visible"), } } } diff --git a/server-rs/crates/spacetime-module/src/bark_battle.rs b/server-rs/crates/spacetime-module/src/bark_battle.rs index 96763abd..2a19b32a 100644 --- a/server-rs/crates/spacetime-module/src/bark_battle.rs +++ b/server-rs/crates/spacetime-module/src/bark_battle.rs @@ -20,6 +20,7 @@ pub fn bark_battle_gallery_view(ctx: &AnonymousViewContext) -> Vec Some(item), Err(error) => { @@ -260,6 +261,7 @@ fn publish_bark_battle_work_tx( created_at: published_at, updated_at: published_at, published_at, + visible: true, }; let mut published = published; match ctx diff --git a/server-rs/crates/spacetime-module/src/bark_battle/tables.rs b/server-rs/crates/spacetime-module/src/bark_battle/tables.rs index 9b436e6a..a92b38b5 100644 --- a/server-rs/crates/spacetime-module/src/bark_battle/tables.rs +++ b/server-rs/crates/spacetime-module/src/bark_battle/tables.rs @@ -1,5 +1,7 @@ use crate::*; +const WORK_VISIBLE_DEFAULT: bool = true; + #[spacetimedb::table( accessor = bark_battle_draft_config, index(accessor = by_bark_battle_draft_owner_user_id, btree(columns = [owner_user_id])), @@ -40,6 +42,9 @@ pub struct BarkBattlePublishedConfigRow { pub(crate) created_at: Timestamp, pub(crate) updated_at: Timestamp, pub(crate) published_at: Timestamp, + // ??????????????????????????????? + #[default(WORK_VISIBLE_DEFAULT)] + pub(crate) visible: bool, } #[spacetimedb::table( diff --git a/server-rs/crates/spacetime-module/src/big_fish/assets.rs b/server-rs/crates/spacetime-module/src/big_fish/assets.rs index 1da68d3c..9c7fda1f 100644 --- a/server-rs/crates/spacetime-module/src/big_fish/assets.rs +++ b/server-rs/crates/spacetime-module/src/big_fish/assets.rs @@ -129,6 +129,7 @@ pub(crate) fn generate_big_fish_asset_tx( published_at: session.published_at, created_at: session.created_at, updated_at, + visible: session.visible, }; replace_big_fish_session(ctx, &session, next_session); for event in readiness.events { @@ -200,6 +201,7 @@ pub(crate) fn publish_big_fish_game_tx( published_at: Some(published_at), created_at: session.created_at, updated_at: published_at, + visible: session.visible, }; replace_big_fish_session(ctx, &session, next_session); for event in readiness.events { diff --git a/server-rs/crates/spacetime-module/src/big_fish/session.rs b/server-rs/crates/spacetime-module/src/big_fish/session.rs index 5ae78d2a..359afe4f 100644 --- a/server-rs/crates/spacetime-module/src/big_fish/session.rs +++ b/server-rs/crates/spacetime-module/src/big_fish/session.rs @@ -23,6 +23,7 @@ pub fn big_fish_gallery_view(ctx: &AnonymousViewContext) -> Vec Some(snapshot), Err(error) => { @@ -284,6 +285,7 @@ pub(crate) fn create_big_fish_session_tx( published_at: None, created_at, updated_at: created_at, + visible: true, }); ctx.db.big_fish_agent_message().insert(BigFishAgentMessage { message_id: input.welcome_message_id, @@ -492,6 +494,7 @@ pub(crate) fn submit_big_fish_message_tx( published_at: session.published_at, created_at: session.created_at, updated_at: submitted_at, + visible: session.visible, }; replace_big_fish_session(ctx, &session, next_session); @@ -542,6 +545,7 @@ pub(crate) fn finalize_big_fish_agent_message_turn_tx( published_at: session.published_at, created_at: session.created_at, updated_at, + visible: session.visible, }; replace_big_fish_session(ctx, &session, next_session); return Err(error_message.to_string()); @@ -600,6 +604,7 @@ pub(crate) fn finalize_big_fish_agent_message_turn_tx( published_at: session.published_at, created_at: session.created_at, updated_at, + visible: session.visible, }; replace_big_fish_session(ctx, &session, next_session); @@ -667,6 +672,7 @@ pub(crate) fn compile_big_fish_draft_tx( published_at: session.published_at, created_at: session.created_at, updated_at: compiled_at, + visible: session.visible, }; replace_big_fish_session(ctx, &session, next_session); for event in readiness.events { @@ -768,6 +774,7 @@ pub(crate) fn record_big_fish_play_tx( published_at: session.published_at, created_at: session.created_at, updated_at: played_at, + visible: session.visible, }; replace_big_fish_session(ctx, &session, next_session); @@ -821,6 +828,7 @@ pub(crate) fn record_big_fish_like_tx( published_at: session.published_at, created_at: session.created_at, updated_at: liked_at, + visible: session.visible, }; replace_big_fish_session(ctx, &session, next_session); } @@ -888,6 +896,7 @@ fn remix_big_fish_work_tx( published_at: source.published_at, created_at: source.created_at, updated_at: remixed_at, + visible: source.visible, }; replace_big_fish_session(ctx, &source, next_source); @@ -909,6 +918,7 @@ fn remix_big_fish_work_tx( published_at: None, created_at: remixed_at, updated_at: remixed_at, + visible: true, }; ctx.db.big_fish_creation_session().insert(target_session); ctx.db.big_fish_agent_message().insert(BigFishAgentMessage { @@ -1238,6 +1248,7 @@ mod tests { }, created_at: Timestamp::from_micros_since_unix_epoch(1), updated_at: Timestamp::from_micros_since_unix_epoch(1), + visible: true, } } diff --git a/server-rs/crates/spacetime-module/src/big_fish/tables.rs b/server-rs/crates/spacetime-module/src/big_fish/tables.rs index fa480120..ea58dacb 100644 --- a/server-rs/crates/spacetime-module/src/big_fish/tables.rs +++ b/server-rs/crates/spacetime-module/src/big_fish/tables.rs @@ -1,5 +1,7 @@ use crate::*; +const WORK_VISIBLE_DEFAULT: bool = true; + #[spacetimedb::table( accessor = big_fish_creation_session, index(accessor = by_big_fish_session_owner_user_id, btree(columns = [owner_user_id])), @@ -28,6 +30,9 @@ pub struct BigFishCreationSession { pub(crate) like_count: u32, #[default(None::)] pub(crate) published_at: Option, + // ??????????????????????????????? + #[default(WORK_VISIBLE_DEFAULT)] + pub(crate) visible: bool, } #[spacetimedb::table( diff --git a/server-rs/crates/spacetime-module/src/custom_world.rs b/server-rs/crates/spacetime-module/src/custom_world.rs index 06133e32..83c932ab 100644 --- a/server-rs/crates/spacetime-module/src/custom_world.rs +++ b/server-rs/crates/spacetime-module/src/custom_world.rs @@ -2,6 +2,8 @@ use crate::*; use spacetimedb::AnonymousViewContext; use std::collections::{HashMap, HashSet}; +const WORK_VISIBLE_DEFAULT: bool = true; + #[spacetimedb::table( accessor = custom_world_profile, index(accessor = by_custom_world_profile_owner_user_id, btree(columns = [owner_user_id])), @@ -42,6 +44,9 @@ pub struct CustomWorldProfile { deleted_at: Option, created_at: Timestamp, updated_at: Timestamp, + // ?????????????????????????? + #[default(WORK_VISIBLE_DEFAULT)] + visible: bool, } #[spacetimedb::table( @@ -192,6 +197,9 @@ pub struct CustomWorldGalleryEntry { like_count: u32, published_at: Timestamp, updated_at: Timestamp, + // ?????????????????????????? + #[default(WORK_VISIBLE_DEFAULT)] + visible: bool, } // Agent 会话首版只负责把可持久化创作状态落进 SpacetimeDB,LLM 采集与卡片生成后续再接入。 #[spacetimedb::procedure] @@ -1229,6 +1237,7 @@ fn upsert_custom_world_profile_record( deleted_at: None, created_at: existing.created_at, updated_at, + visible: existing.visible, } } None => CustomWorldProfile { @@ -1254,6 +1263,7 @@ fn upsert_custom_world_profile_record( deleted_at: None, created_at: updated_at, updated_at, + visible: true, }, }; @@ -1401,6 +1411,7 @@ fn publish_custom_world_profile_record( deleted_at: None, created_at: existing.created_at, updated_at: published_at, + visible: existing.visible, }; let inserted = ctx.db.custom_world_profile().insert(next_row); @@ -1467,6 +1478,7 @@ fn unpublish_custom_world_profile_record( deleted_at: None, created_at: existing.created_at, updated_at, + visible: existing.visible, }; let inserted = ctx.db.custom_world_profile().insert(next_row); @@ -1529,6 +1541,7 @@ fn delete_custom_world_profile_record( deleted_at: Some(deleted_at), created_at: existing.created_at, updated_at: deleted_at, + visible: existing.visible, }; let _ = ctx.db.custom_world_profile().insert(next_row); @@ -1651,6 +1664,7 @@ fn get_custom_world_gallery_detail_record( row.owner_user_id == input.owner_user_id && row.publication_status == CustomWorldPublicationStatus::Published && row.deleted_at.is_none() + && row.visible }); let gallery_entry = ctx @@ -1745,6 +1759,7 @@ fn remix_custom_world_profile_record( .filter(|row| { row.publication_status == CustomWorldPublicationStatus::Published && row.deleted_at.is_none() + && row.visible && row.published_at.is_some() }) .ok_or_else(|| "custom_world 已发布源作品不存在,无法改编".to_string())?; @@ -1777,6 +1792,7 @@ fn remix_custom_world_profile_record( deleted_at: source.deleted_at, created_at: source.created_at, updated_at: remixed_at, + visible: source.visible, }; let updated_source = ctx.db.custom_world_profile().insert(next_source); let source_gallery = sync_custom_world_gallery_entry_from_profile(ctx, &updated_source)?; @@ -1805,6 +1821,7 @@ fn remix_custom_world_profile_record( deleted_at: None, created_at: remixed_at, updated_at: remixed_at, + visible: true, }; if let Some(existing_target) = ctx @@ -1845,6 +1862,7 @@ fn record_custom_world_profile_play_record( .filter(|row| { row.publication_status == CustomWorldPublicationStatus::Published && row.deleted_at.is_none() + && row.visible && row.published_at.is_some() }) .ok_or_else(|| "custom_world 已发布作品不存在,无法记录游玩".to_string())?; @@ -1887,6 +1905,7 @@ fn record_custom_world_profile_play_record( deleted_at: existing.deleted_at, created_at: existing.created_at, updated_at: played_at, + visible: existing.visible, }; let inserted = ctx.db.custom_world_profile().insert(next_row); let gallery_entry = sync_custom_world_gallery_entry_from_profile(ctx, &inserted)?; @@ -1916,6 +1935,7 @@ fn record_custom_world_profile_like_record( .filter(|row| { row.publication_status == CustomWorldPublicationStatus::Published && row.deleted_at.is_none() + && row.visible && row.published_at.is_some() }) .ok_or_else(|| "custom_world 已发布作品不存在,无法点赞".to_string())?; @@ -1967,6 +1987,7 @@ fn record_custom_world_profile_like_record( deleted_at: existing.deleted_at, created_at: existing.created_at, updated_at: liked_at, + visible: existing.visible, }; let inserted = ctx.db.custom_world_profile().insert(next_row); let gallery_entry = sync_custom_world_gallery_entry_from_profile(ctx, &inserted)?; @@ -2582,6 +2603,7 @@ fn is_same_agent_draft_profile_candidate( ) -> bool { row.owner_user_id == owner_user_id && row.deleted_at.is_none() + && row.visible && row.publication_status == CustomWorldPublicationStatus::Draft && row.source_agent_session_id.as_deref() == Some(source_agent_session_id) } @@ -4841,6 +4863,7 @@ fn sync_custom_world_gallery_entry_from_profile( like_count: profile.like_count, published_at, updated_at: profile.updated_at, + visible: profile.visible, }; let inserted = ctx.db.custom_world_gallery_entry().insert(row); @@ -4854,7 +4877,7 @@ fn sync_missing_custom_world_gallery_entries(ctx: &ReducerContext) -> Result<(), .custom_world_profile() .by_custom_world_profile_publication_status() .filter(CustomWorldPublicationStatus::Published) - .filter(|profile| profile.deleted_at.is_none()) + .filter(|profile| profile.deleted_at.is_none() && profile.visible) .collect::>(); for profile in published_profiles { @@ -4926,6 +4949,7 @@ fn ensure_custom_world_profile_public_fields( deleted_at: profile.deleted_at, created_at: profile.created_at, updated_at: profile.updated_at, + visible: profile.visible, }; ctx.db.custom_world_profile().insert(next_row) @@ -4955,6 +4979,7 @@ fn build_custom_world_profile_row_copy(profile: &CustomWorldProfile) -> CustomWo deleted_at: profile.deleted_at, created_at: profile.created_at, updated_at: profile.updated_at, + visible: profile.visible, } } @@ -4997,7 +5022,7 @@ pub(crate) fn custom_world_public_profile_snapshots( .custom_world_profile() .by_custom_world_profile_publication_status() .filter(CustomWorldPublicationStatus::Published) - .filter(|row| row.deleted_at.is_none()) + .filter(|row| row.deleted_at.is_none() && row.visible) .map(|row| build_custom_world_profile_snapshot(&row)) .collect::>(); @@ -5156,6 +5181,7 @@ pub(crate) fn custom_world_public_gallery_snapshots( .custom_world_gallery_entry() .by_custom_world_gallery_owner_user_id() .filter(""..) + .filter(|row| row.visible) .map(|row| { build_custom_world_gallery_entry_snapshot_with_recent_counts(&row, &HashMap::new()) }) @@ -5377,6 +5403,7 @@ mod tests { deleted_at: None, created_at: Timestamp::from_micros_since_unix_epoch(1), updated_at: Timestamp::from_micros_since_unix_epoch(1), + visible: true, }; let deleted = CustomWorldProfile { profile_id: "profile-1".to_string(), @@ -5401,6 +5428,7 @@ mod tests { deleted_at: Some(Timestamp::from_micros_since_unix_epoch(2)), created_at: Timestamp::from_micros_since_unix_epoch(1), updated_at: Timestamp::from_micros_since_unix_epoch(1), + visible: true, }; let published = CustomWorldProfile { profile_id: "profile-1".to_string(), @@ -5425,6 +5453,7 @@ mod tests { deleted_at: None, created_at: Timestamp::from_micros_since_unix_epoch(1), updated_at: Timestamp::from_micros_since_unix_epoch(1), + visible: true, }; assert!(is_same_agent_draft_profile_candidate( @@ -5552,6 +5581,7 @@ mod tests { deleted_at: None, created_at: Timestamp::from_micros_since_unix_epoch(1), updated_at: Timestamp::from_micros_since_unix_epoch(1), + visible: true, }; let mut active_agent_session_ids = HashSet::new(); diff --git a/server-rs/crates/spacetime-module/src/jump_hop.rs b/server-rs/crates/spacetime-module/src/jump_hop.rs index 0209f748..6f73da86 100644 --- a/server-rs/crates/spacetime-module/src/jump_hop.rs +++ b/server-rs/crates/spacetime-module/src/jump_hop.rs @@ -20,6 +20,7 @@ pub fn jump_hop_gallery_view(ctx: &AnonymousViewContext) -> Vec Some(item), Err(error) => { @@ -401,6 +402,7 @@ fn compile_jump_hop_draft_tx( play_count: 0, updated_at: compiled_at, published_at: None, + visible: true, }; upsert_work(ctx, row); replace_session( @@ -1163,6 +1165,7 @@ fn clone_work(row: &JumpHopWorkProfileRow) -> JumpHopWorkProfileRow { play_count: row.play_count, updated_at: row.updated_at, published_at: row.published_at, + visible: row.visible, } } diff --git a/server-rs/crates/spacetime-module/src/jump_hop/tables.rs b/server-rs/crates/spacetime-module/src/jump_hop/tables.rs index 74ef94d6..4b42a444 100644 --- a/server-rs/crates/spacetime-module/src/jump_hop/tables.rs +++ b/server-rs/crates/spacetime-module/src/jump_hop/tables.rs @@ -1,5 +1,7 @@ use crate::*; +const WORK_VISIBLE_DEFAULT: bool = true; + #[spacetimedb::table( accessor = jump_hop_agent_session, index(accessor = by_jump_hop_agent_session_owner_user_id, btree(columns = [owner_user_id])) @@ -51,6 +53,9 @@ pub struct JumpHopWorkProfileRow { pub(crate) play_count: u32, pub(crate) updated_at: Timestamp, pub(crate) published_at: Option, + // ??????????????????????????????? + #[default(WORK_VISIBLE_DEFAULT)] + pub(crate) visible: bool, } #[spacetimedb::table( diff --git a/server-rs/crates/spacetime-module/src/match3d.rs b/server-rs/crates/spacetime-module/src/match3d.rs index b4154fc2..d473c7fd 100644 --- a/server-rs/crates/spacetime-module/src/match3d.rs +++ b/server-rs/crates/spacetime-module/src/match3d.rs @@ -32,6 +32,7 @@ pub fn match3d_gallery_view(ctx: &AnonymousViewContext) -> Vec Some(item), Err(error) => { @@ -571,6 +572,7 @@ fn compile_match3d_draft_tx( updated_at: compiled_at, published_at: previous_published_at, generated_item_assets_json, + visible: true, }; upsert_work(ctx, work); replace_session( @@ -643,6 +645,7 @@ fn build_updated_match3d_work_row( updated_at, published_at: current.published_at, generated_item_assets_json: current.generated_item_assets_json.clone(), + visible: current.visible, }; Ok(next) } @@ -1330,6 +1333,7 @@ fn clone_work(row: &Match3DWorkProfileRow) -> Match3DWorkProfileRow { updated_at: row.updated_at, published_at: row.published_at, generated_item_assets_json: row.generated_item_assets_json.clone(), + visible: row.visible, } } @@ -1885,6 +1889,7 @@ mod tests { updated_at: Timestamp::from_micros_since_unix_epoch(1), published_at: None, generated_item_assets_json: None, + visible: true, }; let snapshot = build_initial_run_snapshot("run-1", &work, 10, None); assert_eq!(snapshot.total_item_count, 12); @@ -1924,6 +1929,7 @@ mod tests { r#"[{"itemId":"match3d-item-1","itemName":"草莓","imageSrc":"/generated-match3d-assets/session/profile/items/item/image.png","status":"image_ready"}]"# .to_string(), ), + visible: true, }; let snapshot = build_work_snapshot(&work).expect("work snapshot should build"); @@ -1969,6 +1975,7 @@ mod tests { r#"[{"itemId":"match3d-item-1","itemName":"草莓","imageSrc":"/generated-match3d-assets/session/profile/items/item/image.png","status":"image_ready"}]"# .to_string(), ), + visible: true, }; let preserved = @@ -2038,6 +2045,7 @@ mod tests { r#"[{"itemId":"match3d-item-1","itemName":"草莓","imageSrc":"/generated-match3d-assets/session/profile/items/item/image.png","status":"image_ready"}]"# .to_string(), ), + visible: true, }; let input = Match3DWorkUpdateInput { profile_id: existing.profile_id.clone(), @@ -2097,6 +2105,7 @@ mod tests { r#"[{"itemId":"match3d-item-1","itemName":"草莓","imageSrc":"/generated-match3d-assets/session/profile/items/item/image.png","status":"image_ready"},{"itemId":"match3d-item-2","itemName":"苹果","imageViews":[{"imageSrc":"/v1.png"},{"imageSrc":"/v2.png"},{"imageSrc":"/v3.png"},{"imageSrc":"/v4.png"},{"imageSrc":"/v5.png"}],"status":"model_ready"},{"itemId":"match3d-item-3","itemName":"香蕉","imageViews":[{"imageSrc":"/v1.png"},{"imageSrc":"/v2.png"},{"imageSrc":"/v3.png"},{"imageSrc":"/v4.png"}],"status":"image_ready"}]"# .to_string(), ), + visible: true, }; let error = validate_publishable_work(&base_work).unwrap_err(); @@ -2156,6 +2165,7 @@ mod tests { updated_at: Timestamp::from_micros_since_unix_epoch(1), published_at: None, generated_item_assets_json: None, + visible: true, }; let input_game_name = None; diff --git a/server-rs/crates/spacetime-module/src/match3d/tables.rs b/server-rs/crates/spacetime-module/src/match3d/tables.rs index 950432cd..14f2b197 100644 --- a/server-rs/crates/spacetime-module/src/match3d/tables.rs +++ b/server-rs/crates/spacetime-module/src/match3d/tables.rs @@ -1,5 +1,7 @@ use crate::*; +const WORK_VISIBLE_DEFAULT: bool = true; + #[spacetimedb::table( accessor = match3d_agent_session, index(accessor = by_match3d_agent_session_owner_user_id, btree(columns = [owner_user_id])) @@ -60,6 +62,9 @@ pub struct Match3DWorkProfileRow { pub(crate) published_at: Option, #[default(None::)] pub(crate) generated_item_assets_json: Option, + // ??????????????????????????????? + #[default(WORK_VISIBLE_DEFAULT)] + pub(crate) visible: bool, } #[spacetimedb::table( diff --git a/server-rs/crates/spacetime-module/src/migration.rs b/server-rs/crates/spacetime-module/src/migration.rs index 443133f5..498d19c4 100644 --- a/server-rs/crates/spacetime-module/src/migration.rs +++ b/server-rs/crates/spacetime-module/src/migration.rs @@ -1257,6 +1257,10 @@ fn normalize_migration_row(table_name: &str, value: &serde_json::Value) -> serde } if table_name == "puzzle_work_profile" { if let Some(object) = next_value.as_object_mut() { + // ??????????????????????????????? + object + .entry("visible".to_string()) + .or_insert_with(|| serde_json::Value::Bool(true)); // 中文注释:拼图公开互动计数晚于基础作品表加入,旧迁移包按 0 兼容。 object .entry("play_count".to_string()) @@ -1294,8 +1298,26 @@ fn normalize_migration_row(table_name: &str, value: &serde_json::Value) -> serde .or_insert(fallback_description); } } + if matches!( + table_name, + "jump_hop_work_profile" + | "square_hole_work_profile" + | "visual_novel_work_profile" + | "bark_battle_published_config" + ) { + if let Some(object) = next_value.as_object_mut() { + // ??????????????????????????????? + object + .entry("visible".to_string()) + .or_insert_with(|| serde_json::Value::Bool(true)); + } + } if table_name == "match3d_work_profile" { if let Some(object) = next_value.as_object_mut() { + // ??????????????????????????????? + object + .entry("visible".to_string()) + .or_insert_with(|| serde_json::Value::Bool(true)); // 中文注释:抓大鹅生成素材字段晚于基础作品表加入,旧迁移包按未生成素材兼容。 object .entry("generated_item_assets_json".to_string()) @@ -1304,6 +1326,10 @@ fn normalize_migration_row(table_name: &str, value: &serde_json::Value) -> serde } if table_name == "wooden_fish_work_profile" { if let Some(object) = next_value.as_object_mut() { + // ??????????????????????????????? + object + .entry("visible".to_string()) + .or_insert_with(|| serde_json::Value::Bool(true)); // 中文注释:敲木鱼背景环境图晚于首版作品表加入,旧迁移包按未生成背景兼容。 object .entry("background_asset_json".to_string()) diff --git a/server-rs/crates/spacetime-module/src/puzzle.rs b/server-rs/crates/spacetime-module/src/puzzle.rs index bb71ba1e..a65cafb3 100644 --- a/server-rs/crates/spacetime-module/src/puzzle.rs +++ b/server-rs/crates/spacetime-module/src/puzzle.rs @@ -38,6 +38,7 @@ use spacetimedb::{ use crate::auth::user_account; const PUZZLE_POINT_INCENTIVE_DEFAULT_U64: u64 = 0; +const WORK_VISIBLE_DEFAULT: bool = true; /// 拼图 Agent session 真相表。 /// 当前只保存结构化字段与 JSON 草稿,不提前拆出更多编辑态子表。 @@ -112,6 +113,9 @@ pub struct PuzzleWorkProfileRow { point_incentive_total_half_points: u64, #[default(PUZZLE_POINT_INCENTIVE_DEFAULT_U64)] point_incentive_claimed_points: u64, + // ??????????????????????????????? + #[default(WORK_VISIBLE_DEFAULT)] + visible: bool, } /// 拼图广场公开详情兼容投影。 @@ -125,6 +129,7 @@ pub fn puzzle_gallery_view(ctx: &AnonymousViewContext) -> Vec .puzzle_work_profile() .by_puzzle_work_publication_status() .filter(PuzzlePublicationStatus::Published) + .filter(|row| row.visible) .filter_map( |row| match build_puzzle_work_profile_from_row_without_recent_count(&row) { Ok(profile) => Some(profile), @@ -154,6 +159,7 @@ pub fn puzzle_gallery_card_view(ctx: &AnonymousViewContext) -> Vec Some(item), Err(error) => { @@ -1578,6 +1584,7 @@ fn update_puzzle_work_tx( created_at: row.created_at, updated_at: Timestamp::from_micros_since_unix_epoch(input.updated_at_micros), published_at: row.published_at, + visible: row.visible, }; replace_puzzle_work_profile(ctx, &row, next_row); sync_puzzle_source_session_draft_from_work(ctx, &row, &preview_draft, input.updated_at_micros)?; @@ -1790,6 +1797,7 @@ fn record_puzzle_work_like_tx( created_at: row.created_at, updated_at: liked_at, published_at: row.published_at, + visible: row.visible, }; replace_puzzle_work_profile(ctx, &row, next_row); ctx.db @@ -1878,6 +1886,7 @@ fn remix_puzzle_work_tx( created_at: source.created_at, updated_at: remixed_at, published_at: source.published_at, + visible: source.visible, }, ); @@ -1945,6 +1954,7 @@ fn remix_puzzle_work_tx( created_at: remixed_at, updated_at: remixed_at, published_at: None, + visible: true, }); get_puzzle_agent_session_tx( @@ -2396,6 +2406,7 @@ fn claim_puzzle_work_point_incentive_tx( created_at: row.created_at, updated_at: claimed_at, published_at: row.published_at, + visible: row.visible, }; replace_puzzle_work_profile(ctx, &row, next_row); @@ -3008,6 +3019,7 @@ fn upsert_puzzle_work_profile(ctx: &TxContext, profile: PuzzleWorkProfile) -> Re published_at: profile .published_at_micros .map(Timestamp::from_micros_since_unix_epoch), + visible: existing.visible, }, ); return Ok(()); @@ -3040,6 +3052,7 @@ fn upsert_puzzle_work_profile(ctx: &TxContext, profile: PuzzleWorkProfile) -> Re published_at: profile .published_at_micros .map(Timestamp::from_micros_since_unix_epoch), + visible: true, }); Ok(()) } @@ -3364,6 +3377,7 @@ fn accrue_puzzle_point_incentive( created_at: row.created_at, updated_at: Timestamp::from_micros_since_unix_epoch(updated_at_micros), published_at: row.published_at, + visible: row.visible, }, ); Ok(()) @@ -3402,6 +3416,7 @@ fn increment_puzzle_profile_play_count( created_at: row.created_at, updated_at: Timestamp::from_micros_since_unix_epoch(updated_at_micros), published_at: row.published_at, + visible: row.visible, }, ); } diff --git a/server-rs/crates/spacetime-module/src/square_hole.rs b/server-rs/crates/spacetime-module/src/square_hole.rs index 4358722a..27f16684 100644 --- a/server-rs/crates/spacetime-module/src/square_hole.rs +++ b/server-rs/crates/spacetime-module/src/square_hole.rs @@ -39,6 +39,7 @@ pub fn square_hole_gallery_view(ctx: &AnonymousViewContext) -> Vec Some(item), Err(error) => { @@ -537,6 +538,7 @@ fn compile_square_hole_draft_tx( play_count: 0, updated_at: compiled_at, published_at: None, + visible: true, }; upsert_work(ctx, work); replace_session( @@ -614,6 +616,7 @@ fn update_square_hole_work_tx( play_count: current.play_count, updated_at, published_at: current.published_at, + visible: current.visible, }; let snapshot = build_work_snapshot(&next)?; replace_work(ctx, ¤t, next); @@ -1141,6 +1144,7 @@ fn clone_work(row: &SquareHoleWorkProfileRow) -> SquareHoleWorkProfileRow { play_count: row.play_count, updated_at: row.updated_at, published_at: row.published_at, + visible: row.visible, } } diff --git a/server-rs/crates/spacetime-module/src/square_hole/tables.rs b/server-rs/crates/spacetime-module/src/square_hole/tables.rs index 59600a8d..e738b896 100644 --- a/server-rs/crates/spacetime-module/src/square_hole/tables.rs +++ b/server-rs/crates/spacetime-module/src/square_hole/tables.rs @@ -1,5 +1,7 @@ use crate::*; +const WORK_VISIBLE_DEFAULT: bool = true; + #[spacetimedb::table( accessor = square_hole_agent_session, index(accessor = by_square_hole_agent_session_owner_user_id, btree(columns = [owner_user_id])) @@ -59,6 +61,9 @@ pub struct SquareHoleWorkProfileRow { pub(crate) play_count: u32, pub(crate) updated_at: Timestamp, pub(crate) published_at: Option, + // ??????????????????????????????? + #[default(WORK_VISIBLE_DEFAULT)] + pub(crate) visible: bool, } #[spacetimedb::table( diff --git a/server-rs/crates/spacetime-module/src/visual_novel.rs b/server-rs/crates/spacetime-module/src/visual_novel.rs index f377e312..0acd0e5b 100644 --- a/server-rs/crates/spacetime-module/src/visual_novel.rs +++ b/server-rs/crates/spacetime-module/src/visual_novel.rs @@ -1,8 +1,9 @@ use crate::*; -use serde::Serialize; use serde::de::DeserializeOwned; use spacetimedb::AnonymousViewContext; +const WORK_VISIBLE_DEFAULT: bool = true; + pub const VISUAL_NOVEL_SOURCE_IDEA: &str = "idea"; pub const VISUAL_NOVEL_SOURCE_DOCUMENT: &str = "document"; pub const VISUAL_NOVEL_SOURCE_BLANK: &str = "blank"; @@ -94,6 +95,9 @@ pub struct VisualNovelWorkProfileRow { pub(crate) created_at: Timestamp, pub(crate) updated_at: Timestamp, pub(crate) published_at: Option, + // ??????????????????????????????? + #[default(WORK_VISIBLE_DEFAULT)] + pub(crate) visible: bool, } /// 视觉小说运行态 run 表。 @@ -178,6 +182,7 @@ pub fn visual_novel_gallery_view(ctx: &AnonymousViewContext) -> Vec Some(item), Err(error) => { @@ -421,13 +426,13 @@ pub struct VisualNovelRuntimeEventProcedureResult { pub error_message: Option, } -#[derive(Clone, Debug, PartialEq, Serialize, SpacetimeType)] +#[derive(Clone, Debug, PartialEq, serde::Serialize, SpacetimeType)] pub struct VisualNovelJsonField { pub key: String, pub value: VisualNovelJsonValue, } -#[derive(Clone, Debug, PartialEq, Serialize, SpacetimeType)] +#[derive(Clone, Debug, PartialEq, serde::Serialize, SpacetimeType)] pub enum VisualNovelJsonValue { Null, Bool(bool), @@ -437,7 +442,7 @@ pub enum VisualNovelJsonValue { Object(Vec), } -#[derive(Clone, Debug, PartialEq, Serialize, SpacetimeType)] +#[derive(Clone, Debug, PartialEq, serde::Serialize, SpacetimeType)] #[serde(rename_all = "camelCase")] pub struct VisualNovelAgentMessageSnapshot { pub message_id: String, @@ -448,7 +453,7 @@ pub struct VisualNovelAgentMessageSnapshot { pub created_at_micros: i64, } -#[derive(Clone, Debug, PartialEq, Serialize, SpacetimeType)] +#[derive(Clone, Debug, PartialEq, serde::Serialize, SpacetimeType)] #[serde(rename_all = "camelCase")] pub struct VisualNovelAgentSessionSnapshot { pub session_id: String, @@ -468,7 +473,7 @@ pub struct VisualNovelAgentSessionSnapshot { pub updated_at_micros: i64, } -#[derive(Clone, Debug, PartialEq, Serialize, SpacetimeType)] +#[derive(Clone, Debug, PartialEq, serde::Serialize, SpacetimeType)] #[serde(rename_all = "camelCase")] pub struct VisualNovelWorkSnapshot { pub work_id: String, @@ -490,7 +495,7 @@ pub struct VisualNovelWorkSnapshot { pub published_at_micros: Option, } -#[derive(Clone, Debug, PartialEq, Serialize, SpacetimeType)] +#[derive(Clone, Debug, PartialEq, serde::Serialize, SpacetimeType)] #[serde(rename_all = "camelCase")] pub struct VisualNovelRuntimeHistoryEntrySnapshot { pub entry_id: String, @@ -506,7 +511,7 @@ pub struct VisualNovelRuntimeHistoryEntrySnapshot { pub created_at_micros: i64, } -#[derive(Clone, Debug, PartialEq, Serialize, SpacetimeType)] +#[derive(Clone, Debug, PartialEq, serde::Serialize, SpacetimeType)] #[serde(rename_all = "camelCase")] pub struct VisualNovelRunSnapshot { pub run_id: String, @@ -526,7 +531,7 @@ pub struct VisualNovelRunSnapshot { pub updated_at_micros: i64, } -#[derive(Clone, Debug, PartialEq, Serialize, SpacetimeType)] +#[derive(Clone, Debug, PartialEq, serde::Serialize, SpacetimeType)] #[serde(rename_all = "camelCase")] pub struct VisualNovelRuntimeEventSnapshot { pub event_id: String, @@ -1029,6 +1034,7 @@ fn compile_visual_novel_work_profile_tx( created_at: compiled_at, updated_at: compiled_at, published_at: None, + visible: true, }; upsert_work(ctx, work); replace_session( @@ -1731,6 +1737,7 @@ fn clone_work(row: &VisualNovelWorkProfileRow) -> VisualNovelWorkProfileRow { created_at: row.created_at, updated_at: row.updated_at, published_at: row.published_at, + visible: row.visible, } } @@ -1971,7 +1978,7 @@ fn parse_json(value: &str, label: &str) -> Result(value: &T) -> String { +fn to_json_string(value: &T) -> String { serde_json::to_string(value).unwrap_or_else(|_| "{}".to_string()) } diff --git a/server-rs/crates/spacetime-module/src/wooden_fish.rs b/server-rs/crates/spacetime-module/src/wooden_fish.rs index a8ef6954..e44683bb 100644 --- a/server-rs/crates/spacetime-module/src/wooden_fish.rs +++ b/server-rs/crates/spacetime-module/src/wooden_fish.rs @@ -20,6 +20,7 @@ pub fn wooden_fish_gallery_view(ctx: &AnonymousViewContext) -> Vec Some(item), Err(error) => { @@ -408,6 +409,7 @@ fn compile_wooden_fish_draft_tx( published_at: None, background_asset_json: background_asset.as_ref().map(to_json_string), back_button_asset_json: back_button_asset.as_ref().map(to_json_string), + visible: true, }; upsert_work(ctx, row); let config = config_from_draft(&draft); @@ -1269,6 +1271,7 @@ fn clone_work(row: &WoodenFishWorkProfileRow) -> WoodenFishWorkProfileRow { play_count: row.play_count, updated_at: row.updated_at, published_at: row.published_at, + visible: row.visible, } } diff --git a/server-rs/crates/spacetime-module/src/wooden_fish/tables.rs b/server-rs/crates/spacetime-module/src/wooden_fish/tables.rs index 74268598..0002f76b 100644 --- a/server-rs/crates/spacetime-module/src/wooden_fish/tables.rs +++ b/server-rs/crates/spacetime-module/src/wooden_fish/tables.rs @@ -1,5 +1,7 @@ use crate::*; +const WORK_VISIBLE_DEFAULT: bool = true; + #[spacetimedb::table( accessor = wooden_fish_agent_session, index(accessor = by_wooden_fish_agent_session_owner_user_id, btree(columns = [owner_user_id])) @@ -49,6 +51,9 @@ pub struct WoodenFishWorkProfileRow { pub(crate) background_asset_json: Option, #[default(None::)] pub(crate) back_button_asset_json: Option, + // ??????????????????????????????? + #[default(WORK_VISIBLE_DEFAULT)] + pub(crate) visible: bool, } #[spacetimedb::table(