From 9d9913095d6a45fc8dcdd53737aaf7cb3597ad42 Mon Sep 17 00:00:00 2001 From: kdletters <61648117+kdletters@users.noreply.github.com> Date: Sat, 2 May 2026 00:27:22 +0800 Subject: [PATCH] Close DDD refactor and remove generated asset proxy --- ...RESULT_MODAL_AND_LEADERBOARD_2026-04-26.md | 2 + docs/technical/README.md | 20 +- .../RUST_API_SERVER_ROUTE_INDEX_2026-04-22.md | 7 +- ...G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md | 20 +- ...VER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md | 272 ++- ...ER_RS_DDD_WP_API_BFF_CLOSURE_2026-05-01.md | 65 + ...DD_WP_AS_ASSET_CHAIN_CLOSURE_2026-05-01.md | 40 + ...DDD_WP_CW_FULL_CHAIN_CLOSURE_2026-05-01.md | 98 + ...SERVER_RS_DDD_WP_DEL_CLEANUP_2026-05-01.md | 80 + ...G_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md | 12 +- ...NTIME_STORY_HOOKS_PROJECTION_2026-04-29.md | 19 +- ...NTIME_STORY_CLIENT_MIGRATION_2026-04-29.md | 25 +- ...UNTIME_BACKEND_TRUTH_CLOSURE_2026-05-01.md | 76 + ..._DDD_WP_RPG_GAMEPLAY_CLOSURE_2026-05-01.md | 38 + ...D_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md | 17 +- ..._WP_RS_RUNTIME_STORY_CLOSURE_2026-05-01.md | 47 + ...SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md | 35 +- .../SERVER_RS_DDD_WP_ST_CLOSURE_2026-05-01.md | 66 + docs/technical/SPACETIMEDB_TABLE_CATALOG.md | 81 +- .../src/contracts/puzzleRuntimeSession.ts | 5 - .../src/contracts/rpgRuntimeContracts.test.ts | 8 +- .../src/contracts/rpgRuntimeStoryAction.ts | 20 - .../src/contracts/rpgRuntimeStoryState.ts | 61 +- packages/shared/src/contracts/story.ts | 31 + scripts/check-server-rs-ddd-boundaries.mjs | 118 ++ scripts/deploy-rust-remote.sh | 6 - scripts/generate-spacetime-bindings.mjs | 87 +- server-rs/Cargo.lock | 5 + server-rs/README.md | 2 +- server-rs/crates/api-server/README.md | 24 +- server-rs/crates/api-server/src/app.rs | 182 +- .../crates/api-server/src/custom_world_ai.rs | 48 +- .../api-server/src/legacy_generated_assets.rs | 246 --- server-rs/crates/api-server/src/llm.rs | 155 +- server-rs/crates/api-server/src/main.rs | 1 - server-rs/crates/api-server/src/puzzle.rs | 481 +---- .../crates/api-server/src/story_sessions.rs | 636 ++++++- server-rs/crates/module-assets/README.md | 14 +- server-rs/crates/module-assets/src/events.rs | 41 +- server-rs/crates/module-assets/src/lib.rs | 1 + server-rs/crates/module-puzzle/README.md | 8 +- .../crates/module-runtime-story/README.md | 4 +- .../module-runtime-story/src/application.rs | 18 +- .../module-runtime-story/src/battle_tests.rs | 1 - .../module-runtime-story/src/bootstrap.rs | 1085 ++++++++++++ .../crates/module-runtime-story/src/lib.rs | 13 + .../module-runtime-story/src/projection.rs | 2 + .../src/session_action.rs | 1578 +++++++++++++++++ server-rs/crates/module-story/Cargo.toml | 14 +- server-rs/crates/module-story/README.md | 7 +- .../crates/module-story/src/application.rs | 296 ++++ server-rs/crates/module-story/src/errors.rs | 2 + server-rs/crates/module-story/src/lib.rs | 173 ++ server-rs/crates/shared-contracts/README.md | 14 +- .../shared-contracts/src/puzzle_runtime.rs | 8 - .../shared-contracts/src/runtime_story.rs | 481 ++--- .../crates/shared-contracts/src/story.rs | 114 ++ server-rs/crates/spacetime-client/README.md | 36 +- server-rs/crates/spacetime-client/src/ai.rs | 4 +- .../crates/spacetime-client/src/combat.rs | 7 +- .../crates/spacetime-client/src/inventory.rs | 2 +- server-rs/crates/spacetime-client/src/lib.rs | 45 + .../module_bindings/accept_quest_reducer.rs | 38 +- .../acknowledge_quest_completion_reducer.rs | 38 +- ...n_disable_profile_redeem_code_procedure.rs | 39 +- ...in_upsert_profile_redeem_code_procedure.rs | 39 +- .../advance_puzzle_next_level_procedure.rs | 39 +- .../ai_result_reference_input_type.rs | 11 +- .../ai_result_reference_kind_type.rs | 11 +- .../ai_result_reference_snapshot_type.rs | 11 +- .../ai_result_reference_type.rs | 26 +- .../ai_stage_completion_input_type.rs | 15 +- .../ai_task_cancel_input_type.rs | 10 +- .../ai_task_create_input_type.rs | 15 +- .../ai_task_event_kind_type.rs | 11 +- .../module_bindings/ai_task_event_table.rs | 58 +- .../src/module_bindings/ai_task_event_type.rs | 35 +- .../ai_task_failure_input_type.rs | 10 +- .../ai_task_finish_input_type.rs | 10 +- .../src/module_bindings/ai_task_kind_type.rs | 11 +- .../ai_task_procedure_result_type.rs | 15 +- .../module_bindings/ai_task_snapshot_type.rs | 33 +- .../ai_task_stage_blueprint_type.rs | 9 +- .../ai_task_stage_kind_type.rs | 11 +- .../ai_task_stage_snapshot_type.rs | 19 +- .../ai_task_stage_start_input_type.rs | 9 +- .../ai_task_stage_status_type.rs | 11 +- .../src/module_bindings/ai_task_stage_type.rs | 37 +- .../ai_task_start_input_type.rs | 10 +- .../module_bindings/ai_task_status_type.rs | 11 +- .../src/module_bindings/ai_task_type.rs | 50 +- .../ai_text_chunk_append_input_type.rs | 9 +- .../ai_text_chunk_snapshot_type.rs | 9 +- .../src/module_bindings/ai_text_chunk_type.rs | 12 +- ...pend_ai_text_chunk_and_return_procedure.rs | 41 +- ...ssion_ledger_entry_and_return_procedure.rs | 42 +- ...hapter_progression_ledger_entry_reducer.rs | 43 +- .../apply_inventory_mutation_reducer.rs | 38 +- .../apply_quest_signal_reducer.rs | 38 +- .../asset_entity_binding_input_type.rs | 14 +- ...et_entity_binding_procedure_result_type.rs | 13 +- .../asset_entity_binding_snapshot_type.rs | 14 +- .../asset_entity_binding_type.rs | 21 +- .../module_bindings/asset_event_kind_type.rs | 18 + .../src/module_bindings/asset_event_table.rs | 99 ++ .../src/module_bindings/asset_event_type.rs | 85 + .../asset_history_entry_snapshot_type.rs | 16 +- .../asset_history_list_input_type.rs | 10 +- .../asset_history_list_result_type.rs | 13 +- .../asset_object_access_policy_type.rs | 11 +- .../asset_object_procedure_result_type.rs | 13 +- .../src/module_bindings/asset_object_type.rs | 36 +- .../asset_object_upsert_input_type.rs | 21 +- .../asset_object_upsert_snapshot_type.rs | 21 +- ...i_result_reference_and_return_procedure.rs | 41 +- .../src/module_bindings/auth_identity_type.rs | 29 +- ...e_snapshot_import_procedure_result_type.rs | 13 +- .../auth_store_snapshot_import_record_type.rs | 10 +- ...th_store_snapshot_procedure_result_type.rs | 13 +- .../auth_store_snapshot_record_type.rs | 14 +- .../auth_store_snapshot_type.rs | 13 +- .../auth_store_snapshot_upsert_input_type.rs | 10 +- ...e_database_migration_operator_procedure.rs | 42 +- .../src/module_bindings/battle_mode_type.rs | 11 +- .../battle_state_input_type.rs | 13 +- .../battle_state_procedure_result_type.rs | 13 +- .../battle_state_query_input_type.rs | 10 +- .../battle_state_snapshot_type.rs | 21 +- .../src/module_bindings/battle_state_type.rs | 44 +- .../src/module_bindings/battle_status_type.rs | 11 +- ...egin_story_session_and_return_procedure.rs | 39 +- .../begin_story_session_reducer.rs | 38 +- .../big_fish_agent_message_kind_type.rs | 11 +- .../big_fish_agent_message_role_type.rs | 11 +- .../big_fish_agent_message_snapshot_type.rs | 11 +- .../big_fish_agent_message_type.rs | 14 +- .../big_fish_anchor_item_type.rs | 9 +- .../big_fish_anchor_pack_type.rs | 9 +- .../big_fish_anchor_status_type.rs | 11 +- .../big_fish_asset_coverage_type.rs | 12 +- .../big_fish_asset_generate_input_type.rs | 15 +- .../big_fish_asset_kind_type.rs | 11 +- .../big_fish_asset_slot_snapshot_type.rs | 15 +- .../big_fish_asset_slot_type.rs | 24 +- .../big_fish_asset_status_type.rs | 11 +- .../big_fish_background_blueprint_type.rs | 10 +- .../big_fish_creation_session_type.rs | 30 +- .../big_fish_creation_stage_type.rs | 11 +- .../big_fish_draft_compile_input_type.rs | 12 +- .../big_fish_event_kind_type.rs | 11 +- .../module_bindings/big_fish_event_table.rs | 56 +- .../module_bindings/big_fish_event_type.rs | 12 +- .../big_fish_game_draft_type.rs | 13 +- .../big_fish_input_submit_input_type.rs | 10 +- .../big_fish_level_blueprint_type.rs | 16 +- .../big_fish_message_finalize_input_type.rs | 15 +- .../big_fish_message_submit_input_type.rs | 10 +- .../big_fish_play_record_input_type.rs | 10 +- .../big_fish_publish_input_type.rs | 10 +- .../big_fish_run_get_input_type.rs | 10 +- .../big_fish_run_procedure_result_type.rs | 14 +- .../big_fish_run_start_input_type.rs | 10 +- .../big_fish_run_status_type.rs | 11 +- .../big_fish_runtime_params_type.rs | 14 +- .../big_fish_runtime_run_type.rs | 12 +- .../big_fish_session_create_input_type.rs | 10 +- .../big_fish_session_get_input_type.rs | 10 +- .../big_fish_session_procedure_result_type.rs | 13 +- .../big_fish_session_snapshot_type.rs | 27 +- .../big_fish_work_delete_input_type.rs | 10 +- .../big_fish_works_list_input_type.rs | 10 +- .../big_fish_works_procedure_result_type.rs | 14 +- ...t_object_to_entity_and_return_procedure.rs | 39 +- .../bind_asset_object_to_entity_reducer.rs | 38 +- .../cancel_ai_task_and_return_procedure.rs | 41 +- .../module_bindings/chapter_pace_band_type.rs | 11 +- .../chapter_progression_get_input_type.rs | 10 +- .../chapter_progression_input_type.rs | 9 +- .../chapter_progression_ledger_input_type.rs | 12 +- ...apter_progression_procedure_result_type.rs | 13 +- .../chapter_progression_snapshot_type.rs | 11 +- .../chapter_progression_type.rs | 46 +- ...orm_browse_history_and_return_procedure.rs | 39 +- .../module_bindings/combat_outcome_type.rs | 11 +- .../compile_big_fish_draft_procedure.rs | 39 +- ...ustom_world_published_profile_procedure.rs | 42 +- .../compile_puzzle_agent_draft_procedure.rs | 41 +- .../complete_ai_stage_and_return_procedure.rs | 41 +- .../complete_ai_task_and_return_procedure.rs | 41 +- ...nfirm_asset_object_and_return_procedure.rs | 41 +- .../confirm_asset_object_reducer.rs | 38 +- .../consume_inventory_item_input_type.rs | 10 +- ...file_wallet_points_and_return_procedure.rs | 39 +- .../continue_story_and_return_procedure.rs | 41 +- .../module_bindings/continue_story_reducer.rs | 38 +- .../create_ai_task_and_return_procedure.rs | 41 +- .../module_bindings/create_ai_task_reducer.rs | 38 +- ...reate_battle_state_and_return_procedure.rs | 39 +- .../create_battle_state_reducer.rs | 38 +- .../create_big_fish_session_procedure.rs | 41 +- ...te_custom_world_agent_session_procedure.rs | 39 +- ...ile_recharge_order_and_return_procedure.rs | 44 +- .../create_puzzle_agent_session_procedure.rs | 41 +- ...m_world_agent_action_execute_input_type.rs | 12 +- ..._world_agent_action_execute_result_type.rs | 13 +- ..._world_agent_card_detail_get_input_type.rs | 10 +- ...world_agent_message_finalize_input_type.rs | 25 +- ...ustom_world_agent_message_snapshot_type.rs | 13 +- ...m_world_agent_message_submit_input_type.rs | 10 +- .../custom_world_agent_message_type.rs | 23 +- ...om_world_agent_operation_get_input_type.rs | 10 +- ...d_agent_operation_procedure_result_type.rs | 13 +- ...rld_agent_operation_progress_input_type.rs | 13 +- ...tom_world_agent_operation_snapshot_type.rs | 13 +- .../custom_world_agent_operation_type.rs | 21 +- ...m_world_agent_session_create_input_type.rs | 18 +- ...stom_world_agent_session_get_input_type.rs | 10 +- ...rld_agent_session_procedure_result_type.rs | 13 +- ...ustom_world_agent_session_snapshot_type.rs | 35 +- .../custom_world_agent_session_type.rs | 94 +- ...tom_world_draft_card_detail_result_type.rs | 13 +- ...draft_card_detail_section_snapshot_type.rs | 10 +- ...m_world_draft_card_detail_snapshot_type.rs | 19 +- .../custom_world_draft_card_snapshot_type.rs | 17 +- .../custom_world_draft_card_type.rs | 32 +- ...world_gallery_detail_by_code_input_type.rs | 10 +- .../custom_world_gallery_detail_input_type.rs | 10 +- ...ustom_world_gallery_entry_snapshot_type.rs | 11 +- .../custom_world_gallery_entry_table.rs | 113 +- .../custom_world_gallery_entry_type.rs | 26 +- .../custom_world_gallery_list_result_type.rs | 13 +- .../custom_world_generation_mode_type.rs | 11 +- .../custom_world_library_detail_input_type.rs | 10 +- ...stom_world_library_mutation_result_type.rs | 17 +- .../custom_world_profile_delete_input_type.rs | 10 +- .../custom_world_profile_list_input_type.rs | 10 +- .../custom_world_profile_list_result_type.rs | 13 +- ...custom_world_profile_publish_input_type.rs | 12 +- .../custom_world_profile_snapshot_type.rs | 23 +- .../custom_world_profile_type.rs | 69 +- ...stom_world_profile_unpublish_input_type.rs | 10 +- .../custom_world_profile_upsert_input_type.rs | 17 +- .../custom_world_publication_status_type.rs | 11 +- .../custom_world_publish_world_input_type.rs | 14 +- .../custom_world_publish_world_result_type.rs | 23 +- ...ld_published_profile_compile_input_type.rs | 12 +- ...d_published_profile_compile_result_type.rs | 13 +- ...published_profile_compile_snapshot_type.rs | 11 +- .../custom_world_role_asset_status_type.rs | 11 +- .../custom_world_session_status_type.rs | 11 +- .../custom_world_session_type.rs | 39 +- .../custom_world_theme_mode_type.rs | 11 +- ...custom_world_work_summary_snapshot_type.rs | 29 +- .../custom_world_works_list_input_type.rs | 10 +- .../custom_world_works_list_result_type.rs | 13 +- ...migration_authorize_operator_input_type.rs | 10 +- .../database_migration_export_input_type.rs | 12 +- .../database_migration_import_input_type.rs | 12 +- ...igration_operator_procedure_result_type.rs | 14 +- .../database_migration_operator_type.rs | 16 +- ...atabase_migration_procedure_result_type.rs | 15 +- ...se_migration_revoke_operator_input_type.rs | 10 +- .../database_migration_table_stat_type.rs | 10 +- .../delete_big_fish_work_procedure.rs | 39 +- ...te_custom_world_agent_session_procedure.rs | 39 +- ...stom_world_profile_and_return_procedure.rs | 39 +- .../delete_puzzle_work_procedure.rs | 39 +- ...e_runtime_snapshot_and_return_procedure.rs | 39 +- .../drag_puzzle_piece_or_group_procedure.rs | 41 +- .../equip_inventory_item_input_type.rs | 10 +- ...ute_custom_world_agent_action_procedure.rs | 39 +- ...th_store_snapshot_from_tables_procedure.rs | 43 +- ...rt_database_migration_to_file_procedure.rs | 39 +- .../fail_ai_task_and_return_procedure.rs | 41 +- ...e_big_fish_agent_message_turn_procedure.rs | 41 +- ...stom_world_agent_message_turn_procedure.rs | 42 +- ...ize_puzzle_agent_message_turn_procedure.rs | 41 +- .../generate_big_fish_asset_procedure.rs | 41 +- .../get_auth_store_snapshot_procedure.rs | 43 +- .../get_battle_state_procedure.rs | 39 +- .../get_big_fish_run_procedure.rs | 39 +- .../get_big_fish_session_procedure.rs | 41 +- .../get_chapter_progression_procedure.rs | 41 +- ...ustom_world_agent_card_detail_procedure.rs | 39 +- ..._custom_world_agent_operation_procedure.rs | 41 +- ...et_custom_world_agent_session_procedure.rs | 41 +- ..._world_gallery_detail_by_code_procedure.rs | 41 +- ...t_custom_world_gallery_detail_procedure.rs | 39 +- ...t_custom_world_library_detail_procedure.rs | 41 +- ...player_progression_or_default_procedure.rs | 39 +- .../get_profile_dashboard_procedure.rs | 39 +- .../get_profile_play_stats_procedure.rs | 39 +- .../get_profile_recharge_center_procedure.rs | 41 +- ...rofile_referral_invite_center_procedure.rs | 39 +- .../get_puzzle_agent_session_procedure.rs | 41 +- .../get_puzzle_gallery_detail_procedure.rs | 39 +- .../get_puzzle_run_procedure.rs | 41 +- .../get_puzzle_work_detail_procedure.rs | 39 +- .../get_runtime_inventory_state_procedure.rs | 41 +- ...et_runtime_setting_or_default_procedure.rs | 39 +- .../get_runtime_snapshot_procedure.rs | 41 +- .../get_story_session_state_procedure.rs | 39 +- .../grant_inventory_item_input_type.rs | 9 +- ...ression_experience_and_return_procedure.rs | 41 +- ...t_player_progression_experience_reducer.rs | 41 +- .../import_auth_store_snapshot_procedure.rs | 43 +- ..._database_migration_from_file_procedure.rs | 41 +- ...gration_incremental_from_file_procedure.rs | 41 +- .../inventory_container_kind_type.rs | 11 +- .../inventory_equipment_slot_type.rs | 11 +- .../inventory_item_rarity_type.rs | 11 +- .../inventory_item_snapshot_type.rs | 19 +- .../inventory_item_source_kind_type.rs | 11 +- .../inventory_mutation_input_type.rs | 11 +- .../inventory_mutation_type.rs | 13 +- .../inventory_slot_snapshot_type.rs | 25 +- .../module_bindings/inventory_slot_type.rs | 49 +- ...list_asset_history_and_return_procedure.rs | 39 +- .../list_big_fish_works_procedure.rs | 41 +- ..._custom_world_gallery_entries_procedure.rs | 43 +- .../list_custom_world_profiles_procedure.rs | 41 +- .../list_custom_world_works_procedure.rs | 41 +- .../list_platform_browse_history_procedure.rs | 41 +- .../list_profile_save_archives_procedure.rs | 39 +- .../list_profile_wallet_ledger_procedure.rs | 39 +- .../list_puzzle_gallery_procedure.rs | 43 +- .../list_puzzle_works_procedure.rs | 41 +- .../src/module_bindings/mod.rs | 960 +++++----- ...attle_interaction_procedure_result_type.rs | 13 +- .../npc_battle_interaction_result_type.rs | 9 +- .../npc_interaction_battle_mode_type.rs | 11 +- .../npc_interaction_procedure_result_type.rs | 13 +- .../npc_interaction_result_type.rs | 17 +- .../npc_interaction_status_type.rs | 11 +- .../npc_relation_stance_type.rs | 11 +- .../npc_relation_state_type.rs | 9 +- .../npc_social_action_kind_type.rs | 11 +- .../npc_stance_profile_type.rs | 16 +- .../npc_state_procedure_result_type.rs | 13 +- .../npc_state_snapshot_type.rs | 17 +- .../src/module_bindings/npc_state_type.rs | 53 +- .../npc_state_upsert_input_type.rs | 19 +- .../player_progression_get_input_type.rs | 10 +- .../player_progression_grant_input_type.rs | 9 +- .../player_progression_grant_source_type.rs | 11 +- ...layer_progression_procedure_result_type.rs | 13 +- .../player_progression_snapshot_type.rs | 11 +- .../player_progression_type.rs | 22 +- .../profile_dashboard_state_type.rs | 13 +- .../profile_invite_code_type.rs | 13 +- .../profile_membership_type.rs | 12 +- .../profile_played_world_type.rs | 30 +- .../profile_recharge_order_type.rs | 27 +- .../profile_redeem_code_type.rs | 16 +- .../profile_redeem_code_usage_type.rs | 13 +- .../profile_referral_relation_type.rs | 23 +- .../profile_save_archive_type.rs | 33 +- .../profile_wallet_ledger_type.rs | 15 +- .../publish_big_fish_game_procedure.rs | 41 +- ...stom_world_profile_and_return_procedure.rs | 39 +- .../publish_custom_world_profile_reducer.rs | 41 +- .../publish_custom_world_world_procedure.rs | 39 +- .../publish_puzzle_work_procedure.rs | 41 +- ...uzzle_agent_message_finalize_input_type.rs | 15 +- .../puzzle_agent_message_kind_type.rs | 11 +- .../puzzle_agent_message_role_type.rs | 11 +- .../puzzle_agent_message_row_type.rs | 14 +- .../puzzle_agent_message_submit_input_type.rs | 10 +- .../puzzle_agent_session_create_input_type.rs | 10 +- .../puzzle_agent_session_get_input_type.rs | 10 +- ...zle_agent_session_procedure_result_type.rs | 14 +- .../puzzle_agent_session_row_type.rs | 34 +- .../puzzle_agent_stage_type.rs | 11 +- .../puzzle_draft_compile_input_type.rs | 10 +- .../module_bindings/puzzle_event_kind_type.rs | 11 +- .../src/module_bindings/puzzle_event_table.rs | 56 +- .../src/module_bindings/puzzle_event_type.rs | 16 +- ...puzzle_generated_images_save_input_type.rs | 10 +- .../puzzle_leaderboard_entry_row_type.rs | 13 +- .../puzzle_leaderboard_submit_input_type.rs | 10 +- .../puzzle_publication_status_type.rs | 11 +- .../puzzle_publish_input_type.rs | 16 +- .../puzzle_run_drag_input_type.rs | 10 +- .../puzzle_run_get_input_type.rs | 10 +- .../puzzle_run_next_level_input_type.rs | 10 +- .../puzzle_run_procedure_result_type.rs | 14 +- .../puzzle_run_start_input_type.rs | 10 +- .../puzzle_run_swap_input_type.rs | 10 +- .../puzzle_runtime_run_row_type.rs | 33 +- .../puzzle_select_cover_image_input_type.rs | 10 +- .../puzzle_work_delete_input_type.rs | 10 +- .../puzzle_work_get_input_type.rs | 10 +- .../puzzle_work_procedure_result_type.rs | 14 +- .../puzzle_work_profile_row_type.rs | 44 +- .../puzzle_work_upsert_input_type.rs | 16 +- .../puzzle_works_list_input_type.rs | 10 +- .../puzzle_works_procedure_result_type.rs | 14 +- .../quest_completion_ack_input_type.rs | 10 +- .../quest_hostile_npc_defeated_signal_type.rs | 12 +- .../quest_item_delivered_signal_type.rs | 10 +- .../quest_log_event_kind_type.rs | 11 +- .../src/module_bindings/quest_log_type.rs | 37 +- .../quest_narrative_binding_snapshot_type.rs | 11 +- .../quest_narrative_origin_type.rs | 11 +- .../quest_narrative_type_type.rs | 11 +- .../quest_npc_spar_completed_signal_type.rs | 10 +- .../quest_npc_talk_completed_signal_type.rs | 10 +- .../quest_objective_kind_type.rs | 11 +- .../quest_objective_snapshot_type.rs | 17 +- .../quest_progress_signal_type.rs | 15 +- .../quest_record_input_type.rs | 37 +- .../src/module_bindings/quest_record_type.rs | 94 +- .../quest_reward_equipment_slot_type.rs | 11 +- .../quest_reward_intel_type.rs | 12 +- .../quest_reward_item_rarity_type.rs | 11 +- .../module_bindings/quest_reward_item_type.rs | 17 +- .../quest_reward_snapshot_type.rs | 19 +- .../quest_scene_reached_signal_type.rs | 10 +- .../quest_signal_apply_input_type.rs | 9 +- .../module_bindings/quest_signal_kind_type.rs | 11 +- .../src/module_bindings/quest_status_type.rs | 11 +- .../quest_step_snapshot_type.rs | 17 +- .../quest_treasure_inspected_signal_type.rs | 12 +- .../quest_turn_in_input_type.rs | 10 +- .../record_big_fish_play_procedure.rs | 41 +- ..._profile_referral_invite_code_procedure.rs | 39 +- .../redeem_profile_reward_code_procedure.rs | 39 +- .../module_bindings/refresh_session_type.rs | 22 +- ...file_wallet_points_and_return_procedure.rs | 39 +- ...olve_combat_action_and_return_procedure.rs | 39 +- .../resolve_combat_action_input_type.rs | 10 +- ...lve_combat_action_procedure_result_type.rs | 13 +- .../resolve_combat_action_reducer.rs | 38 +- .../resolve_combat_action_result_type.rs | 11 +- ...battle_interaction_and_return_procedure.rs | 41 +- ...solve_npc_battle_interaction_input_type.rs | 15 +- ...ve_npc_interaction_and_return_procedure.rs | 41 +- .../resolve_npc_interaction_input_type.rs | 12 +- .../resolve_npc_interaction_reducer.rs | 38 +- ..._npc_social_action_and_return_procedure.rs | 41 +- .../resolve_npc_social_action_input_type.rs | 13 +- .../resolve_npc_social_action_reducer.rs | 38 +- ...easure_interaction_and_return_procedure.rs | 41 +- .../resolve_treasure_interaction_reducer.rs | 38 +- ...ofile_save_archive_and_return_procedure.rs | 39 +- ...e_database_migration_operator_procedure.rs | 39 +- .../rpg_agent_draft_card_kind_type.rs | 11 +- .../rpg_agent_draft_card_status_type.rs | 11 +- .../rpg_agent_message_kind_type.rs | 11 +- .../rpg_agent_message_role_type.rs | 11 +- .../rpg_agent_operation_status_type.rs | 11 +- .../rpg_agent_operation_type_type.rs | 11 +- .../module_bindings/rpg_agent_stage_type.rs | 11 +- ...runtime_browse_history_clear_input_type.rs | 10 +- .../runtime_browse_history_list_input_type.rs | 10 +- ...me_browse_history_procedure_result_type.rs | 13 +- .../runtime_browse_history_snapshot_type.rs | 11 +- .../runtime_browse_history_sync_input_type.rs | 11 +- .../runtime_browse_history_theme_mode_type.rs | 11 +- ...runtime_browse_history_write_input_type.rs | 22 +- ...e_inventory_state_procedure_result_type.rs | 13 +- ...untime_inventory_state_query_input_type.rs | 10 +- .../runtime_inventory_state_snapshot_type.rs | 13 +- .../runtime_item_equipment_slot_type.rs | 11 +- .../runtime_item_reward_item_rarity_type.rs | 11 +- .../runtime_item_reward_item_snapshot_type.rs | 17 +- .../runtime_platform_theme_type.rs | 11 +- ...untime_profile_dashboard_get_input_type.rs | 10 +- ...profile_dashboard_procedure_result_type.rs | 13 +- ...runtime_profile_dashboard_snapshot_type.rs | 12 +- ...rofile_membership_benefit_snapshot_type.rs | 10 +- ...untime_profile_membership_snapshot_type.rs | 15 +- .../runtime_profile_membership_status_type.rs | 11 +- .../runtime_profile_membership_tier_type.rs | 11 +- ...ntime_profile_play_stats_get_input_type.rs | 10 +- ...rofile_play_stats_procedure_result_type.rs | 13 +- ...untime_profile_play_stats_snapshot_type.rs | 13 +- ...time_profile_played_world_snapshot_type.rs | 16 +- ..._profile_recharge_center_get_input_type.rs | 10 +- ...e_recharge_center_procedure_result_type.rs | 15 +- ...e_profile_recharge_center_snapshot_type.rs | 21 +- ...rofile_recharge_order_create_input_type.rs | 10 +- ...me_profile_recharge_order_snapshot_type.rs | 13 +- ...time_profile_recharge_order_status_type.rs | 11 +- ...time_profile_recharge_product_kind_type.rs | 11 +- ..._profile_recharge_product_snapshot_type.rs | 9 +- ...le_redeem_code_admin_disable_input_type.rs | 10 +- ...redeem_code_admin_procedure_result_type.rs | 13 +- ...ile_redeem_code_admin_upsert_input_type.rs | 13 +- .../runtime_profile_redeem_code_mode_type.rs | 11 +- ...ntime_profile_redeem_code_snapshot_type.rs | 11 +- ...e_profile_reward_code_redeem_input_type.rs | 10 +- ...eward_code_redeem_procedure_result_type.rs | 13 +- ...rofile_reward_code_redeem_snapshot_type.rs | 9 +- ...me_profile_save_archive_list_input_type.rs | 10 +- ...file_save_archive_procedure_result_type.rs | 19 +- ..._profile_save_archive_resume_input_type.rs | 10 +- ...time_profile_save_archive_snapshot_type.rs | 20 +- ...me_profile_wallet_adjustment_input_type.rs | 10 +- ...wallet_adjustment_procedure_result_type.rs | 13 +- ...ofile_wallet_ledger_entry_snapshot_type.rs | 9 +- ...e_profile_wallet_ledger_list_input_type.rs | 10 +- ...ile_wallet_ledger_procedure_result_type.rs | 13 +- ..._profile_wallet_ledger_source_type_type.rs | 11 +- ...e_referral_invite_center_get_input_type.rs | 10 +- ...ral_invite_center_procedure_result_type.rs | 13 +- ...me_referral_invite_center_snapshot_type.rs | 14 +- .../runtime_referral_redeem_input_type.rs | 10 +- ...e_referral_redeem_procedure_result_type.rs | 13 +- .../runtime_referral_redeem_snapshot_type.rs | 9 +- .../runtime_setting_get_input_type.rs | 10 +- .../runtime_setting_procedure_result_type.rs | 13 +- .../runtime_setting_snapshot_type.rs | 9 +- .../module_bindings/runtime_setting_type.rs | 12 +- .../runtime_setting_upsert_input_type.rs | 9 +- .../runtime_snapshot_delete_input_type.rs | 10 +- .../runtime_snapshot_get_input_type.rs | 10 +- .../runtime_snapshot_procedure_result_type.rs | 13 +- .../runtime_snapshot_row_type.rs | 17 +- .../module_bindings/runtime_snapshot_type.rs | 12 +- .../runtime_snapshot_upsert_input_type.rs | 12 +- .../save_puzzle_generated_images_procedure.rs | 39 +- .../select_puzzle_cover_image_procedure.rs | 39 +- .../module_bindings/start_ai_task_reducer.rs | 38 +- .../start_ai_task_stage_reducer.rs | 38 +- .../start_big_fish_run_procedure.rs | 39 +- .../start_puzzle_run_procedure.rs | 39 +- .../story_continue_input_type.rs | 12 +- .../module_bindings/story_event_kind_type.rs | 11 +- .../story_event_snapshot_type.rs | 11 +- .../src/module_bindings/story_event_type.rs | 16 +- .../story_session_input_type.rs | 12 +- .../story_session_procedure_result_type.rs | 17 +- .../story_session_snapshot_type.rs | 13 +- .../story_session_state_input_type.rs | 10 +- ...ory_session_state_procedure_result_type.rs | 17 +- .../story_session_status_type.rs | 11 +- .../src/module_bindings/story_session_type.rs | 35 +- .../submit_big_fish_input_procedure.rs | 41 +- .../submit_big_fish_message_procedure.rs | 41 +- ...it_custom_world_agent_message_procedure.rs | 41 +- .../submit_puzzle_agent_message_procedure.rs | 41 +- ...bmit_puzzle_leaderboard_entry_procedure.rs | 41 +- .../swap_puzzle_pieces_procedure.rs | 39 +- .../treasure_interaction_action_type.rs | 11 +- .../treasure_record_procedure_result_type.rs | 13 +- .../treasure_record_snapshot_type.rs | 17 +- .../module_bindings/treasure_record_type.rs | 39 +- .../treasure_resolve_input_type.rs | 17 +- .../module_bindings/turn_in_quest_reducer.rs | 38 +- .../unequip_inventory_item_input_type.rs | 10 +- ...stom_world_profile_and_return_procedure.rs | 39 +- .../unpublish_custom_world_profile_reducer.rs | 41 +- .../update_puzzle_work_procedure.rs | 39 +- .../upsert_auth_store_snapshot_procedure.rs | 39 +- ...hapter_progression_and_return_procedure.rs | 41 +- .../upsert_chapter_progression_reducer.rs | 38 +- ...orld_agent_operation_progress_procedure.rs | 42 +- ...stom_world_profile_and_return_procedure.rs | 39 +- .../upsert_custom_world_profile_reducer.rs | 41 +- .../upsert_npc_state_and_return_procedure.rs | 39 +- .../upsert_npc_state_reducer.rs | 38 +- ...orm_browse_history_and_return_procedure.rs | 39 +- ...rt_runtime_setting_and_return_procedure.rs | 39 +- ...t_runtime_snapshot_and_return_procedure.rs | 39 +- .../src/module_bindings/user_account_type.rs | 31 +- .../user_browse_history_type.rs | 21 +- .../crates/spacetime-client/src/runtime.rs | 44 +- .../crates/spacetime-client/src/story.rs | 6 +- .../src/asset_metadata/bindings.rs | 36 + .../src/asset_metadata/objects.rs | 66 + .../spacetime-module/src/gameplay/mod.rs | 379 +--- .../crates/spacetime-module/src/migration.rs | 1 + .../crates/spacetime-module/src/puzzle.rs | 47 +- .../PlatformEntryFlowShellImpl.tsx | 139 +- .../flow/campTravelHomeScene.ts | 4 +- .../flow/storyOpeningCampDialogue.ts | 2 +- .../functionCatalog/functionCatalog.test.ts | 2 +- .../functionCatalog/npc/npcChatQuestOffer.ts | 2 +- src/data/sceneEncounterPreviews.ts | 5 +- .../rpg-runtime-story/choiceActions.test.ts | 22 +- src/hooks/rpg-runtime-story/choiceActions.ts | 4 +- .../rpg-runtime-story/inventoryActions.ts | 6 +- .../rpgRuntimeStoryGateway.ts | 52 +- .../runtimeStoryCoordinator.test.ts | 394 ++-- .../rpg-runtime-story/storyChoiceRuntime.ts | 8 +- .../rpg-runtime-story/storyContextBuilder.ts | 1 + .../storyRequestCoordinator.ts | 4 +- .../rpg-session/useRpgSessionBootstrap.ts | 5 +- .../rpg-session/useRpgSessionPersistence.ts | 6 +- src/hooks/runtimeAuthGuards.test.tsx | 2 +- src/hooks/useGameFlow.customWorld.test.tsx | 10 +- src/services/ai.test.ts | 147 +- src/services/ai.ts | 3 +- src/services/aiService.ts | 113 +- src/services/aiTypes.ts | 1 + src/services/apiClient.test.ts | 4 +- src/services/puzzle-runtime/index.ts | 1 - .../puzzle-runtime/puzzleRuntimeClient.ts | 22 - .../rpgCreationAssetClient.test.ts | 92 + .../rpg-creation/rpgCreationAssetClient.ts | 2 +- src/services/rpg-runtime/index.ts | 34 +- .../rpg-runtime/rpgRuntimeStoryClient.test.ts | 632 ++++--- .../rpg-runtime/rpgRuntimeStoryClient.ts | 196 +- vite.config.ts | 35 - 605 files changed, 11811 insertions(+), 10106 deletions(-) create mode 100644 docs/technical/SERVER_RS_DDD_WP_API_BFF_CLOSURE_2026-05-01.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_AS_ASSET_CHAIN_CLOSURE_2026-05-01.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_CW_FULL_CHAIN_CLOSURE_2026-05-01.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_DEL_CLEANUP_2026-05-01.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_PZ_RUNTIME_BACKEND_TRUTH_CLOSURE_2026-05-01.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_RPG_GAMEPLAY_CLOSURE_2026-05-01.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_RS_RUNTIME_STORY_CLOSURE_2026-05-01.md create mode 100644 docs/technical/SERVER_RS_DDD_WP_ST_CLOSURE_2026-05-01.md delete mode 100644 server-rs/crates/api-server/src/legacy_generated_assets.rs create mode 100644 server-rs/crates/module-runtime-story/src/bootstrap.rs create mode 100644 server-rs/crates/module-runtime-story/src/session_action.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/asset_event_kind_type.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/asset_event_table.rs create mode 100644 server-rs/crates/spacetime-client/src/module_bindings/asset_event_type.rs create mode 100644 src/services/rpg-creation/rpgCreationAssetClient.test.ts diff --git a/docs/technical/PUZZLE_LEVEL_CLEAR_RESULT_MODAL_AND_LEADERBOARD_2026-04-26.md b/docs/technical/PUZZLE_LEVEL_CLEAR_RESULT_MODAL_AND_LEADERBOARD_2026-04-26.md index 5c6a0ebf..2c4bd305 100644 --- a/docs/technical/PUZZLE_LEVEL_CLEAR_RESULT_MODAL_AND_LEADERBOARD_2026-04-26.md +++ b/docs/technical/PUZZLE_LEVEL_CLEAR_RESULT_MODAL_AND_LEADERBOARD_2026-04-26.md @@ -2,6 +2,8 @@ 更新时间:`2026-04-26` +> 2026-05-01 更新:正式平台入口已切到后端真相源。`startLocalPuzzleRun`、`swapLocalPuzzlePieces`、`dragLocalPuzzlePiece` 和 `advanceLocalPuzzleNextLevel` 只代表早期 V1/调试直达页背景,不再作为平台内 Puzzle 运行态主链。 + ## 1. 本次目标 玩家每完成拼图运行时的一关后,立即弹出独立结算弹窗。弹窗需要显示: diff --git a/docs/technical/README.md b/docs/technical/README.md index 97776f29..27fbc30c 100644 --- a/docs/technical/README.md +++ b/docs/technical/README.md @@ -4,13 +4,21 @@ ## 文档列表 +- [SERVER_RS_DDD_WP_DEL_CLEANUP_2026-05-01.md](./SERVER_RS_DDD_WP_DEL_CLEANUP_2026-05-01.md):记录 `WP-DEL 删除旧层与命名收口`,物理删除旧 runtime story HTTP DTO、前端 `Rpg*` alias、旧 `/api/custom-world/*` 非 runtime 前缀、Puzzle `local-next-level` 入口和 `/generated-*` 资产直读代理;生成资产读取统一走 OSS read-url 链路。 +- [SERVER_RS_DDD_WP_API_BFF_CLOSURE_2026-05-01.md](./SERVER_RS_DDD_WP_API_BFF_CLOSURE_2026-05-01.md):记录 `WP-API api-server BFF` 收尾,补齐 `/api/llm/chat/completions` 的 `stream=true` SSE 代理,明确手机号/微信配置门控和角色动画资产占位不阻塞本次 BFF 关闭。 +- [SERVER_RS_DDD_WP_AS_ASSET_CHAIN_CLOSURE_2026-05-01.md](./SERVER_RS_DDD_WP_AS_ASSET_CHAIN_CLOSURE_2026-05-01.md):记录 `WP-AS Assets` 资产主链收尾,补齐资产领域事件、`asset_event` event table、OSS 确认、API facade、Rust bindings、表目录和 migration 白名单。 +- [SERVER_RS_DDD_WP_CW_FULL_CHAIN_CLOSURE_2026-05-01.md](./SERVER_RS_DDD_WP_CW_FULL_CHAIN_CLOSURE_2026-05-01.md):记录 `WP-CW Custom World` 当前主链收尾,补齐 runtime Custom World 场景图入口、RPG 创作资产 client 主路径、DashScope 缺配置测试口径和旧 `/api/custom-world/*` 兼容入口边界。 +- [SERVER_RS_DDD_WP_ST_CLOSURE_2026-05-01.md](./SERVER_RS_DDD_WP_ST_CLOSURE_2026-05-01.md):记录 `WP-ST SpacetimeDB Adapter` 当前稳定范围收尾,补齐 `asset_event`、表目录、migration 白名单、Rust bindings 和 Windows bindings 生成 fallback。 +- [SERVER_RS_DDD_WP_RPG_GAMEPLAY_CLOSURE_2026-05-01.md](./SERVER_RS_DDD_WP_RPG_GAMEPLAY_CLOSURE_2026-05-01.md):记录 `WP-RPG Gameplay 域` 全域收口,将战斗胜利、任务交付和宝箱奖励的跨域结算计划下沉到 `module-story`。 +- [SERVER_RS_DDD_WP_PZ_RUNTIME_BACKEND_TRUTH_CLOSURE_2026-05-01.md](./SERVER_RS_DDD_WP_PZ_RUNTIME_BACKEND_TRUTH_CLOSURE_2026-05-01.md):记录 `WP-PZ Puzzle` 正式平台运行态从前端本地裁决切到后端真相源,补齐 owner draft 预览 run、交换/拖动/下一关/排行榜后端调用和无 schema 变更口径。 +- [SERVER_RS_DDD_WP_RS_RUNTIME_STORY_CLOSURE_2026-05-01.md](./SERVER_RS_DDD_WP_RS_RUNTIME_STORY_CLOSURE_2026-05-01.md):记录 `WP-RS Runtime Story` 写链路收尾,补齐 `/api/story/sessions/runtime` 与 `/api/story/sessions/{storySessionId}/actions/resolve`,统一返回 `StoryRuntimeMutationResponse.projection`,并保持旧 `/api/runtime/story/*` 未挂载。 - [SERVER_RS_DDD_WP_CW_ACTION_AND_DOMAIN_SPLIT_2026-04-30.md](./SERVER_RS_DDD_WP_CW_ACTION_AND_DOMAIN_SPLIT_2026-04-30.md):记录 `WP-CW Custom World` 的领域拆分与 Agent action 收口,将 `module-custom-world` 大 `lib.rs` 拆入 DDD 骨架,并移除 Custom World 运行代码中的最小兼容占位动作。 - [SERVER_RS_DDD_WP_BF_AND_G2_DRIFT_CLEANUP_2026-04-30.md](./SERVER_RS_DDD_WP_BF_AND_G2_DRIFT_CLEANUP_2026-04-30.md):记录 `WP-BF Big Fish` 物理拆分漂移和 G2 迁移期口径清理,将 Big Fish 创作域类型、命令、应用规则和错误层拆入 DDD 文件,并清理剩余 `过渡落位` 注释。 - [SERVER_RS_DDD_TESTS_SUPPORT_CRATE_CLOSURE_2026-04-30.md](./SERVER_RS_DDD_TESTS_SUPPORT_CRATE_CLOSURE_2026-04-30.md):记录 `tests-support` 从目录占位收口为 `server-rs` workspace 共享测试支撑 crate,首版提供 Maincloud healthz 与 HTTP smoke 通用断言。 - [SERVER_RS_DDD_WP_BF_RUNTIME_BACKEND_TRUTH_2026-04-29.md](./SERVER_RS_DDD_WP_BF_RUNTIME_BACKEND_TRUTH_2026-04-29.md):记录 `WP-BF Big Fish` 运行态从前端本地规则切到 Rust 领域真相源、SpacetimeDB run 表、API facade 和前端新接口接入的关闭口径。 - [SERVER_RS_DDD_WP_PF_PLATFORM_ERROR_CLASSIFICATION_2026-04-29.md](./SERVER_RS_DDD_WP_PF_PLATFORM_ERROR_CLASSIFICATION_2026-04-29.md):记录 `WP-PF platform side effects` 平台副作用收口,统一 LLM、OSS、SMS、微信平台错误分类与 API 映射,并将微信 OAuth provider 下沉到 `platform-auth`。 - [SERVER_RS_DDD_WP_RT_ADAPTER_API_CLOSURE_2026-04-29.md](./SERVER_RS_DDD_WP_RT_ADAPTER_API_CLOSURE_2026-04-29.md):记录 `WP-RT Runtime/Profile/Save` Adapter/API 收口,将 checkpoint、profile/save archive meta、充值/邀请/兑换/钱包等剩余纯规则迁入 `module-runtime`,移除 `/api/runtime/profile/*` 旧兼容挂载并对齐前端 `/api/profile/*` 请求路径。 -- [SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md](./SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md):冻结 `WP-SC Spacetime Client` 本次基础设施重构边界,明确只收口 `spacetime-client` 的 typed facade、错误映射和 row snapshot mapper,不预判尚未由 `WP-ST` 稳定的表、reducer、procedure 或 row shape。 +- [SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md](./SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md):记录 `WP-SC Spacetime Client` 在当前稳定 facade 范围内的关闭口径,收口 `spacetime-client` 的 typed facade、错误映射、row snapshot mapper、story runtime inventory source 接线和 README 状态;后续只随 `WP-ST` 新 facade 稳定后增量接线。 - [SERVER_RS_DDD_WP_RT_APPLICATION_RECORD_REFACTOR_2026-04-29.md](./SERVER_RS_DDD_WP_RT_APPLICATION_RECORD_REFACTOR_2026-04-29.md):记录 `WP-RT Runtime/Profile/Save` 的应用记录投影拆分切片,将 settings、browse history、profile/save 等 `build_runtime_*_record` 迁入 `module-runtime/src/application.rs`,不改回包字段语义。 - [SERVER_RS_DDD_WP_RT_COMMANDS_REFACTOR_2026-04-29.md](./SERVER_RS_DDD_WP_RT_COMMANDS_REFACTOR_2026-04-29.md):记录 `WP-RT Runtime/Profile/Save` 的命令构造拆分切片,将 settings、browse history、profile/save 等 `build_runtime_*_input` 和写入归一化函数迁入 `module-runtime/src/commands.rs`,不改校验语义。 - [SERVER_RS_DDD_WP_AI_INTERNAL_MODULE_SPLIT_2026-04-29.md](./SERVER_RS_DDD_WP_AI_INTERNAL_MODULE_SPLIT_2026-04-29.md):记录 `WP-AI AI Task` 的 `module-ai` 内部子模块拆分,将 domain、commands、application 与行为测试继续拆到职责更细的子文件,同时保持 `module_ai::*` 公开导出、SpacetimeDB schema、BFF route 和前端契约不变。 @@ -24,16 +32,16 @@ - [SERVER_RS_DDD_WP_PZ_DOMAIN_ENUM_REHOME_2026-04-29.md](./SERVER_RS_DDD_WP_PZ_DOMAIN_ENUM_REHOME_2026-04-29.md):记录 `WP-PZ Puzzle` 基础领域常量与枚举归位切片,将 Puzzle Agent、发布状态、运行态状态、ID 前缀、标签数量和洗牌次数口径迁入 `module-puzzle/src/domain.rs`,不改 SpacetimeDB、API 或前端行为。 - [SERVER_RS_DDD_WP_RPG_COMBAT_DOMAIN_ENUM_REHOME_2026-04-29.md](./SERVER_RS_DDD_WP_RPG_COMBAT_DOMAIN_ENUM_REHOME_2026-04-29.md):记录 `WP-RPG Gameplay 域` 的 combat 基础领域常量与枚举归位切片,将战斗 ID 前缀、版本、伤害、切磋保底生命、旧攻击 function 列表和基础枚举迁入 `module-combat/src/domain.rs`,不改 SpacetimeDB、API 或前端行为。 - [SERVER_RS_DDD_WP_ST_AUTH_ADAPTER_SPLIT_2026-04-29.md](./SERVER_RS_DDD_WP_ST_AUTH_ADAPTER_SPLIT_2026-04-29.md):记录 `WP-ST` Auth SpacetimeDB adapter 目录化切片,将认证表、procedure 和快照 JSON mapper 拆入 `auth/` 子模块,不改 schema、procedure 签名或绑定形状。 -- [SERVER_RS_DDD_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md](./SERVER_RS_DDD_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md):记录 `WP-RS Runtime Story 去兼容层` 的 compat 残留审计切片,清理 `module-runtime-story` 运行代码注释口径,并冻结仍需等待新写接口的前端和 contract 残留。 +- [SERVER_RS_DDD_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md](./SERVER_RS_DDD_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md):记录 `WP-RS Runtime Story 去兼容层` 的 compat 残留审计切片;2026-05-01 后运行写链路已迁到 story session scoped route,旧展示 DTO 和历史命名统一留给 `WP-DEL` 清理。 - [SERVER_RS_DDD_WP_AS_ASSET_OBJECT_TYPE_REHOME_2026-04-29.md](./SERVER_RS_DDD_WP_AS_ASSET_OBJECT_TYPE_REHOME_2026-04-29.md):记录 `WP-AS Assets` 资产对象类型归位切片,将领域快照、命令 DTO、应用返回 DTO 和字段错误拆入 `module-assets` 的 DDD 骨架文件,不改 SpacetimeDB、API、OSS 或前端行为。 - [SERVER_RS_DDD_WP_RT_ERROR_LAYER_REFACTOR_2026-04-29.md](./SERVER_RS_DDD_WP_RT_ERROR_LAYER_REFACTOR_2026-04-29.md):记录 `WP-RT Runtime/Profile/Save` 的错误层拆分切片,将 settings、browse history、profile/save 三组字段错误和中文错误文案迁入 `module-runtime/src/errors.rs`,不改校验语义。 - [SERVER_RS_DDD_WP_RT_DOMAIN_SNAPSHOT_RECORD_REFACTOR_2026-04-29.md](./SERVER_RS_DDD_WP_RT_DOMAIN_SNAPSHOT_RECORD_REFACTOR_2026-04-29.md):记录 `WP-RT Runtime/Profile/Save` 的 snapshot、profile、wallet、played world 与 save archive 领域快照和记录类型拆分切片,只移动纯类型和枚举方法,不改 SpacetimeDB、API 或前端接线。 - [SERVER_RS_DDD_WP_RT_RUNTIME_SETTINGS_DOMAIN_REFACTOR_2026-04-29.md](./SERVER_RS_DDD_WP_RT_RUNTIME_SETTINGS_DOMAIN_REFACTOR_2026-04-29.md):记录 `WP-RT Runtime/Profile/Save` 的 runtime settings 领域值对象拆分切片,将默认设置、平台主题和值对象迁入 `module-runtime/src/domain.rs`,不改 SpacetimeDB、API 或前端接线。 - [SERVER_RS_DDD_WP_A_AUTH_DOMAIN_VALUE_OBJECT_REFACTOR_2026-04-29.md](./SERVER_RS_DDD_WP_A_AUTH_DOMAIN_VALUE_OBJECT_REFACTOR_2026-04-29.md):记录 `WP-A Auth` DDD 分层收口,将账号、会话、验证码、微信 state/绑定规则、命令输入、应用返回、领域错误和领域事件归位到 `module-auth` 骨架,并核查 API、platform 与 SpacetimeDB adapter 边界。 - [SERVER_RS_DDD_WP_ST_CUSTOM_WORLD_ROOT_SPLIT_2026-04-29.md](./SERVER_RS_DDD_WP_ST_CUSTOM_WORLD_ROOT_SPLIT_2026-04-29.md):记录 `WP-ST` Custom World SpacetimeDB adapter 从根入口迁入 `custom_world/mod.rs` 的边界、无 schema 变更口径和验收命令。 -- [SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md](./SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md):记录 `WP-FE-S` RPG runtime story client 读取侧迁到 `storySessionId` scoped runtime projection,并补齐 story session 新主链 `begin/continue/state/projection` API client 的边界、旧写接口暂留原因和后续依赖。 -- [SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md](./SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md):记录 `WP-FE-H` RPG runtime story 读取侧 hooks 接线切片,将 option catalog 与继续游戏刷新显式接入 `getRpgStoryRuntimeProjection`,写接口和组件层仍等待后续收口。 -- [SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md](./SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md):记录 `WP-FE-C` RPG runtime shell 组件测试夹具接线切片,将组件测试 mock 对齐当前 hooks 暴露的 UI 对象形状,不触碰未稳定写接口。 +- [SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md](./SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md):记录 `WP-FE-S` RPG runtime story client 迁到 `storySessionId` scoped runtime projection,并在 2026-05-01 收尾切片中关闭旧 `/api/runtime/story/*` 开局与动作写侧调用。 +- [SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md](./SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md):记录 `WP-FE-H` RPG runtime story hooks 接线切片,将 option catalog、继续游戏刷新与正式动作结算统一接入 story runtime projection client。 +- [SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md](./SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md):记录 `WP-FE-C` RPG runtime shell 组件测试夹具接线切片,将组件测试 mock 对齐当前 hooks 暴露的 UI 对象形状,并确认组件层不拼接 runtime story API 路径。 - [SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_PROGRESS_2026-04-29.md](./SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_PROGRESS_2026-04-29.md):记录 `G1 契约与路由矩阵` 已完成的本地进度、验证结果、单 owner 边界和下一批并行任务入口。 - [SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md](./SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md):冻结 `server-rs` DDD G1 契约与路由矩阵,明确新旧 HTTP 路由去留、DTO 删除/保留/重命名、页面到 query/result DTO 映射、breaking change、API 错误 envelope 和共享契约单 owner 边界。 - [SERVER_RS_DDD_WP_API_BFF_START_2026-04-29.md](./SERVER_RS_DDD_WP_API_BFF_START_2026-04-29.md):记录 `WP-API api-server BFF` 启动切片,先收口旧 runtime story 兼容路由挂载、错误 envelope 回归和后续依赖,不越过 `spacetime-client` 接线边界。 @@ -96,7 +104,7 @@ - [JENKINS_RUST_BUILD_DEPLOY_PIPELINES_2026-04-23.md](./JENKINS_RUST_BUILD_DEPLOY_PIPELINES_2026-04-23.md):冻结 Jenkins `构建 / 部署 / 构建并部署` 三条流水线的职责、版本号传递、上游触发门禁、本地目录部署脚本与 `/home/ubuntu/Genarrative-deploy/` 覆盖策略。 - [JENKINS_DEPLOY_ENV_BOM_FIX_2026-04-25.md](./JENKINS_DEPLOY_ENV_BOM_FIX_2026-04-25.md):记录 Jenkins 部署时 `.env.local` 首行 UTF-8 BOM 导致 `start.sh` 加载失败的根因,并冻结发布包构建、部署脚本和启动脚本的环境文件净化规则。 - [RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md](./RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md):冻结 Rust 本地一键联调脚本与 Ubuntu 发布包构建脚本的执行口径,覆盖 `npm run dev:rust`、`npm run build:rust:ubuntu`、Vite release、Linux `api-server`、SpacetimeDB wasm、启动停止脚本、默认 scp 上传和安全清库开关。 -- [RUST_API_SERVER_ROUTE_INDEX_2026-04-22.md](./RUST_API_SERVER_ROUTE_INDEX_2026-04-22.md):记录当前 Rust `api-server` 已挂载的 101 条 Axum 路由,并补充管理后台入口与管理接口索引,按 auth、assets、runtime、custom world、story、generated path 等挂载面归类,用于对照 Node 能力基线与切流 smoke 清单。 +- [RUST_API_SERVER_ROUTE_INDEX_2026-04-22.md](./RUST_API_SERVER_ROUTE_INDEX_2026-04-22.md):记录当前 Rust `api-server` 已挂载路由,并补充管理后台入口与管理接口索引,按 auth、assets、runtime、custom world、story 等挂载面归类,用于对照 Node 能力基线与切流 smoke 清单;`/generated-*` 直读代理已下线。 - [BACKEND_REWRITE_CROSS_CUTTING_GOVERNANCE_2026-04-22.md](./BACKEND_REWRITE_CROSS_CUTTING_GOVERNANCE_2026-04-22.md):冻结后端重写收口阶段的横向治理规则,覆盖 TypeScript contract 到 Rust DTO 映射、SpacetimeDB schema 演进、大对象 / workflow cache 存储边界和文档维护门禁。 - [PLATFORM_LLM_TEXT_GATEWAY_DESIGN_2026-04-21.md](./PLATFORM_LLM_TEXT_GATEWAY_DESIGN_2026-04-21.md):`platform-llm` 文本模型网关首版设计,冻结 OpenAI 兼容 `/chat/completions`、SSE 增量解析、错误模型与重试边界。 - [API_SERVER_PLATFORM_LLM_PROXY_DESIGN_2026-04-21.md](./API_SERVER_PLATFORM_LLM_PROXY_DESIGN_2026-04-21.md):`api-server` 接入 `platform-llm` 的最小代理设计,冻结 `/api/llm/chat/completions` 的配置、状态注入与首版非流式兼容边界。 diff --git a/docs/technical/RUST_API_SERVER_ROUTE_INDEX_2026-04-22.md b/docs/technical/RUST_API_SERVER_ROUTE_INDEX_2026-04-22.md index a35a152a..a0aae0d7 100644 --- a/docs/technical/RUST_API_SERVER_ROUTE_INDEX_2026-04-22.md +++ b/docs/technical/RUST_API_SERVER_ROUTE_INDEX_2026-04-22.md @@ -148,12 +148,7 @@ ### 3.10 Legacy Generated 路径 -1. `GET /generated-character-drafts/{*path}` -2. `GET /generated-characters/{*path}` -3. `GET /generated-animations/{*path}` -4. `GET /generated-custom-world-scenes/{*path}` -5. `GET /generated-custom-world-covers/{*path}` -6. `GET /generated-qwen-sprites/{*path}` +旧 `/generated-*` 直读代理已下线。生成资产读取统一走 `GET /api/assets/read-url` 或 asset object projection;`/generated-*` 字符串仅作为 `legacyPublicPath` / OSS object key 兼容标识保留。 ### 3.11 Health diff --git a/docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md b/docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md index edd6e5ff..652f92aa 100644 --- a/docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md +++ b/docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md @@ -33,7 +33,7 @@ G1 单 owner 文件范围: | 鉴权公开查询 | `GET /api/auth/login-options`、`GET /api/auth/public-users/by-code/{code}`、`GET /api/auth/public-users/by-id/{user_id}` | 保留 | `AuthLoginOptionsResponse`、`PublicUserSearchResponse` | WP-A | | 鉴权会话 | `GET /api/auth/me`、`GET /api/auth/sessions`、`POST /api/auth/refresh`、`POST /api/auth/logout`、`POST /api/auth/logout-all` | 保留 | `AuthMeResponse`、`AuthSessionsResponse`、`RefreshSessionResponse`、`LogoutResponse`、`LogoutAllResponse` | WP-A | | 鉴权登录 | `POST /api/auth/phone/send-code`、`POST /api/auth/phone/login`、`GET /api/auth/wechat/start`、`GET /api/auth/wechat/callback`、`POST /api/auth/wechat/bind-phone`、`POST /api/auth/entry`、`POST /api/auth/password/change`、`POST /api/auth/password/reset` | 保留 | TS 命名统一使用 `Auth*` 前缀,Rust 命名维持领域语义 | WP-A | -| 旧本地生成资产代理 | `GET /generated-character-drafts/{*path}`、`/generated-characters/{*path}`、`/generated-animations/{*path}`、`/generated-big-fish-assets/{*path}`、`/generated-puzzle-assets/{*path}`、`/generated-custom-world-scenes/{*path}`、`/generated-custom-world-covers/{*path}`、`/generated-qwen-sprites/{*path}` | 删除 | 正式读取统一改为 `GET /api/assets/read-url` 或 asset object projection;本地生成路径只允许迁移窗口内存在 | WP-AS、WP-FE、WP-DEL | +| 旧本地生成资产代理 | `GET /generated-character-drafts/{*path}`、`/generated-characters/{*path}`、`/generated-animations/{*path}`、`/generated-big-fish-assets/{*path}`、`/generated-puzzle-assets/{*path}`、`/generated-custom-world-scenes/{*path}`、`/generated-custom-world-covers/{*path}`、`/generated-qwen-sprites/{*path}` | 已删除 | 正式读取统一走 `GET /api/assets/read-url` 或 asset object projection;`/generated-*` 仅允许作为 legacyPublicPath/object key 标识,不再作为可裸读路由 | WP-AS、WP-FE、WP-DEL | | LLM 代理 | `POST /api/llm/chat/completions` | 收敛 | 仅作为平台能力代理;玩法 prompt 不允许由前端直接传入 | WP-PF、WP-API | | Runtime chat | `POST /api/runtime/chat/character/suggestions`、`/summary`、`/reply/stream`、`/npc/dialogue/stream`、`/npc/turn/stream`、`/npc/recruit/stream` | 重命名 | 收敛到 session scoped story/chat 命令;请求体不得携带前端拼装的世界真相 | WP-RS、WP-RPG、WP-FE | | 文档输入 | `POST /api/runtime/creation-agent/document-inputs/parse` | 保留 | `ParseCreationAgentDocumentInputRequest/Response` | WP-CW、WP-BF、WP-PZ | @@ -44,8 +44,8 @@ G1 单 owner 文件范围: | RPG 作品库 | `GET /api/runtime/custom-world-library`、`GET/PUT/DELETE /api/runtime/custom-world-library/{profile_id}`、`POST /publish`、`POST /unpublish`、`GET /api/runtime/custom-world-gallery`、`GET /api/runtime/custom-world-gallery/{owner_user_id}/{profile_id}`、`GET /api/runtime/custom-world-gallery/by-code/{code}` | 收敛 | 命名后续改为 RPG creation/work route family;删除 `custom-world` 旧泛名歧义 | WP-CW、WP-FE | | RPG Agent | `POST /api/runtime/custom-world/agent/sessions`、`GET/DELETE /sessions/{session_id}`、`GET /result-view`、`GET /works`、`GET /cards/{card_id}`、`POST /messages`、`POST /messages/stream`、`POST /actions`、`GET /operations/{operation_id}` | 收敛 | DTO 重命名为 `RpgAgent*`,Rust 当前 `CustomWorldAgent*` 后续物理重命名 | WP-CW、WP-FE、WP-DEL | | Big Fish Agent/Works/Runtime | `POST /api/runtime/big-fish/agent/sessions`、`GET /sessions/{session_id}`、`POST /messages`、`POST /messages/stream`、`POST /actions`、`GET /works`、`DELETE /works/{session_id}`、`GET /gallery`、`POST /sessions/{session_id}/play`、`POST /works/{session_id}/play`、`POST /sessions/{session_id}/runs`、`GET /runs/{run_id}`、`POST /runs/{run_id}/input` | 保留 | `BigFish*` DTO,运行态正式使用 `BigFishRunResponse` 和 `SubmitBigFishInputRequest`;`sessions/{id}/play` 与 `works/{id}/play` 后续二选一保留 | WP-BF | -| Puzzle Agent/Works/Runtime | `POST /api/runtime/puzzle/agent/sessions`、`GET /sessions/{session_id}`、`POST /messages`、`POST /messages/stream`、`POST /actions`、`GET /works`、`GET/PUT/DELETE /works/{profile_id}`、`GET /gallery`、`GET /gallery/{profile_id}`、`POST /runs`、`POST /runs/local-next-level`、`GET /runs/{run_id}`、`POST /runs/{run_id}/swap`、`POST /runs/{run_id}/drag`、`POST /runs/{run_id}/next-level`、`POST /runs/{run_id}/leaderboard` | 保留 | `PuzzleAgent*`、`PuzzleWork*`、`PuzzleRun*` DTO | WP-PZ | -| RPG profile/asset generation | `POST /api/runtime/custom-world/profile`、`POST /api/custom-world/entity`、`POST /api/runtime/custom-world/entity`、`POST /api/custom-world/scene-npc`、`POST /api/runtime/custom-world/scene-npc`、`POST /api/custom-world/scene-image`、`POST /api/custom-world/cover-image`、`POST /api/runtime/custom-world/cover-image`、`POST /api/custom-world/cover-upload`、`POST /api/runtime/custom-world/cover-upload` | 重命名 | 去掉非 runtime 前缀旧入口;统一到 RPG creation asset/profile route family | WP-CW、WP-AS、WP-DEL | +| Puzzle Agent/Works/Runtime | `POST /api/runtime/puzzle/agent/sessions`、`GET /sessions/{session_id}`、`POST /messages`、`POST /messages/stream`、`POST /actions`、`GET /works`、`GET/PUT/DELETE /works/{profile_id}`、`GET /gallery`、`GET /gallery/{profile_id}`、`POST /runs`、`GET /runs/{run_id}`、`POST /runs/{run_id}/swap`、`POST /runs/{run_id}/drag`、`POST /runs/{run_id}/next-level`、`POST /runs/{run_id}/leaderboard`;旧 `POST /runs/local-next-level` 已取消挂载 | 保留 | `PuzzleAgent*`、`PuzzleWork*`、`PuzzleRun*` DTO;旧 `AdvanceLocalPuzzleNextLevelRequest` 已删除 | WP-PZ、WP-DEL | +| RPG profile/asset generation | `POST /api/runtime/custom-world/profile`、`POST /api/runtime/custom-world/entity`、`POST /api/runtime/custom-world/scene-npc`、`POST /api/runtime/custom-world/scene-image`、`POST /api/runtime/custom-world/cover-image`、`POST /api/runtime/custom-world/cover-upload`;旧 `/api/custom-world/*` 非 runtime 前缀已取消挂载 | 重命名 | 去掉非 runtime 前缀旧入口;统一到 RPG creation asset/profile route family | WP-CW、WP-AS、WP-DEL | | Profile | `GET/POST/DELETE /api/profile/browse-history`、`GET /api/profile/dashboard`、`GET /api/profile/wallet-ledger`、`GET /api/profile/recharge-center`、`POST /api/profile/recharge/orders`、`GET /api/profile/referrals/invite-center`、`POST /api/profile/referrals/redeem-code`、`POST /api/profile/redeem-codes/redeem`、`GET /api/profile/play-stats`、`GET /api/profile/save-archives`、`POST /api/profile/save-archives/{world_key}`;旧 `GET/POST/DELETE /api/runtime/profile/*` 已取消挂载 | 重命名 | 保留 `/api/profile/*` 主链,删除 `/api/runtime/profile/*` 旧兼容入口 | WP-RT、WP-FE、WP-DEL | | Runtime inventory | `GET /api/runtime/sessions/{runtime_session_id}/inventory` | 保留 | `RuntimeInventoryStateResponse` | WP-RPG、WP-RT | | Runtime story 旧层 | `POST /api/runtime/story/sessions`、`POST /api/runtime/story/state/resolve`、`GET /api/runtime/story/state/{session_id}`、`POST /api/runtime/story/actions/resolve`、`POST /api/runtime/story/initial`、`POST /api/runtime/story/continue` | 已删除 | 已从 `api-server` 取消挂载并删除 `api-server/src/runtime_story*` 兼容实现;后续前端迁移到 `GET/POST /api/story/*` 和 session scoped story/chat facade | WP-RS、WP-FE、WP-DEL | @@ -89,10 +89,10 @@ G1 单 owner 文件范围: | DTO/文件 | 删除条件 | 替代 | | --- | --- | --- | | `LegacyApiErrorResponse` | 全部路由完成 envelope 归一后 | `ApiErrorEnvelope` | -| Rust `RuntimeStoryStateResolveRequest` | 前端切到 `GET /api/story/sessions/{story_session_id}/state` 后 | `StorySessionStateResponse` 加拆分投影 | -| Rust/TS `RuntimeStoryBootstrapRequest/Response` | `POST /api/runtime/story/initial` 删除后 | `BeginStorySessionRequest`、`StorySessionMutationResponse` | +| Rust `RuntimeStoryStateResolveRequest` | 已在 WP-DEL 删除 | `StorySessionStateResponse` 与 story runtime projection | +| Rust/TS `RuntimeStoryBootstrapRequest/Response` | 已在 WP-DEL 删除 | `BeginStoryRuntimeSessionRequest`、`StoryRuntimeMutationResponse` | | Rust/TS `RuntimeStoryAiRequest/Response` | `POST /api/runtime/story/continue` 删除后 | `ContinueStoryRequest`、`StorySessionMutationResponse` | -| Rust/TS `RuntimeStoryActionRequest/Response` 旧总入口形态 | `POST /api/runtime/story/actions/resolve` 删除后 | story/battle/npc/inventory 分命令 result DTO | +| Rust/TS `RuntimeStoryActionResponse` 旧总入口形态 | 已在 WP-DEL 删除 | `StoryRuntimeMutationResponse.projection` 与 story/battle/npc/inventory 分投影 | | TS `StoryRequestPayload`、`PlainTextPromptRequest`、`PlainTextResponse` | runtime chat 不再由前端传 prompt 后 | 后端 session scoped chat/story command | | TS `CreateCustomWorldSessionRequest`、`AnswerCustomWorldSessionQuestionRequest`、`CustomWorldSessionRecord` 等旧问答生成 DTO | 确认无前端运行引用后 | RPG Agent session DTO | | `/api/runtime/profile/*` 旧兼容 DTO 别名 | 前端全量迁到 `/api/profile/*` 后 | Runtime profile DTO | @@ -117,7 +117,7 @@ G1 单 owner 文件范围: | Big Fish 广场/作品 | bearer token 或公开 gallery query | `RecordBigFishPlayRequest` | `BigFishWorksResponse`、`BigFishGalleryResponse`、`BigFishSessionResponse` | | Puzzle Agent | route param `session_id` | `CreatePuzzleAgentSessionRequest`、`SendPuzzleAgentMessageRequest`、`ExecutePuzzleAgentActionRequest` | `PuzzleAgentSessionResponse`、`PuzzleAgentActionResponse` | | Puzzle 作品/广场 | route param `profile_id` | `PutPuzzleWorkRequest` | `PuzzleWorksResponse`、`PuzzleWorkDetailResponse`、`PuzzleGalleryResponse`、`PuzzleGalleryDetailResponse` | -| Puzzle 运行态 | route param `run_id` | `StartPuzzleRunRequest`、`AdvanceLocalPuzzleNextLevelRequest`、`SwapPuzzlePiecesRequest`、`DragPuzzlePieceRequest`、`SubmitPuzzleLeaderboardRequest` | `PuzzleRunResponse` | +| Puzzle 运行态 | route param `run_id` | `StartPuzzleRunRequest`、`SwapPuzzlePiecesRequest`、`DragPuzzlePieceRequest`、`SubmitPuzzleLeaderboardRequest` | `PuzzleRunResponse` | | Runtime 设置与存档 | bearer token | `PutRuntimeSettingsRequest`、`PutSavedGameSnapshotRequest`、`PutRuntimeSaveCheckpointRequest` | `RuntimeSettingsResponse`、`SavedGameSnapshotResponse`、`BasicOkResponse` | | 个人中心 | bearer token | `CreateProfileRechargeOrderRequest`、`RedeemProfileReferralInviteCodeRequest`、`RedeemProfileRewardCodeRequest`、`PlatformBrowseHistoryUpsertRequest` | `ProfileDashboardSummaryResponse`、`ProfileWalletLedgerResponse`、`ProfileRechargeCenterResponse`、`ProfileReferralInviteCenterResponse`、`ProfilePlayStatsResponse`、`ProfileSaveArchiveListResponse`、`PlatformBrowseHistoryResponse` | | RPG Story 运行态 | route param `story_session_id`、`battle_state_id` | `BeginStorySessionRequest`、`ContinueStoryRequest`、`CreateStoryBattleRequest`、`CreateStoryNpcBattleRequest`、`ResolveStoryBattleRequest` | `StorySessionMutationResponse`、`StorySessionStateResponse`、`StoryRuntimeProjectionResponse`、`StoryBattleStateResponse`、`ResolveStoryBattleResponse`、`RuntimeInventoryStateResponse` | @@ -125,12 +125,12 @@ G1 单 owner 文件范围: ## 5. Breaking change 清单 -1. 删除兼容层是本轮默认策略。旧 `/api/runtime/story/*`、`/_internal/auth/*`、`/generated-*` 和 `/api/runtime/profile/*` 兼容入口在对应前端迁移完成后物理删除。 +1. 删除兼容层是本轮默认策略。旧 `/api/runtime/story/*`、`/api/custom-world/*` 非 runtime 前缀、`/api/runtime/puzzle/runs/local-next-level`、`/api/runtime/profile/*` 兼容入口和 `/generated-*` 资产直读代理均在对应前端迁移完成后物理删除。 2. Runtime story/chat 不再接受前端拼装的 `worldType`、`character`、`monsters`、`history`、`context`、prompt 文本作为正式真相。正式命令必须以 `runtimeSessionId`、`storySessionId`、`battleStateId` 等后端 session id 为索引。 3. `CustomWorld*` 作为 RPG 主链命名将被重命名为 `RpgCreation*` 或 `RpgAgent*`。前端可同步修改,不保留旧命名适配层。 4. `/api/custom-world/*` 非 runtime 前缀旧入口删除,统一进入 RPG creation route family 或 asset route family。 5. `/api/runtime/profile/*` 兼容入口删除,统一使用 `/api/profile/*`。 -6. 资产读取不再依赖 `/generated-*` 静态代理作为正式 contract,统一走 asset object、read url 或后端投影里的正式 URL 字段。 +6. 资产读取不再依赖 `/generated-*` 静态代理作为正式 contract,统一走 asset object、read url 或后端投影里的正式 URL 字段;`/generated-*` 只保留为 legacyPublicPath/object key 兼容标识。 7. LLM 代理不得作为玩法 prompt 透传入口。玩法 prompt 由 `api-server`/`platform-llm` 内部编排,前端只提交用户动作和展示态输入。 8. API 错误体统一为 `ApiErrorEnvelope`。旧 `{ error, meta }` 只允许在已列入删除计划的旧接口中短期存在。 @@ -175,4 +175,4 @@ G1 单 owner 文件范围: 2. `WP-ST` 负责 SpacetimeDB 表、reducer/procedure 和 `migration.rs`,不得由玩法领域任务直接抢改。 3. `WP-API` 负责 `api-server/src/app.rs` 和 route 挂载入口,领域任务只提供应用结果和错误模型。 4. `WP-FE` 在后端新接口稳定后删除旧前端兼容层,不新增对旧 route 的二次适配。 -5. `WP-DEL` 只能在搜索确认无运行引用后删除旧 DTO、旧 route 和旧静态代理。 +5. `WP-DEL` 只能在搜索确认无运行引用后删除旧 DTO、旧 route 和旧静态代理;`/generated-*` 直读代理已移除后,后续不得重新引入裸读转发。 diff --git a/docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md b/docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md index 0fa4d580..404f534a 100644 --- a/docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md +++ b/docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md @@ -158,23 +158,23 @@ LLM、OSS、SMS、微信等外部副作用可以独立准备,不等待 `WP-SC` | G1 契约与路由矩阵 | 已完成;本轮共享契约与路由矩阵持续巡检已关闭,已同步 profile、Big Fish run、story projection/battle 和文档输入契约索引漂移 | 未认领 | G0 后 | `shared-contracts`、`packages/shared/src/contracts/*`、API 路由索引 | 领域实现 | DTO 分组、breaking change 清单、前后端路由矩阵 | shared contract 测试通过 | | G2 DDD 骨架与边界检查 | 已完成并持续执行;本轮 G2 骨架巡检已关闭,15 个 module crate 均通过边界门禁,后续每批继续跑门禁 | 未认领 | G0 后 | `module-*` 骨架、`scripts/check-server-rs-ddd-boundaries.mjs` | 业务重写 | 所有 `module-*` 具备 `domain/commands/application/events/errors`,检查脚本覆盖禁用依赖 | `npm.cmd run check:server-rs-ddd` | | WP-A Auth | 已完成;module-auth 已完成 domain/commands/application/errors/events 分层归位,账号、会话、验证码、微信 state/绑定规则由 module-auth 承接,api-server/platform-auth/spacetime-module 边界已核查 | 未认领 | G1 后 | `module-auth`、`spacetime-module/src/auth*`、`api-server/src/auth*`、`platform-auth` | 其他玩法域 | 账号、会话、验证码、微信绑定领域化;真实短信/微信在 platform | `cargo test -p module-auth`,auth API 测试 | -| WP-AS Assets | 进行中;资产对象类型归位、资产领域测试和 SpacetimeDB row mapper 切片已完成,资产 API/OSS/facade 尚未全链收口 | 未认领 | G1 后 | `module-assets`、`spacetime-module/src/asset_metadata/*`、资产 API、OSS adapter | 玩法业务规则 | 资产对象与绑定规则纯化;OSS head/upload 移出领域核心 | `cargo test -p module-assets`,资产 facade 测试 | +| WP-AS Assets | 已完成;资产对象确认、实体槽位绑定、历史读取、OSS 对象确认、API facade、SpacetimeDB adapter、`asset_event` 和 Rust bindings 已闭环 | 未认领 | 后续只随 `asset_job/asset_manifest`、专业资产生成或 `WP-DEL/WP-V` 增量维护 | `module-assets`、`spacetime-module/src/asset_metadata/*`、资产 API、OSS adapter、`spacetime-client` assets facade | 玩法业务规则 | 资产对象与绑定规则纯化;OSS head/upload 移出领域核心,事件表只承接订阅与审计事实 | `cargo test -p module-assets`,`cargo check -p spacetime-module`,`cargo check -p spacetime-client` | | WP-AI AI Task | 已完成;领域层、SpacetimeDB AI adapter/event、spacetime-client facade、BFF 路由和定向测试已闭环,`module-ai` 内部子模块拆分已完成,真实 LLM/SSE/前端消费归后续 WP-PF/WP-API/WP-FE 承接 | 未认领 | G1 后 | `module-ai`、`spacetime-module/src/ai/*`、AI task API | LLM prompt 业务规则 | AI task/stage/chunk/result 状态机领域化 | `cargo test -p module-ai`,AI task reducer/procedure smoke | -| WP-CW Custom World | 进行中;基础领域枚举、DDD 物理拆分和 Agent action 最小兼容占位已关闭,profile/agent/draft/gallery/publish gate 全链仍待继续接 API/前端和资产对象链 | 未认领 | G1 后 | `module-custom-world`、`spacetime-module/src/custom_world/*`、`api-server` custom world 路由、前端创作 client | Big Fish/Puzzle | profile、agent session、draft card、gallery、publish gate 领域化;LLM 留在 API/platform | `cargo test -p module-custom-world`,custom world 定向测试 | +| WP-CW Custom World | 已完成;基础领域枚举、DDD 物理拆分、Agent action 确定性编排、稳定 SpacetimeDB facade、runtime API 路由和 RPG 创作资产 client 主路径已收口,旧 `/api/custom-world/*` 非 runtime 前缀已由 WP-DEL 取消挂载 | 未认领 | 后续只随 `WP-AS/WP-PF/WP-V` 增量维护 | `module-custom-world`、`spacetime-module/src/custom_world/*`、`api-server` custom world 路由、前端创作 client | Big Fish/Puzzle | profile、agent session、draft card、gallery、publish gate 领域化;LLM 和图片生成留在 API/platform | `cargo test -p module-custom-world`,`cargo test -p api-server custom_world`,前端资产 client 测试 | | WP-BF Big Fish | 已完成;运行态真相源和 DDD 物理拆分均已收口,并完成 SpacetimeDB run 表、spacetime-client facade、API 路由、前端 client 接入和本地运行态删除 | 未认领 | G1 后 | `module-big-fish`、`spacetime-module/src/big_fish/*`、Big Fish API、Big Fish 前端 client | Puzzle/RPG | 会话、草稿、素材槽、运行态纯规则;草稿校验下沉 | `cargo test -p module-big-fish`,Big Fish API 测试 | -| WP-PZ Puzzle | 进行中;领域类型与规则拆分切片已关闭,Puzzle API/前端消费仍未全链完成 | 未认领 | G1 后 | `module-puzzle`、`spacetime-module/src/puzzle*`、Puzzle API、Puzzle 前端 client | Big Fish/RPG | Agent session、work profile、runtime run、排行榜规则领域化 | `cargo test -p module-puzzle`,Puzzle 定向测试 | +| WP-PZ Puzzle | 已完成;领域类型与规则拆分、正式平台运行态后端真相源、owner draft 预览 run、Puzzle API 和前端正式消费链路已收口,旧 `local-next-level` 兼容入口已由 WP-DEL 取消挂载 | 未认领 | 后续只随 `WP-V` 增量维护 | `module-puzzle`、`spacetime-module/src/puzzle*`、Puzzle API、Puzzle 前端 client | Big Fish/RPG | Agent session、work profile、runtime run、排行榜规则领域化 | `cargo test -p module-puzzle`,Puzzle 定向测试 | | WP-RT Runtime/Profile/Save | 已完成;runtime settings、snapshot/profile/save archive 类型、错误层、命令构造、应用记录投影、Adapter/API/Frontend profile 路径和剩余纯规则均已收口 | 未认领 | G1 后 | `module-runtime`、`spacetime-module/src/runtime/*`、runtime/save/profile API | RPG story 规则 | runtime setting、snapshot、wallet、played world、save archive 领域化 | `cargo test -p module-runtime`,runtime API 测试 | -| WP-RPG Gameplay 域 | 进行中;combat/inventory/npc/progression/quest/runtime-item/story 的 DDD 物理拆分漂移已关闭,完整 story action 写侧与跨域组合结算仍待 `WP-RS/WP-ST/WP-SC/WP-API/WP-FE` 主链收口 | 未认领 | G1 后 | `module-combat`、`module-inventory`、`module-npc`、`module-progression`、`module-quest`、`module-runtime-item`、`module-story` | 创作域 | 战斗、背包、NPC、成长、任务、宝箱、story session 纯规则与跨域事件 | 各 module 测试;跨域应用结果测试 | -| WP-RS Runtime Story 去兼容层 | 进行中;compat 残留审计和 `module-runtime-story` 顶层 DDD 物理拆分漂移已关闭,旧前端写 client 与旧 contract 残留已冻结到后续 WP-FE-S/WP-DEL | 未认领 | G1 后 | `module-runtime-story`、`api-server/src/runtime_story/*`、`src/hooks/rpg-runtime-story/*` | 非 RPG 创作域 | 先将历史 `module-runtime-story-compat` 迁为新主链 crate,再删除 HTTP compat 层、接 session scoped 新接口、前端匹配新接口 | `cargo test -p module-runtime-story`,runtime story/API/前端定向测试 | -| WP-ST SpacetimeDB Adapter | 进行中;已完成 AI task event、Big Fish readiness event、Asset row mapper、Puzzle publish event、Gameplay/Custom World 根入口瘦身、Custom World Agent action 确定性编排和 Auth adapter 目录化切片,其他上下文和绑定生成仍待推进 | 未认领 | 领域任务输出稳定后 | `spacetime-module/src/**`、`migration.rs`、表目录 | `api-server` 业务逻辑 | table/reducer/procedure/mapper/queries 按上下文接入领域函数;必要 event/projection table;`lib.rs/migration.rs/表目录` 单 owner 合流;已完成 AI task event、Big Fish readiness event、Asset row mapper、Puzzle publish event、Gameplay 根入口瘦身、Custom World 根入口瘦身、Auth adapter 目录化 | `cargo check -p spacetime-module`,需要时 `spacetime build/generate` | -| WP-SC Spacetime Client | 进行中;story runtime projection inventory source 接线切片已关闭,读取投影已纳入稳定 runtime inventory facade;后续仅随 WP-ST 新 facade 稳定后继续 typed facade/row mapper | 未认领 | 对应 WP-ST facade 稳定后 | `spacetime-client/src/**`、绑定 mapper | 领域规则、未稳定 facade 的预判接线 | typed facade、错误映射、row snapshot mapper | `cargo check -p spacetime-client` | +| WP-RPG Gameplay 域 | 已完成;combat/inventory/npc/progression/quest/runtime-item/story 的 DDD 物理拆分、跨域结算计划和 SpacetimeDB adapter 消费计划均已收口,完整 runtime story 写侧入口继续归 `WP-RS/WP-API/WP-FE` | 未认领 | 后续只随 `WP-RS/WP-DEL/WP-V` 增量维护 | `module-combat`、`module-inventory`、`module-npc`、`module-progression`、`module-quest`、`module-runtime-item`、`module-story` | 创作域 | 战斗、背包、NPC、成长、任务、宝箱、story session 纯规则与跨域事件 | `cargo test -p module-combat -p module-inventory -p module-npc -p module-progression -p module-quest -p module-runtime-item -p module-story` | +| WP-RS Runtime Story 去兼容层 | 已完成;session scoped 开局与动作写入口已切到 `/api/story/sessions/runtime` 和 `/api/story/sessions/{storySessionId}/actions/resolve`,返回 projection 并保持旧 `/api/runtime/story/*` 未挂载 | 未认领 | 后续只随 WP-DEL/WP-V 增量维护 | `module-runtime-story`、`api-server/src/story_sessions.rs`、story runtime contract | 非 RPG 创作域 | 旧 HTTP compat 层不恢复;运行时快照、story event 和 runtime projection 由 server-rs 主链闭合 | `cargo test -p module-runtime-story`,`cargo test -p api-server story_sessions`,前端 runtime story 定向测试 | +| WP-ST SpacetimeDB Adapter | 已完成;当前稳定 adapter 范围已完成 table/reducer/procedure、event table、表目录、migration 白名单、Rust bindings 和 DDD 门禁闭环,后续仅随新增 SpacetimeDB facade 增量维护 | 未认领 | 后续只随新增 table/reducer/procedure 或 row shape 增量维护 | `spacetime-module/src/**`、`migration.rs`、表目录、bindings 生成脚本 | `api-server` 业务逻辑 | table/reducer/procedure/mapper/queries 按上下文接入领域函数;必要 event/projection table;`lib.rs/migration.rs/表目录` 单 owner 合流;已完成 AI task event、Big Fish readiness event、Asset row mapper、Asset event、Puzzle publish event、Gameplay/Custom World 根入口瘦身、Custom World Agent action 确定性编排、Auth adapter 目录化和 bindings 生成闭环 | `cargo check -p spacetime-module`,`npm.cmd run spacetime:generate -- --rust-only`,`npm.cmd run check:server-rs-ddd` | +| WP-SC Spacetime Client | 已完成;当前稳定 facade 范围内 typed facade、错误 helper、row snapshot mapper、story runtime inventory source 接线和 README 状态已收尾,后续仅随 WP-ST 新 facade 另开增量切片 | 未认领 | 对应 WP-ST facade 稳定后增量维护 | `spacetime-client/src/**`、绑定 mapper | 领域规则、未稳定 facade 的预判接线 | typed facade、错误映射、row snapshot mapper | `cargo test -p spacetime-client`,`cargo check -p spacetime-client` | | WP-PF platform side effects | 已完成;LLM/OSS/SMS/微信平台副作用错误分类与 API 接线错误模型已统一,微信 OAuth provider 已下沉到 `platform-auth` | 未认领 | G1 后可独立准备;接入 API 前与 WP-API 对齐错误模型 | `platform-*`、`api-server` platform 接线 | 领域状态机 | LLM、OSS、SMS、微信等副作用统一 adapter | platform crate 测试或 API smoke | -| WP-API api-server BFF | 进行中;story/game facade 当前阶段收口切片已关闭,runtime projection、story battle DTO 和 owner guard 已推进,更多 session scoped 写接口依赖 WP-ST/WP-SC | 未认领 | WP-SC facade 和 WP-PF 接口稳定后 | `api-server/src/**`,其中 `app.rs` 单 owner | SpacetimeDB table 定义、领域主规则、绕过 spacetime-client 的直连实现 | 路由、鉴权、SSE、请求响应映射、平台编排收口 | `cargo test -p api-server`,`cargo check -p api-server` | -| WP-FE-S Frontend API client 迁移 | 进行中;story session API client 收口切片已关闭,已补齐 begin/continue/state/projection 新主链请求封装与定向测试;开局快照和完整动作结算仍等待后端新写接口 | 未认领 | G1 和 WP-API 契约稳定后 | `src/services/**`、必要 contract type import | hooks / components 大改 | API client、路径常量、请求体与响应解析对齐新 contract | `npm.cmd run test -- src/services` | -| WP-FE-H Frontend hooks 迁移 | 进行中;RPG runtime story 读取侧 hooks 接线切片已关闭,完整开局/动作写接口 hooks 迁移与组件接线仍等待后端新接口 | 未认领 | WP-FE-S 完成且后端接口可用后 | `src/hooks/**`、必要 hook 测试 | components 大面积 UI 改版 | hooks 改为调用新 client,只保留 loading/error/transition 和 UI 临时态 | `npm.cmd run test -- src/hooks` | -| WP-FE-C Frontend components 接线 | 进行中;RPG runtime shell 组件测试夹具接线切片已关闭,组件测试 mock 已对齐当前 hooks UI 出口;完整组件迁移仍等待后端写接口和 hooks 写侧稳定 | 未认领 | WP-FE-H 完成后 | `src/components/**`、组件测试 | services / hooks contract 改动 | 组件接入新 hooks 和 DTO;不新增规则说明文案 | 相关组件 vitest / 交互测试 | -| WP-DEL 删除旧层与命名收口 | 暂不可执行;需等待后端新接口、前端迁移和旧引用替换完成 | 未认领 | 新接口与前端迁移后 | 旧 compat、旧 facade、旧 contract、旧测试 | 新主链 | 物理删除旧入口、旧命名、旧 fixture 中非必要样本 | 搜索无运行代码引用旧层 | -| WP-V 全链验证与发布 smoke | 暂不可执行;必须在 WP-DEL 后串行执行,当前仍处第 1/2/3 批交错推进 | 未认领 | 最后 | 文档、测试脚本、README | 新功能扩展 | 全链命令、Maincloud smoke、文档交接 | 第 8 节命令通过或记录非本轮阻塞 | +| WP-API api-server BFF | 已完成;story/game facade、runtime projection、story battle DTO、owner guard、Custom World runtime 资产入口、平台错误映射和 `/api/llm/chat/completions` 流式 SSE 代理均已收口,后续新 session scoped 写接口随 `WP-RS/WP-ST/WP-SC` 增量接入 | 未认领 | WP-SC facade 和 WP-PF 接口稳定后 | `api-server/src/**`,其中 `app.rs` 单 owner | SpacetimeDB table 定义、领域主规则、绕过 spacetime-client 的直连实现 | 路由、鉴权、SSE、请求响应映射、平台编排收口 | `cargo test -p api-server`,`cargo check -p api-server` | +| WP-FE-S Frontend API client 迁移 | 已完成;runtime story service 写侧已切到新 story session scoped mutation response,前端从 projection 水合 snapshot,不再消费旧 `snapshot/viewModel/presentation/patches` 组合 | 未认领 | 后续只随 WP-DEL/WP-V 增量维护 | `src/services/rpg-runtime/**`、`packages/shared/src/contracts/story.ts` | hooks / components 大改 | API client、路径常量、请求体与响应解析对齐新 contract | `npm.cmd run test -- src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts` | +| WP-FE-H Frontend hooks 迁移 | 已完成;runtime story hooks 写侧和读取侧均通过 `storySessionId` scoped client,背包/NPC/战斗表现只消费 service 返回的投影快照与可选 presentation | 未认领 | WP-FE-S 完成后已关闭 | `src/hooks/rpg-runtime-story/**`、必要 hook 测试 | components 大面积 UI 改版 | hooks 改为调用新 client,只保留 loading/error/transition 和 UI 临时态 | `npm.cmd run test -- src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts` | +| WP-FE-C Frontend components 接线 | 已完成;RPG runtime shell 组件继续只消费 hooks 出口,不拼接任何 runtime story API 路径,组件测试夹具对齐新 hooks 结果形状 | 未认领 | WP-FE-H 完成后已关闭 | `src/components/rpg-runtime-shell/**`、组件测试 | services / hooks contract 改动 | 组件接入新 hooks 和 DTO;不新增规则说明文案 | `npm.cmd run test -- src/components/rpg-runtime-shell/RpgRuntimeShell.test.tsx` | +| WP-DEL 删除旧层与命名收口 | 已完成;旧 runtime story HTTP DTO、前端 `Rpg*` runtime story alias、旧 `/api/custom-world/*` 非 runtime 前缀、Puzzle `local-next-level` 入口和 `/generated-*` 资产直读代理已物理删除 | 未认领 | 新接口与前端迁移后 | 旧 compat、旧 facade、旧 contract、旧测试 | 新主链 | 物理删除旧入口、旧命名、旧 fixture 中非必要样本 | 搜索无运行代码引用旧层 | +| WP-V 全链验证与发布 smoke | 已完成;WP-DEL 后串行执行 SpacetimeDB bindings 生成、Rust/TS/encoding/DDD 全链验证和 Maincloud healthz smoke | 未认领 | 已关闭 | 文档、测试脚本、README | 新功能扩展 | 全链命令、Maincloud smoke、文档交接 | 命令通过;Maincloud 启动恢复快照出现 503 警告但 `/healthz` 200,smoke 后已清理进程和 3100 端口 | ## 4.1 未完整收口内容整合清单 @@ -186,21 +186,19 @@ LLM、OSS、SMS、微信等外部副作用可以独立准备,不等待 `WP-SC` | 归属工作包 | 未完整收口项 | 当前证据 | 收口边界 | 依赖与优先级 | | --- | --- | --- | --- | --- | -| `WP-RPG Gameplay 域` | 成长系统归属 RPG 域;`module-progression` 真实首版与 DDD 物理拆分已收口,完整 RPG 成长闭环仍需继续接跨域链路 | `server-rs/crates/module-progression/README.md` 已说明 DDD 物理拆分完成;`src/domain.rs`、`commands.rs`、`application.rs`、`events.rs`、`errors.rs` 已承载真实类型、命令、应用规则、事件和错误;`cargo test -p module-progression` 通过 | 已关闭本项“分层文件仍是过渡壳”的漂移;继续保持在 `WP-RPG` 内推进,不拆成通用 Runtime 能力;章节蓝图输入、章节预算审计、quest/combat/npc 联动事件继续跟随跨域主链 | 已关闭拆分漂移;完整成长闭环仍是 P0,依赖 `module-custom-world` 章节蓝图 Rust 化边界稳定,随后接 `WP-ST/WP-SC/WP-API` | -| `WP-RPG Gameplay 域` | `module-story` README 与 DDD 物理拆分漂移已关闭;后续只剩完整 story action 写侧与跨域事件继续排队 | `server-rs/crates/module-story/README.md` 已改为当前真实边界;`src/domain.rs`、`commands.rs`、`application.rs`、`events.rs`、`errors.rs` 已承载真实类型、命令、应用映射、事件和错误;`cargo test -p module-story` 通过 | 保持 `module-story` 作为 story session 纯领域薄层;不恢复旧 `/api/runtime/story/*` compat route;完整动作结算继续跟随 `WP-RS/WP-ST/WP-SC/WP-API/WP-FE` | 已关闭本项漂移;剩余写侧依赖仍在 P0 主链 | -| `WP-RPG Gameplay 域` | `module-combat`、`module-inventory`、`module-npc`、`module-quest`、`module-runtime-item` 的 DDD 物理拆分漂移已关闭;完整跨域组合结算仍排队 | 上述 crate 的 `domain/commands/application/events/errors` 已承载真实类型、命令、应用规则、事件和错误;RPG 六个子域源码不再命中 `过渡落位`;`cargo test -p module-combat -p module-inventory -p module-npc -p module-progression -p module-quest -p module-runtime-item` 通过 | 已关闭本项“分层文件仍是过渡壳”的漂移;跨域副作用继续只输出领域事件,不在单个 crate 内互相直连;完整 story action 写侧与组合结算跟随后续主链 | 已关闭拆分漂移;完整跨域写侧仍是 P0,依赖 `WP-RS/WP-ST/WP-SC/WP-API/WP-FE` | -| `WP-RS Runtime Story 去兼容层` | `module-runtime-story` 顶层 DDD 物理拆分漂移已关闭,但旧写接口仍未完整替代 | `module-runtime-story/src/domain.rs`、`commands.rs`、`application.rs`、`events.rs`、`errors.rs` 已承载顶层真实类型、命令 helper、应用 helper、事件和错误;`cargo test -p module-runtime-story` 通过;旧前端写 client 与 `RuntimeStoryActionResponse` 等残留仍等待 `WP-FE-S/WP-DEL` | 已关闭本项“DDD 文件仍是过渡壳”的漂移;继续补齐 session scoped 开局、动作结算、inventory action、NPC interaction、forge/battle/quest 写接口;前端只接新 `/api/story/*` 与新 contract | 已关闭拆分漂移;旧写接口替换仍是 P0,依赖 `WP-ST/WP-SC` facade,完成后解锁 `WP-FE-S/WP-FE-H/WP-FE-C/WP-DEL` | -| `WP-CW Custom World` | Custom World agent 最小兼容占位已关闭,真实动作改为 SpacetimeDB 内确定性状态编排;完整外部副作用链仍待 `WP-AS/WP-PF/WP-API` | `spacetime-module/src/custom_world/mod.rs` 已移除 `execute_placeholder_custom_world_action`;`generate_characters`、`generate_landmarks`、`generate_role_assets`、`sync_role_assets`、`generate_scene_assets`、`sync_scene_assets`、`expand_long_tail` 已分别写回 draft profile、draft card、asset coverage、publish gate 或 long-tail 状态;`cargo check -p spacetime-module` 通过 | 已关闭“最小兼容占位”漂移;LLM、图片生成、OSS、资产对象确认仍只通过 `WP-API/WP-PF/WP-AS` 编排,不放进 reducer/procedure | 已关闭 P0 占位项;外部副作用和资产对象全链仍随 `WP-AS/WP-PF/WP-API` 继续 | -| `WP-CW Custom World` | `module-custom-world` DDD 物理拆分已收口;profile/agent/draft/gallery/publish gate 的 API/前端全链仍待后续推进 | `module-custom-world/src/lib.rs` 已收口为模块声明、公开导出和测试;`domain/commands/application/events/errors` 已承载真实类型、命令、规则、事件和错误;`module-custom-world/README.md` 已更新当前边界;源码不再命中 `过渡落位`;`cargo test -p module-custom-world` 通过 | 已关闭“分层文件仍是过渡壳”的漂移;继续保持 SpacetimeDB 表和 procedure 由 `WP-ST` 落地,API/前端只消费稳定 facade | 已关闭拆分漂移;完整创作全链仍是 P0/P1 主链,依赖 `WP-ST/WP-SC/WP-API/WP-FE` | +| `WP-RPG Gameplay 域` | RPG 七个领域 crate 与跨域组合结算已收口 | `module-combat`、`module-inventory`、`module-npc`、`module-progression`、`module-quest`、`module-runtime-item`、`module-story` 均已完成 DDD 文件承载;`module-story` 新增 `RpgGameplaySettlementPlan`,战斗胜利、任务交付和宝箱奖励的背包、成长、章节账本计划已进入纯领域层;`spacetime-module/src/gameplay/mod.rs` 改为消费计划,`cargo test` 覆盖 RPG 七个领域 crate,`cargo check -p spacetime-module` 通过 | 已关闭“完整跨域组合结算仍排队”的漂移;完整 runtime story 写侧入口、旧 contract 删除和前端写侧迁移继续归 `WP-RS/WP-FE/WP-DEL`,不再阻塞 `WP-RPG` | 已关闭;后续只随 `WP-RS/WP-DEL/WP-V` 增量维护 | +| `WP-RS Runtime Story 去兼容层` | `module-runtime-story` 顶层 DDD 物理拆分和 session scoped 写入口已关闭 | `module-runtime-story/src/domain.rs`、`commands.rs`、`application.rs`、`events.rs`、`errors.rs` 已承载顶层真实类型;`api-server/src/story_sessions.rs` 已提供 `/api/story/sessions/runtime` 和 `/api/story/sessions/{storySessionId}/actions/resolve`;写入口返回 `StoryRuntimeMutationResponse.projection`,旧 `/api/runtime/story/*` 仍未挂载 | 已关闭“DDD 文件仍是过渡壳”和“旧写接口未完整替代”的漂移;旧 alias、旧展示 DTO 和历史文档清理由 `WP-DEL` 统一处理 | 已关闭;后续只随 `WP-DEL/WP-V` 增量维护 | +| `WP-CW Custom World` | Custom World agent 最小兼容占位、确定性动作编排、runtime API 主入口和 RPG 创作资产 client 主路径已关闭 | `spacetime-module/src/custom_world/mod.rs` 已移除 `execute_placeholder_custom_world_action`;`generate_characters`、`generate_landmarks`、`generate_role_assets`、`sync_role_assets`、`generate_scene_assets`、`sync_scene_assets`、`expand_long_tail` 已分别写回 draft profile、draft card、asset coverage、publish gate 或 long-tail 状态;`api-server/src/app.rs` 已补齐 `/api/runtime/custom-world/scene-image`;`src/services/rpg-creation/rpgCreationAssetClient.ts` 已切到 `/api/runtime/custom-world/*`;`rpgCreationAssetClient.test.ts` 覆盖三类资产入口 | 已关闭“最小兼容占位”和“前端继续调用旧资产入口”的漂移;LLM、图片生成、OSS、资产对象确认仍只通过 `WP-API/WP-PF/WP-AS` 编排,不放进 reducer/procedure | 已关闭;后续外部副作用能力和资产对象深水区只随 `WP-AS/WP-PF/WP-V` 增量维护 | +| `WP-CW Custom World` | `module-custom-world` DDD 物理拆分、profile/agent/draft/gallery/publish gate 当前稳定 facade/API 主链已收口 | `module-custom-world/src/lib.rs` 已收口为模块声明、公开导出和测试;`domain/commands/application/events/errors` 已承载真实类型、命令、规则、事件和错误;`spacetime-client/src/custom_world.rs` 已具备 profile、gallery、agent session、message、action、operation、card detail、works facade;Custom World runtime route 已覆盖 profile/library/gallery/agent/works/entity/scene-npc/scene-image/cover-image/cover-upload;源码不再命中 `过渡落位` | 已关闭“分层文件仍是过渡壳”和“稳定 facade 未接 API/前端主入口”的漂移;旧 `/api/custom-world/*` 非 runtime 前缀已由 WP-DEL 取消挂载,不再作为 RPG 创作主 client 路径 | 已关闭;后续随 `WP-V` 验证,不阻塞 `WP-CW` 完成 | | `WP-BF Big Fish` | `module-big-fish` 已完成 DDD 物理拆分和迁移期口径清理 | `module-big-fish/src/lib.rs` 已收口为模块声明、公开导出和测试;`domain/commands/application/events/errors` 已承载创作域类型、命令、应用规则、事件和错误;新增 `module-big-fish/README.md`;源码不再命中 `过渡落位`;`cargo test -p module-big-fish` 通过 | 已关闭“文档完成但 lib.rs 仍承载大量规则”的漂移;不改 SpacetimeDB/API/前端 shape | 已关闭;后续只随全链验证和回归维护 | -| `WP-PZ Puzzle` | Puzzle API、前端消费和运行态全链仍未完成;DDD 注释漂移已清理 | 第 4 节仍标记 `WP-PZ` 进行中;`module-puzzle/README.md` 写明当前只固定 contract 与最小规则;`domain.rs`、`events.rs` 文件头已改为当前领域口径;源码不再命中 `过渡落位`;`cargo test -p module-puzzle` 通过 | 补齐 agent session、work profile、runtime run、排行榜、图片选择和结果页草稿的领域应用服务,再接 `WP-ST/WP-SC/WP-API/WP-FE` | P1;与 `WP-CW` 并行时避免共改创作 agent 通用文档 | -| `WP-AS Assets` | 资产对象类型、领域测试和 row mapper 已完成,资产 API、OSS adapter、facade 和 event table 尚未全链收口;DDD 注释漂移已清理 | 第 4 节仍标记 `WP-AS` 进行中;`module-assets/README.md` 写明尚未进入完整资产状态建模;`module-assets/src/events.rs` 文件头已改为当前领域口径;源码不再命中 `过渡落位`;`cargo test -p module-assets` 通过 | 将 asset object、binding、manifest、history、OSS head/upload 和确认状态统一成领域应用结果,SpacetimeDB 与 API 只做 adapter | P1;依赖 `WP-PF` OSS 错误模型,反向支撑 `WP-CW/WP-PZ/WP-BF` | -| `WP-API api-server BFF` | LLM 流式代理、微信登录、手机号登录、角色动画资产仍有首版禁用或占位链 | `api-server/src/llm.rs` 流式请求返回 `501`;`wechat_auth.rs` 返回“微信登录暂未启用”;`phone_auth.rs`、`password_management.rs` 返回“手机号登录暂未启用”;`character_animation_assets.rs` 仍写 Stage 1 序列帧和视频占位链 | API 层只做 BFF 编排、鉴权、SSE 和 DTO 映射;真实副作用落 `platform-llm/platform-auth/platform-oss`,领域状态变化必须回写 SpacetimeDB facade | P1;依赖 `WP-PF` 能力与 `WP-ST/WP-SC` 写入 facade | -| `WP-FE-S/WP-FE-H/WP-FE-C` | 前端读取侧已推进,完整开局和动作写侧仍等后端新接口;不能继续补旧 compat client | 文档记录 `beginRuntimeStorySession`、`resolveRuntimeStoryAction` 暂未迁移;旧 `src/services/rpg-runtime/rpgRuntimeStoryClient.ts` 写侧和测试仍等待新接口 | `WP-FE-S` 先换 API client,`WP-FE-H` 再换 hooks,`WP-FE-C` 最后接组件;前端只做表现和临时 UI 状态,不重建规则 | P0;强依赖 `WP-RS/WP-ST/WP-SC/WP-API` | -| `WP-ST SpacetimeDB Adapter` | 多个上下文已根入口瘦身,但剩余 table/reducer/procedure、migration、表目录和绑定生成仍未整体闭环 | 第 4 节仍标记 `WP-ST` 进行中;Custom World 真实动作、RPG 写接口、Puzzle/Assets 完整链都需要继续接 `spacetime-module` | 所有 SpacetimeDB schema 变更由 `WP-ST` 单 owner 合流,并同步 `migration.rs`、表目录和生成绑定;禁止 API 直连生成绑定绕过 `spacetime-client` | P0;是 `WP-SC/WP-API/WP-FE/DEL` 的主依赖 | -| `WP-SC Spacetime Client` | 已接 runtime projection inventory source,但后续 typed facade、row mapper 和错误映射仍随 `WP-ST` 滚动补齐 | 第 4 节仍标记 `WP-SC` 进行中;文档记录“后续仅随 WP-ST 新 facade 稳定后继续 typed facade/row mapper” | 只在 reducer/procedure shape 稳定后补 typed facade;不在 client crate 内发明领域规则或预判 row shape | P0;跟随 `WP-ST` 分批执行 | -| `WP-DEL 删除旧层与命名收口` | 旧 compat、旧 contract、旧 facade、旧测试仍不能删除 | 第 4 节标记暂不可执行;`RuntimeStoryActionResponse`、旧 runtime story contract、旧前端写 client 等仍等待新写接口和前端迁移完成 | 所有新接口和前端迁移完成后再统一物理删除;删除前必须搜索运行代码无旧层引用 | P0;依赖 `WP-FE-S/WP-FE-H/WP-FE-C` 完成 | -| `WP-V 全链验证与发布 smoke` | 全链验证仍被旧层删除和 Maincloud smoke 阻塞 | 第 4 节标记暂不可执行;此前 `api-server:maincloud` 多次以常驻服务超时或端口占用形式结束,需要在最终阶段统一验证 | 串行执行 cargo、npm、encoding、DDD boundary、SpacetimeDB build/generate 和 Maincloud smoke;只记录非本轮阻塞,不新增功能 | P2;必须在 `WP-DEL` 后 | +| `WP-PZ Puzzle` | 正式平台运行态全链已收口;旧兼容入口物理删除窗口已关闭 | `module-puzzle/README.md` 已更新后端真相源口径;新增 `SERVER_RS_DDD_WP_PZ_RUNTIME_BACKEND_TRUTH_CLOSURE_2026-05-01.md`;`PlatformEntryFlowShellImpl` 不再导入 `puzzleLocalRuntime`;结果页草稿预览、作品开局、交换、拖动、下一关和排行榜均调用后端 API;`/puzzle` 调试直达页仍保留本地 runtime;旧 `/runs/local-next-level` 已由 WP-DEL 删除 | 已关闭正式平台 Puzzle 主链;`puzzleLocalRuntime` 仅允许作为 `/puzzle` 调试直达页开发辅助,正式 API 只保留 `/runs/{run_id}/next-level` | 已关闭;后续只随 `WP-V` 增量维护 | +| `WP-AS Assets` | 当前资产主链已收口;生成资产读取统一走 OSS read-url 链路 | 新增 `SERVER_RS_DDD_WP_AS_ASSET_CHAIN_CLOSURE_2026-05-01.md`;`module-assets` 已补齐资产领域事件;`spacetime-module` 新增 `asset_event` public event table 并在对象确认、实体绑定变更后写事件;`migration.rs`、表目录和 Rust bindings 已对齐;`module-assets/README.md` 已更新完成口径;`/generated-*` 直读代理已在 WP-DEL 移除 | 正式资产状态仍以 `asset_object` 和 `asset_entity_binding` 为准;`asset_event` 只做订阅和审计事实;`asset_job`、`asset_manifest`、专业资产生成表另开切片;`/generated-*` 仅作为 legacyPublicPath/object key 标识 | 已关闭;后续只随 `WP-PF/WP-DEL/WP-V` 或新增资产生成需求增量维护 | +| `WP-API api-server BFF` | 当前稳定 BFF 范围已收口;LLM 流式代理已从 `501` 改为真实 SSE,手机号/微信属于配置门控,角色动画资产深水区不阻塞 WP-API | `api-server/src/llm.rs` 已通过 `platform-llm::stream_text` 输出 `delta/complete/[DONE]`;`wechat_auth.rs`、`phone_auth.rs`、`password_management.rs` 的“暂未启用”由配置决定且定向测试可开启;`character_animation_assets.rs` 的 Stage 1 序列帧/视频占位链继续归 `WP-AS/WP-PF/WP-V` 深水区 | API 层只做 BFF 编排、鉴权、SSE 和 DTO 映射;后续新增写接口必须等待 `WP-ST/WP-SC` facade 稳定,不在 API 层复制领域规则 | 已关闭;后续只随 `WP-RS/WP-ST/WP-SC/WP-AS/WP-V` 增量维护 | +| `WP-FE-S/WP-FE-H/WP-FE-C` | 前端 runtime story service/hooks/components 已切到新 story session scoped 写读主链 | `beginRuntimeStorySession` 调用 `/api/story/sessions/runtime`;`resolveRuntimeStoryAction` 调用 `/api/story/sessions/{storySessionId}/actions/resolve` 并发送扁平 `ResolveStoryRuntimeActionRequest`;hooks 从 projection 水合快照并通过 `inventoryView` 消费背包视图;组件层不拼 API | 已关闭前端写读迁移;前端只做表现和临时 UI 状态,不重建规则 | 已关闭;旧 alias 与旧 DTO 物理删除转入 `WP-DEL` | +| `WP-ST SpacetimeDB Adapter` | 当前稳定范围已收尾;后续只随新增 SpacetimeDB facade 或 schema shape 变化增量维护 | 新增 `SERVER_RS_DDD_WP_ST_CLOSURE_2026-05-01.md`;`asset_event` 已对齐表目录、`migration.rs` 和 Rust bindings;`npm.cmd run spacetime:generate -- --rust-only` 已可在 Windows formatter 失败时自动分批格式化;DDD 门禁已检查表目录与迁移白名单漂移 | 所有后续 SpacetimeDB schema 变更仍由 `WP-ST` 单 owner 合流,并同步 `migration.rs`、表目录和生成绑定;禁止 API 直连生成绑定绕过 `spacetime-client` | 当前项关闭;后续作为 `WP-SC/WP-API/WP-FE/DEL` 的增量依赖 | +| `WP-SC Spacetime Client` | 当前稳定 facade 范围已收尾;后续新增 typed facade、row mapper 和错误映射只随 `WP-ST` 新 facade 另开增量切片 | 第 4 节已标记 `WP-SC` 完成;`spacetime-client/README.md` 已更新完成口径;错误 helper 补齐 `validation_failed` 与 `reducer_failed`;`cargo test -p spacetime-client` 通过后作为关闭证据 | 只在 reducer/procedure shape 稳定后补 typed facade;不在 client crate 内发明领域规则或预判 row shape | 已关闭;后续跟随 `WP-ST` 增量维护 | +| `WP-DEL 删除旧层与命名收口` | 旧层物理清理已关闭 | 旧 `RuntimeStoryStateResolveRequest`、`RuntimeStoryBootstrapRequest/Response`、`RuntimeStoryActionResponse`、TS `RuntimeActionRequest/Response`、前端 `beginRpgRuntimeStorySession`/`resolveRpgRuntimeStoryAction` 等 alias、旧 `/api/custom-world/*` 非 runtime 前缀、Puzzle `/runs/local-next-level` 和 `/generated-*` 资产直读代理已删除;新增旧路由未挂载测试 | 已完成旧入口、旧命名和非必要 fixture 删除;`/generated-*` 仅保留为 read-url 入参标识;story battle 等独立 presentation 语义继续保留为当前投影表现类型 | 已关闭;后续进入 `WP-V` | +| `WP-V 全链验证与发布 smoke` | 已关闭;WP-DEL 后全链验证和 Maincloud smoke 已完成 | `npm.cmd run spacetime:generate -- --rust-only` 通过,Windows 长路径格式化失败由脚本短路径分批 `rustfmt` fallback 完成;`cargo fmt --all --check`、`cargo check -p spacetime-module`、`cargo test --workspace --exclude spacetime-module`、`npm.cmd run check:encoding`、`npm.cmd run check:server-rs-ddd` 均通过;`npm.cmd run api-server:maincloud` 拉起后 `/healthz` 返回 200,smoke 后确认无 `api-server` 残留进程且 3100 端口已释放 | Maincloud 启动恢复认证快照阶段出现两条 `503 Service Unavailable` 警告,但不影响服务启动和 healthz;不新增功能 | 已关闭 | | `G2/跨包清理` | `过渡落位` 迁移期口径已清零;后续只保留旧 compat/API/FE 链路的真实依赖项 | 搜索 `server-rs/crates` 不再命中 `过渡落位`;`module-runtime`、`module-puzzle`、`module-assets` 文件头已更新;Big Fish 物理拆分漂移已关闭 | 后续 `WP-DEL/WP-V` 只关注旧 compat、旧 contract、旧 facade、旧测试和全链 smoke,不再把已清理的注释漂移重复认领 | 已关闭 P2 清理项;最终验收继续依赖 `WP-DEL/WP-V` | | `tests-support` | 已从目录占位收口为 workspace 共享测试支撑 crate | `server-rs/Cargo.toml` 已纳入 `crates/tests-support`;`server-rs/crates/tests-support/src/lib.rs` 提供 Maincloud healthz 默认地址、smoke URL 归一化、HTTP 2xx 断言和 healthz 非空响应体断言;`cargo test -p tests-support` 通过 | 已关闭“只有 README 的悬空占位”;后续 contract/reducer/view/projection 夹具只在真实测试策略稳定后继续扩展 | 已关闭 P2 占位项;后续随 `WP-V` 复用和扩展 | @@ -429,6 +427,41 @@ spacetime describe --json ## 10. 本地进度记录 +### 2026-05-01 WP-FE-S/H/C runtime story 收尾切片 + +已完成: + +1. `WP-FE-S`:`beginRuntimeStorySession` 改为调用 `POST /api/story/sessions/runtime`,`resolveRuntimeStoryAction` 改为调用 `POST /api/story/sessions/{storySessionId}/actions/resolve`。 +2. `WP-FE-S`:前端写侧响应统一消费 `StoryRuntimeMutationResponse.projection`,由 `StoryRuntimeProjectionResponse.gameState` 在 service 边界水合 `HydratedSavedGameSnapshot`,不再读取旧 `snapshot/viewModel/presentation/patches` 组合。 +3. `WP-FE-H`:`resolveServerRuntimeChoice` 只提交 `storySessionId/functionId/actionText/payload`,背包动作改用 `response.inventoryView`,battle presentation 仅在响应显式携带可选 presentation 时播放。 +4. `WP-FE-C`:`RpgRuntimeShell` 组件层保持只消费 hooks 出口,不拼接 `/api/runtime/story/*` 或 `/api/story/*` 路径,不新增规则说明 UI 文案。 +5. `server-rs`:补齐 `/api/story/sessions/runtime` 与 `/api/story/sessions/{storySessionId}/actions/resolve` 鉴权测试,保持旧 `/api/runtime/story/*` 未挂载。 +6. `packages/shared/src/contracts/story.ts` 移除旧 `StoryRuntimeBootstrap*` / `StoryRuntimeAction*` HTTP 写侧类型,只保留新 `BeginStoryRuntimeSessionRequest`、`ResolveStoryRuntimeActionRequest`、`StoryRuntimeMutationResponse`。 +7. `src/data/functionCatalog/flow/campTravelHomeScene.ts` 的 executor 文档指向 `module-runtime-story/src/session_action.rs` 新主链。 + +边界说明: + +1. 本次不新增或修改 SpacetimeDB 表结构,因此不改 `migration.rs`。 +2. 不恢复旧 `/api/runtime/story/*`,不兼容 `server-node`。 +3. `rpgRuntimeStoryState.ts` 中仍存在 view model / presentation 类型,作为旧展示语义与 story battle 表现的后续清理输入,物理删除归 `WP-DEL`。 +4. 历史设计文档中的旧路径引用不作为运行代码阻塞,统一归 `WP-DEL/WP-V` 文档清理。 + +验证: + +```powershell +npm.cmd run test -- src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts +npm.cmd run test -- src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts +npm.cmd run test -- src/hooks/rpg-runtime-story/storyChoiceRuntime.test.ts +npm.cmd run test -- src/components/rpg-runtime-shell/RpgRuntimeShell.test.tsx +cargo fmt --all --check --manifest-path server-rs\Cargo.toml +cargo check --workspace --manifest-path server-rs\Cargo.toml +cargo test -p shared-contracts -p module-runtime-story --manifest-path server-rs\Cargo.toml +cargo test -p api-server story_sessions --manifest-path server-rs\Cargo.toml +npm.cmd run check:server-rs-ddd +``` + +结果:通过。`cargo check` 和 `api-server story_sessions` 仍有既有 `prompt/rpg/runtime_chat.rs` dead code warning,不影响本切片。 + ### 2026-04-29 WP-FE-C RPG runtime shell 组件测试夹具接线切片 已完成: @@ -1233,10 +1266,10 @@ npm.cmd run api-server:maincloud 5. 同步更新 `RUST_API_SERVER_ROUTE_INDEX_2026-04-22.md` 和 G1 契约矩阵,将 runtime story 旧层标记为已删除。 6. 本次未修改 SpacetimeDB 表结构,未触碰 `migration.rs`。 -当前剩余: +2026-05-01 更新: -1. 前端 `src/services/rpg-runtime/rpgRuntimeStoryClient.ts` 仍指向 `/api/runtime/story`,需要在 `WP-FE-S` 中迁到新 `/api/story/*` 和 session scoped facade。 -2. `packages/shared/src/contracts/rpgRuntimeStory*` 与 `shared-contracts/src/runtime_story*` 仍保留旧 DTO,需等前端迁移完成后由 `WP-DEL` 统一删除。 +1. 前端 `src/services/rpg-runtime/rpgRuntimeStoryClient.ts` 已迁到 `/api/story/sessions/runtime`、`/api/story/sessions/{storySessionId}/runtime-projection` 和 `/api/story/sessions/{storySessionId}/actions/resolve`。 +2. `packages/shared/src/contracts/rpgRuntimeStory*` 与 `shared-contracts/src/runtime_story*` 仍保留历史展示 DTO,需由 `WP-DEL` 统一评估 story battle 表现依赖后删除。 3. runtime chat 仍有 `runtimeStory` 命名和 prompt helper,需另起 session scoped chat/story facade 任务继续收口。 验证: @@ -1329,7 +1362,7 @@ cargo test -p api-server wechat --manifest-path server-rs/Cargo.toml cargo check -p api-server --manifest-path server-rs/Cargo.toml cargo fmt -p platform-llm -p platform-oss -p platform-auth -p api-server --manifest-path server-rs/Cargo.toml --check npm.cmd run check:server-rs-ddd -npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_PF_PLATFORM_ERROR_CLASSIFICATION_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/platform-llm/src/lib.rs server-rs/crates/platform-oss/src/lib.rs server-rs/crates/platform-auth/src/lib.rs server-rs/crates/platform-auth/Cargo.toml server-rs/crates/api-server/src/platform_errors.rs server-rs/crates/api-server/src/llm.rs server-rs/crates/api-server/src/state.rs server-rs/crates/api-server/src/wechat_provider.rs server-rs/crates/api-server/src/wechat_auth.rs server-rs/crates/api-server/src/assets.rs server-rs/crates/api-server/src/big_fish.rs server-rs/crates/api-server/src/character_visual_assets.rs server-rs/crates/api-server/src/character_animation_assets.rs server-rs/crates/api-server/src/custom_world_ai.rs server-rs/crates/api-server/src/legacy_generated_assets.rs server-rs/crates/api-server/src/puzzle.rs server-rs/crates/api-server/src/phone_auth.rs server-rs/crates/api-server/src/http_error.rs server-rs/crates/api-server/src/main.rs server-rs/crates/module-auth/src/lib.rs +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_PF_PLATFORM_ERROR_CLASSIFICATION_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/platform-llm/src/lib.rs server-rs/crates/platform-oss/src/lib.rs server-rs/crates/platform-auth/src/lib.rs server-rs/crates/platform-auth/Cargo.toml server-rs/crates/api-server/src/platform_errors.rs server-rs/crates/api-server/src/llm.rs server-rs/crates/api-server/src/state.rs server-rs/crates/api-server/src/wechat_provider.rs server-rs/crates/api-server/src/wechat_auth.rs server-rs/crates/api-server/src/assets.rs server-rs/crates/api-server/src/big_fish.rs server-rs/crates/api-server/src/character_visual_assets.rs server-rs/crates/api-server/src/character_animation_assets.rs server-rs/crates/api-server/src/custom_world_ai.rs server-rs/crates/api-server/src/puzzle.rs server-rs/crates/api-server/src/phone_auth.rs server-rs/crates/api-server/src/http_error.rs server-rs/crates/api-server/src/main.rs server-rs/crates/module-auth/src/lib.rs npm.cmd run api-server:maincloud ``` @@ -1500,7 +1533,23 @@ npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_202 1. `spacetime-client/src/*.rs` facade 中已不再保留重复的 `.map_err(|error| SpacetimeClientError::Procedure(error.to_string()))` 模式。 2. `spacetime-client/src/mapper.rs` 中仅保留需要领域语义表达的 `Procedure(...)` 构造。 3. 本次未修改 `spacetime-module`、`migration.rs`、生成绑定、`api-server` 路由或前端。 -4. `WP-SC` 当前基础设施收口已完成;后续只在 `WP-ST` 新 facade 或 row shape 稳定后,按领域补 typed facade 与 row snapshot mapper。 +4. `WP-SC` 当前稳定 facade 范围已完成关闭;后续只在 `WP-ST` 新 facade 或 row shape 稳定后,按领域另开增量切片补 typed facade 与 row snapshot mapper。 + +### 2026-05-01 WP-SC 当前稳定 facade 收尾关闭 + +已完成: + +1. `spacetime-client/src/lib.rs` 补齐 `SpacetimeClientError::validation_failed` 与 `SpacetimeClientError::reducer_failed`,并新增错误 helper 单元测试。 +2. AI reducer callback 的业务错误统一走 `reducer_failed`,保持 API 层按 `Runtime` 分类的既有语义。 +3. combat、inventory、runtime、story facade 的本地命令构造与输入校验错误统一走 `validation_failed`,减少各 facade 内重复 `Runtime(error.to_string())`。 +4. `spacetime-client/README.md` 已从早期占位说明更新为当前完成口径,明确当前稳定 facade 范围已收尾。 +5. `SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md` 已补充 2026-05-01 关闭口径和最终验收命令。 + +边界说明: + +1. 本次不修改 `spacetime-module`、`migration.rs`、生成绑定、HTTP route、shared contract 或前端。 +2. `WP-ST` 后续若新增 table/reducer/procedure 或 row shape,`WP-SC` 只按对应领域增量补 facade/mapper,不再阻塞当前工作包关闭。 +3. `WP-RS/WP-API/WP-FE/WP-DEL` 仍因旧 runtime story 写侧和前端迁移保持进行中。 ### 2026-04-29 WP-ST AI Task 事件 Adapter 切片 @@ -1640,7 +1689,7 @@ npm.cmd run api-server:maincloud 4. `getRuntimeStoryState` 复用 `getStoryRuntimeProjection`,读取侧继续保持不回退 `runtimeSessionId` 的约束。 5. `src/services/rpg-runtime/index.ts` 导出新 story session client 函数和结果类型,供后续 `WP-FE-H` 迁移 hooks 使用。 6. 补回 `src/services/customWorldAgentGenerationProgress.ts` 的“建立场景连接”进度阶段,使前端草稿生成进度重新对齐既有 13 步文档与 service 全量验收。 -7. 更新 `SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md` 与 `docs/technical/README.md`,明确本轮只收口 service client;旧 `beginRuntimeStorySession` 与 `resolveRuntimeStoryAction` 仍等待后端完整开局快照和 session scoped 动作结算接口。 +7. 更新 `SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md` 与 `docs/technical/README.md`,明确首轮只收口 service client;2026-05-01 收尾切片已补齐 `beginRuntimeStorySession` 与 `resolveRuntimeStoryAction` 的新写侧接入。 验证: @@ -1696,12 +1745,8 @@ cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml 2. 新增 `SERVER_RS_DDD_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md`,冻结本次残留审计边界。 3. 清理 `module-runtime-story` 运行代码注释里的 `compat / 兼容` 阶段性定位,将 crate 口径收束为 runtime story 主链纯规则。 4. 更新 `module-runtime-story/README.md`,明确后续要迁的是旧 `/api/runtime/story/*` 写侧能力,而不是继续扩展兼容桥。 -5. 冻结本次不删除的残留: - - `src/services/rpg-runtime/rpgRuntimeStoryClient.ts` 仍指向 `/api/runtime/story` - - `src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts` 仍断言旧写接口路径 - - `packages/shared/src/contracts/rpgRuntimeStoryState.ts` 与 `server-rs/crates/shared-contracts/src/runtime_story.rs` 仍保留 `RuntimeStoryActionResponse` - - `src/hooks/rpg-runtime-story/**` 仍保留部分旧调用面兼容注释 -6. 上述残留需等待 session scoped 新写接口稳定后,由 `WP-FE-S -> WP-FE-H -> WP-DEL` 顺序处理。 +5. 2026-05-01 更新:旧写侧能力已迁入 session scoped route,README 已改为“增强规则与投影,删除旧命名”的口径。 +6. 剩余残留只包括历史展示 DTO、旧命名和历史文档引用,统一转入 `WP-DEL`。 验证: @@ -1932,6 +1977,40 @@ npm.cmd run api-server:maincloud 结果:命令在 124 秒观察窗口内超时,因为 `cargo run` 前台常驻;随后确认 `127.0.0.1:3100` 已由本仓库 `server-rs/target/debug/api-server.exe` 监听。`GET /healthz` 返回 `200`;`GET /api/story/sessions/storysess_001/state`、`GET /api/story/sessions/storysess_001/runtime-projection`、`GET /api/story/battles/battle_001`、`POST /api/story/battles/resolve` 未登录均返回 `401`,旧 `POST /api/runtime/story/actions/resolve` 返回 `404`。 +### 2026-05-01 WP-API BFF 收尾 + +已完成: + +1. 新增 `SERVER_RS_DDD_WP_API_BFF_CLOSURE_2026-05-01.md`,记录本次 WP-API 当前稳定范围关闭口径。 +2. `api-server/src/llm.rs` 移除 `stream=true` 返回 `501` 的首版禁用分支,改为调用 `platform-llm::LlmClient::stream_text`。 +3. `/api/llm/chat/completions` 流式响应固定输出 `delta`、`complete` 和 `[DONE]` SSE 事件;上游流式中途失败时输出 `error` SSE 事件。 +4. 缺 LLM API Key 仍在响应开始前返回标准 `SERVICE_UNAVAILABLE` 错误 envelope,非流式 JSON envelope 行为不变。 +5. 为完成全量验证,`module-runtime-story` 补齐测试编译门槛:提高大 JSON 快照构造的宏递归上限、给 `StoryRuntimeActionResolveOutput` 补 `Debug` derive,并移除未用 import。 + +边界说明: + +1. 本次不改 SpacetimeDB 表结构、reducer、procedure、绑定 shape 或 `migration.rs`。 +2. `api-server` 只做鉴权、DTO 映射、SSE 转发和平台错误转换,不新增 LLM prompt 或玩法规则。 +3. 手机号登录与微信登录属于配置门控;角色动画 Stage 1 占位链归资产/模型生成深水区,不阻塞 WP-API 当前稳定范围完成。 +4. 后续新增 session scoped 写接口仍必须等待 `WP-RS/WP-ST/WP-SC` facade 稳定后再接入,禁止 API 层绕过 `spacetime-client`。 + +验证: + +```powershell +cargo fmt --all --check --manifest-path server-rs\Cargo.toml +cargo test -p api-server llm --manifest-path server-rs\Cargo.toml +cargo test -p api-server custom_world_ai --manifest-path server-rs\Cargo.toml +cargo test -p api-server runtime_story_legacy_routes_are_not_mounted --manifest-path server-rs\Cargo.toml +cargo test -p api-server --manifest-path server-rs\Cargo.toml +cargo test -p module-runtime-story --manifest-path server-rs\Cargo.toml +cargo check --workspace --manifest-path server-rs\Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_API_BFF_CLOSURE_2026-05-01.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/api-server/src/llm.rs server-rs/crates/api-server/src/custom_world_ai.rs server-rs/crates/module-runtime-story/src/lib.rs server-rs/crates/module-runtime-story/src/bootstrap.rs server-rs/crates/module-runtime-story/src/session_action.rs +npm.cmd run api-server:maincloud +``` + +结果:通过。`api-server` 全量 203 个测试通过、4 个真实外部服务测试按预期 ignored;`module-runtime-story` 11 个测试通过;workspace 编译通过;DDD 边界检查通过 15 个 module crate;编码检查通过 8 个文件。`api-server:maincloud` 启动后 `GET http://127.0.0.1:3100/healthz` 返回 `200 {"ok":true,"service":"genarrative-api-server"}`,本轮 smoke 未留下常驻进程。启动日志中 Maincloud WebSocket 恢复认证快照短暂返回 `503 Service Unavailable`,未阻止 API server 就绪,属于外部 Maincloud 可用性告警。 + ### 2026-04-29 WP-API story runtime projection route 接线 已完成: @@ -1986,11 +2065,11 @@ npm.cmd run api-server:maincloud 4. `loadRuntimeInventoryView` 改为从新投影映射背包视图。 5. `rpgRuntimeStoryGateway` 的 option catalog / resume 读取侧改为使用 `storySessionId`,缺失时显式失败,不再用 `runtimeSessionId` 兜底。 -当前边界: +2026-05-01 更新: -1. `beginRuntimeStorySession` 暂未迁移,因为新 `/api/story/sessions` 当前只创建 story session,不返回开局 `GameState` 快照。 -2. `resolveRuntimeStoryAction` 暂未迁移,因为新主链尚未提供等价的 story session scoped 动作结算与状态更新回包。 -3. `WP-FE-H` hooks 大迁移继续等待后端写接口稳定;本轮只做读取侧必要网关接线。 +1. `beginRuntimeStorySession` 已迁到 `POST /api/story/sessions/runtime`,返回 `StoryRuntimeMutationResponse.projection`,由 service 边界水合 `GameState` 快照。 +2. `resolveRuntimeStoryAction` 已迁到 `POST /api/story/sessions/{storySessionId}/actions/resolve`,请求体使用扁平 `ResolveStoryRuntimeActionRequest`。 +3. `WP-FE-H` hooks 写侧和读取侧均通过 service client 消费 projection,不再等待后端写接口。 验证: @@ -2261,6 +2340,41 @@ npm.cmd run api-server:maincloud 结果:命令在 60 秒观察窗口内超时,但随后探测 `http://127.0.0.1:3100/healthz` 返回 `200`,本地存在 `api-server` 运行进程。本切片未触发新的 Rust 编译错误。 +### 2026-05-01 WP-AS Assets 资产主链收尾 + +已完成: + +1. 新增 `SERVER_RS_DDD_WP_AS_ASSET_CHAIN_CLOSURE_2026-05-01.md`,记录本次 WP-AS 当前主链关闭口径。 +2. `module-assets/src/events.rs` 补齐 `AssetDomainEvent`、`AssetObjectConfirmedEvent` 和 `AssetEntityBindingChangedEvent`,领域层能表达对象确认与实体绑定变更事实。 +3. `spacetime-module/src/asset_metadata/objects.rs` 新增 `asset_event` public event table,并在 `upsert_asset_object` 成功后写入 `ObjectConfirmed` 事件。 +4. `spacetime-module/src/asset_metadata/bindings.rs` 在 `upsert_asset_entity_binding` 成功后写入 `EntityBindingChanged` 事件。 +5. `migration.rs`、`SPACETIMEDB_TABLE_CATALOG.md` 和 Rust `module_bindings` 已补齐 `asset_event`,订阅端可通过 `asset_event().on_insert(...)` 感知资产主链变化。 +6. `module-assets/README.md` 已更新完成口径:资产对象、绑定、历史、OSS 确认、API facade 和 event table 已闭环。 + +边界说明: + +1. 正式资产状态仍以 `asset_object` 和 `asset_entity_binding` 为准;`asset_event` 只承接订阅和审计所需的轻量事实。 +2. 本次不新增 `asset_job`、`asset_manifest` 或专业资产生成表。 +3. 旧 `/generated-*` 读兼容入口已由 `WP-DEL` 物理删除,生成资产读取统一走 OSS read-url 链路;历史 DTO 中的 `/generated-*` 只作为 `legacyPublicPath` 标识。 +4. `spacetime-client/src/module_bindings/**` 仍是生成物;本次通过 `spacetime generate` 产出 `asset_event_*` 文件,只在 `mod.rs` 做最小接线,避免全目录格式化噪声。 + +验证: + +```powershell +spacetime generate --no-config --lang rust --out-dir C:\g\rs --module-path server-rs\crates\spacetime-module --yes +cargo fmt -p module-assets -p spacetime-module --manifest-path server-rs\Cargo.toml --check +cargo test -p module-assets --manifest-path server-rs\Cargo.toml +cargo check -p spacetime-module --manifest-path server-rs\Cargo.toml +cargo check -p spacetime-module --manifest-path server-rs\Cargo.toml --target wasm32-unknown-unknown +cargo check -p spacetime-client --manifest-path server-rs\Cargo.toml +cargo check -p api-server --manifest-path server-rs\Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_AS_ASSET_CHAIN_CLOSURE_2026-05-01.md docs/technical/SPACETIMEDB_TABLE_CATALOG.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md docs/technical/README.md server-rs/crates/module-assets/README.md server-rs/crates/module-assets/src/events.rs server-rs/crates/module-assets/src/lib.rs server-rs/crates/spacetime-module/src/asset_metadata/objects.rs server-rs/crates/spacetime-module/src/asset_metadata/bindings.rs server-rs/crates/spacetime-module/src/migration.rs server-rs/crates/spacetime-client/src/module_bindings/mod.rs server-rs/crates/spacetime-client/src/module_bindings/asset_event_kind_type.rs server-rs/crates/spacetime-client/src/module_bindings/asset_event_type.rs server-rs/crates/spacetime-client/src/module_bindings/asset_event_table.rs +npm.cmd run api-server:maincloud +``` + +结果:通过。`spacetime generate --no-config --lang rust --out-dir C:\g\rs --module-path server-rs\crates\spacetime-module --yes` 生成成功;`cargo fmt -p module-assets -p spacetime-module` 通过;`module-assets` 8 个测试通过;`spacetime-module` native 与 `wasm32-unknown-unknown` 编译通过;`spacetime-client` 与 `api-server` 编译通过;`api-server assets::tests` 27 个非 ignored 测试通过、3 个 live 测试按需 ignored;DDD 边界检查 15 个 module crate 通过;编码检查 14 个文件通过;`npm.cmd run api-server:maincloud` 已启动到 `127.0.0.1:3100`,`GET /healthz` 返回 `200 {"ok":true,"service":"genarrative-api-server"}`,随后本次 smoke 进程已清理且 3100 端口已释放。`api-server` 仍有既有 runtime prompt 未使用 warning;Maincloud WebSocket 在启动恢复认证快照时返回过 `503 Service Unavailable` warning,但不影响本次本地 healthz smoke。 + ### 2026-04-29 WP-RS 领域投影 builder 切片 已完成: @@ -2385,7 +2499,7 @@ cargo check -p module-combat -p module-inventory -p module-npc -p module-progres 1. 新增 `SERVER_RS_DDD_WP_RS_RUNTIME_STORY_DOMAIN_SPLIT_2026-04-30.md`,记录本次 `module-runtime-story` 顶层收口边界。 2. `module-runtime-story/src/domain.rs` 收口 runtime story 顶层常量、action 结算结果、生成故事 payload、NPC 任务上下文和待接任务上下文。 3. `module-runtime-story/src/commands.rs` 收口 `resolve_action_text`。 -4. `module-runtime-story/src/application.rs` 收口 `RuntimeStoryActionResponseParts`、`simple_story_resolution`、`build_status_patch` 和 `current_world_type`。 +4. `module-runtime-story/src/application.rs` 收口 `simple_story_resolution`、`build_status_patch` 和 `current_world_type`;旧 `RuntimeStoryActionResponseParts` 已在 WP-DEL 删除。 5. `module-runtime-story/src/errors.rs` 补入 `RuntimeStoryRuleError`。 6. `module-runtime-story/src/events.rs` 补入 `RuntimeStoryDomainEvent`。 7. `module-runtime-story/src/lib.rs` 收口为模块声明、公开导出和既有子模块 re-export,保持 `module_runtime_story::*` 公开 API。 @@ -2394,7 +2508,7 @@ cargo check -p module-combat -p module-inventory -p module-npc -p module-progres 1. 本次不迁移旧 `/api/runtime/story/*` 写侧接口,不新增 session scoped 写 route。 2. 本次不改 SpacetimeDB 表、reducer、procedure、绑定 shape 或 `migration.rs`。 -3. `RuntimeStoryActionResponse`、旧前端写 client 和旧 contract 删除仍等待后续 `WP-FE-S/WP-FE-H/WP-FE-C/WP-DEL`。 +3. `RuntimeStoryActionResponse`、旧前端写 client alias 和旧 contract 已在 WP-DEL 删除,当前主链只返回 story runtime projection。 验证: @@ -2435,6 +2549,74 @@ cargo check -p spacetime-module --manifest-path server-rs\Cargo.toml 结果:通过,`module-custom-world` 13 个单元测试通过;`spacetime-module` 编译通过;Custom World 源码不再命中 `execute_placeholder_custom_world_action`、`最小兼容占位` 或 `过渡落位`。 +### 2026-05-01 WP-CW Custom World 全链收尾 + +已完成: + +1. 新增 `SERVER_RS_DDD_WP_CW_FULL_CHAIN_CLOSURE_2026-05-01.md`,记录本次 WP-CW 当前主链关闭口径。 +2. `api-server/src/app.rs` 补齐 `POST /api/runtime/custom-world/scene-image`,与 entity、scene-npc、cover-image、cover-upload 统一进入 runtime Custom World 主链。 +3. `api-server/src/custom_world_ai.rs` 在 scene-image 与 cover-image 的计费资产操作前显式检查 DashScope 配置,缺 key 时稳定返回 `SERVICE_UNAVAILABLE`,不受开发机 `.env.local` 影响。 +4. `api-server/src/custom_world_ai.rs` 的场景图 prompt 测试改为复用正式 prompt compiler 输出作为断言源,避免提示词中文细节演进导致脆弱断言。 +5. `src/services/rpg-creation/rpgCreationAssetClient.ts` 将 RPG 创作资产请求主路径从 `/api/custom-world/*` 切到 `/api/runtime/custom-world/*`。 +6. 新增 `src/services/rpg-creation/rpgCreationAssetClient.test.ts`,覆盖 scene image、landmark/entity、scene npc 三类资产 client 路由。 + +边界说明: + +1. 本次不改 SpacetimeDB 表结构、procedure 签名、绑定 shape 或 `migration.rs`。 +2. 旧 `/api/custom-world/entity`、`/api/custom-world/scene-npc`、`/api/custom-world/scene-image`、`/api/custom-world/cover-image`、`/api/custom-world/cover-upload` 已在 WP-DEL 取消挂载,当前只保留 `/api/runtime/custom-world/*` 主链。 +3. LLM、DashScope 图片生成、OSS 上传仍留在 `api-server + platform-*`;reducer/procedure 只做确定性状态编排和持久化。 +4. 后续若要删除旧入口,归 `WP-DEL/WP-V` 统一搜索、替换和 smoke,不再阻塞 `WP-CW` 完成状态。 + +验证: + +```powershell +cargo fmt --all --check --manifest-path server-rs\Cargo.toml +cargo test -p module-custom-world --manifest-path server-rs\Cargo.toml +cargo test -p api-server custom_world --manifest-path server-rs\Cargo.toml +cargo check -p spacetime-module --manifest-path server-rs\Cargo.toml +cargo check -p spacetime-client --manifest-path server-rs\Cargo.toml +npm.cmd run test -- src/services/rpg-creation/rpgCreationGenerationClient.test.ts src/services/rpg-creation/rpgCreationGenerationClient.node.test.ts src/services/rpg-creation/rpgCreationAssetClient.test.ts +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md docs/technical/SERVER_RS_DDD_WP_CW_FULL_CHAIN_CLOSURE_2026-05-01.md server-rs/crates/api-server/src/custom_world_ai.rs src/services/rpg-creation/rpgCreationAssetClient.ts src/services/rpg-creation/rpgCreationAssetClient.test.ts +npm.cmd run api-server:maincloud +``` + +结果:`module-custom-world` 13 个单元测试通过;`api-server custom_world` 30 个定向测试通过;`spacetime-module` 与 `spacetime-client` 编译通过;RPG 创作 generation/asset client 3 个测试文件 7 个用例通过;DDD 边界检查 15 个 module crate 通过;编码检查 6 个文件通过;`api-server:maincloud` 超时前 `/healthz` 返回 `200 {"ok":true,"service":"genarrative-api-server"}`,本次 smoke 子进程已清理且 `3100` 端口已释放。`cargo fmt --all --check` 已确认 `api-server` 本轮文件格式通过,但当前工作区另有非本轮 `module-story` 格式漂移,需由对应工作包收口。 + +### 2026-05-01 WP-ST SpacetimeDB Adapter 收尾 + +已完成: + +1. 新增 `SERVER_RS_DDD_WP_ST_CLOSURE_2026-05-01.md`,记录本次 WP-ST 当前稳定范围关闭口径。 +2. `asset_event` 已作为资产事件表进入生成绑定,包含 `asset_event_table.rs`、`asset_event_type.rs` 和 `asset_event_kind_type.rs`。 +3. `SPACETIMEDB_TABLE_CATALOG.md` 补齐 `database_migration_operator`、profile 邀请/推荐/会员/充值表,以及 `asset_event` 目录项。 +4. `scripts/check-server-rs-ddd-boundaries.mjs` 增加 SpacetimeDB table、`migration.rs` 白名单和表目录漂移检查。 +5. `scripts/generate-spacetime-bindings.mjs` 在 SpacetimeDB CLI 已产出 Rust bindings 但生成后 formatter 失败时,自动在短临时目录分批 `rustfmt`,再同步到仓库生成目录。 +6. `spacetime-client/README.md` 已同步生成物维护口径,恢复流程统一使用 `npm.cmd run spacetime:generate -- --rust-only`。 + +边界说明: + +1. 本次不新增 story action 写接口,不迁移前端写侧,不删除旧 compat 层。 +2. 后续新增 SpacetimeDB 表、reducer、procedure 或 row shape 时,仍必须由 `WP-ST` 同步 `migration.rs`、表目录和 bindings。 +3. `database_migration_operator` 是迁移权限表,不纳入业务迁移导出白名单。 + +验证: + +```powershell +npm.cmd run spacetime:generate -- --rust-only +cargo fmt --all --check --manifest-path server-rs\Cargo.toml +cargo check -p spacetime-module --manifest-path server-rs\Cargo.toml +cargo check -p spacetime-client --manifest-path server-rs\Cargo.toml +cargo test -p module-assets --manifest-path server-rs\Cargo.toml +cargo test -p spacetime-client --manifest-path server-rs\Cargo.toml +cargo check -p api-server --manifest-path server-rs\Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding +npm.cmd run api-server:maincloud +``` + +结果:通过。SpacetimeDB CLI 完成 module build 和 bindings 产出后,自身 formatter 在 Windows 长参数场景失败;仓库脚本接管分批 `rustfmt` 并成功同步生成目录。`spacetime-module` 与 `spacetime-client` 编译通过,`module-assets` 8 个测试通过,`spacetime-client` 10 个测试通过,DDD 边界检查通过 15 个 module crate,编码检查通过 2815 个文件;`api-server:maincloud` 启动后 `/healthz` 返回 `200 {"ok":true,"service":"genarrative-api-server"}`,本次启动进程已清理并释放 `3100` 端口。当前仍存在 `module-runtime-story` 与 `api-server` 既有 unused warning,非本次 WP-ST 引入。 + ### 2026-04-30 WP-BF 与 G2 迁移期口径清理切片 已完成: diff --git a/docs/technical/SERVER_RS_DDD_WP_API_BFF_CLOSURE_2026-05-01.md b/docs/technical/SERVER_RS_DDD_WP_API_BFF_CLOSURE_2026-05-01.md new file mode 100644 index 00000000..ee9ebdbd --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_API_BFF_CLOSURE_2026-05-01.md @@ -0,0 +1,65 @@ +# server-rs DDD WP-API BFF 收尾(2026-05-01) + +## 1. 背景 + +`WP-API api-server BFF` 的启动切片已关闭旧 `/api/runtime/story/*` 兼容挂载,并把 `/api/story/*`、runtime projection、battle facade、平台错误模型等入口接到当前稳定的 `spacetime-client` 与 `platform-*` 能力上。 + +本收尾切片只处理 BFF 层还可以独立闭合的缺口:`/api/llm/chat/completions` 已具备非流式代理,但 `stream=true` 仍返回 `501`。`platform-llm` 已提供 OpenAI 兼容 SSE 解析和 `stream_text` 增量回调,因此 API 层可以补齐真正 SSE 输出,而不新增领域规则、不改 SpacetimeDB schema。 + +## 2. 收尾目标 + +1. `POST /api/llm/chat/completions` 保持非流式 JSON envelope 行为不变。 +2. `POST /api/llm/chat/completions` 在 `stream=true` 时返回 `text/event-stream`,由 `platform-llm` 负责上游 SSE 解析,API 层只转成前端可消费事件。 +3. 流式事件固定为: + - `delta`:包含 `delta`、`content`、`finishReason`。 + - `complete`:复用 `LlmChatCompletionResponse` 的 `id/model/content/finishReason`。 + - 普通 data `[DONE]`:标记流结束。 +4. 上游流式过程中失败时,HTTP 已经开始返回 `200`,因此用 `error` SSE 事件携带 `code/message`,随后发送 `[DONE]`。 +5. 缺 LLM API Key 仍在开始流之前返回标准 `SERVICE_UNAVAILABLE` 错误 envelope。 + +## 3. 不变边界 + +1. 本次不新增、修改 SpacetimeDB table、reducer、procedure、绑定 shape 或 `migration.rs`。 +2. 本次不把 LLM prompt、剧情、玩法规则放进 `api-server`;API 层只做鉴权、DTO 映射、SSE 转发和平台错误转换。 +3. 旧 `/api/runtime/story/*` 继续不挂载;新 `/api/story/*` 继续只通过 `spacetime-client` facade。 +4. 手机号登录与微信登录属于配置门控和 `platform-auth` 能力接入,不是 WP-API 缺失实现;角色动画 Stage 1 占位链继续归资产/模型生成深水区,不阻塞本次 BFF 收尾。 + +## 4. 验收 + +```powershell +cargo fmt -p api-server --manifest-path server-rs\Cargo.toml --check +cargo test -p api-server llm --manifest-path server-rs\Cargo.toml +cargo test -p api-server custom_world_ai --manifest-path server-rs\Cargo.toml +cargo test -p api-server runtime_story_legacy_routes_are_not_mounted --manifest-path server-rs\Cargo.toml +cargo check -p api-server --manifest-path server-rs\Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_API_BFF_CLOSURE_2026-05-01.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/api-server/src/llm.rs server-rs/crates/api-server/src/custom_world_ai.rs +npm.cmd run api-server:maincloud +``` + +说明:`api-server:maincloud` 是常驻服务命令,验收时以启动后 `/healthz` 探测 `200` 作为成功证据。 + +## 5. 验证结果 + +本轮已执行: + +```powershell +cargo fmt --all --check --manifest-path server-rs\Cargo.toml +cargo test -p api-server llm --manifest-path server-rs\Cargo.toml +cargo test -p api-server custom_world_ai --manifest-path server-rs\Cargo.toml +cargo test -p api-server runtime_story_legacy_routes_are_not_mounted --manifest-path server-rs\Cargo.toml +cargo test -p api-server --manifest-path server-rs\Cargo.toml +cargo test -p module-runtime-story --manifest-path server-rs\Cargo.toml +cargo check --workspace --manifest-path server-rs\Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_API_BFF_CLOSURE_2026-05-01.md docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/api-server/src/llm.rs server-rs/crates/api-server/src/custom_world_ai.rs server-rs/crates/module-runtime-story/src/lib.rs server-rs/crates/module-runtime-story/src/bootstrap.rs server-rs/crates/module-runtime-story/src/session_action.rs +npm.cmd run api-server:maincloud +``` + +结果:通过。`api-server` 全量 203 个测试通过、4 个真实外部服务测试按预期 ignored;`module-runtime-story` 11 个测试通过;workspace 编译通过;DDD 边界检查通过 15 个 module crate;编码检查通过 8 个文件。`api-server:maincloud` 启动后 `GET http://127.0.0.1:3100/healthz` 返回 `200 {"ok":true,"service":"genarrative-api-server"}`,本轮 smoke 未留下常驻进程。启动日志中 Maincloud WebSocket 恢复认证快照短暂返回 `503 Service Unavailable`,未阻止 API server 就绪,属于外部 Maincloud 可用性告警。 + +本次为完成全量验证,还补齐了 `module-runtime-story` 测试编译门槛: + +1. `module-runtime-story/src/lib.rs` 提高 `json!` 宏展开递归上限,避免开局快照大 JSON 构造在测试编译时触发 recursion limit。 +2. `StoryRuntimeActionResolveOutput` 补 `Debug` derive,满足 `expect_err` 测试约束。 +3. 移除 `bootstrap.rs` 与 `session_action.rs` 的未用 import,减少验证噪声。 diff --git a/docs/technical/SERVER_RS_DDD_WP_AS_ASSET_CHAIN_CLOSURE_2026-05-01.md b/docs/technical/SERVER_RS_DDD_WP_AS_ASSET_CHAIN_CLOSURE_2026-05-01.md new file mode 100644 index 00000000..1e5fba0d --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_AS_ASSET_CHAIN_CLOSURE_2026-05-01.md @@ -0,0 +1,40 @@ +# WP-AS Assets 资产主链收尾记录(2026-05-01) + +## 1. 收尾目标 + +关闭 `WP-AS Assets` 当前主链:资产对象确认、实体槽位绑定、历史读取、OSS 对象确认、API facade、SpacetimeDB adapter 和订阅事件需要形成同一条后端真相链。 + +收尾后,`WP-AS` 不再以“资产 API/OSS/facade/event table 尚未全链收口”的口径挂起。后续 `asset_job`、`asset_manifest`、专业生成任务表和旧兼容入口物理删除,按新增需求或 `WP-DEL/WP-V` 另开切片。 + +## 2. 已完成内容 + +1. `module-assets` 补齐资产领域事件:`AssetDomainEvent`、`AssetObjectConfirmedEvent` 和 `AssetEntityBindingChangedEvent`。 +2. `spacetime-module` 新增 `asset_event` public event table,记录 `ObjectConfirmed` 与 `EntityBindingChanged` 两类轻量事实。 +3. `confirm_asset_object*` 成功 upsert `asset_object` 后写入对象确认事件;`bind_asset_object_to_entity*` 成功 upsert `asset_entity_binding` 后写入绑定变更事件。 +4. `migration.rs` 已纳入 `asset_event`,`SPACETIMEDB_TABLE_CATALOG.md` 已补齐资产事件表结构、索引和查询模板。 +5. Rust `module_bindings` 已补齐 `asset_event_type.rs`、`asset_event_kind_type.rs`、`asset_event_table.rs` 和 `mod.rs` 事件 diff 接线。 +6. `module-assets/README.md` 已更新为当前状态:资产对象、绑定、历史、OSS 确认、API facade 和 event table 已闭环,生成任务和 manifest 仍是后续扩展。 + +## 3. 边界说明 + +1. 正式资产状态仍以 `asset_object` 和 `asset_entity_binding` 为准;`asset_event` 只用于订阅端、BFF 和审计流程感知主链变化。 +2. OSS 上传、HEAD Object、读代理和平台错误仍由 `api-server + platform-oss` 承接,领域 crate 不直接触碰网络副作用。 +3. 本次不新增 `asset_job`、`asset_manifest` 或角色/动作/场景专用资产生成表。 +4. 旧 `/generated-*` 读兼容入口暂不物理删除,后续归 `WP-DEL/WP-V` 统一处理。 + +## 4. 验收命令 + +```powershell +spacetime generate --no-config --lang rust --out-dir C:\g\rs --module-path server-rs\crates\spacetime-module --yes +cargo fmt -p module-assets -p spacetime-module --manifest-path server-rs\Cargo.toml --check +cargo test -p module-assets --manifest-path server-rs\Cargo.toml +cargo check -p spacetime-module --manifest-path server-rs\Cargo.toml +cargo check -p spacetime-module --manifest-path server-rs\Cargo.toml --target wasm32-unknown-unknown +cargo check -p spacetime-client --manifest-path server-rs\Cargo.toml +cargo check -p api-server --manifest-path server-rs\Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_AS_ASSET_CHAIN_CLOSURE_2026-05-01.md docs/technical/SPACETIMEDB_TABLE_CATALOG.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md docs/technical/README.md server-rs/crates/module-assets/README.md server-rs/crates/module-assets/src/events.rs server-rs/crates/module-assets/src/lib.rs server-rs/crates/spacetime-module/src/asset_metadata/objects.rs server-rs/crates/spacetime-module/src/asset_metadata/bindings.rs server-rs/crates/spacetime-module/src/migration.rs server-rs/crates/spacetime-client/src/module_bindings/mod.rs server-rs/crates/spacetime-client/src/module_bindings/asset_event_kind_type.rs server-rs/crates/spacetime-client/src/module_bindings/asset_event_type.rs server-rs/crates/spacetime-client/src/module_bindings/asset_event_table.rs +npm.cmd run api-server:maincloud +``` + +执行结果以全局任务清单的本次记录为准。 diff --git a/docs/technical/SERVER_RS_DDD_WP_CW_FULL_CHAIN_CLOSURE_2026-05-01.md b/docs/technical/SERVER_RS_DDD_WP_CW_FULL_CHAIN_CLOSURE_2026-05-01.md new file mode 100644 index 00000000..36fda7a9 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_CW_FULL_CHAIN_CLOSURE_2026-05-01.md @@ -0,0 +1,98 @@ +# WP-CW Custom World 全链收尾 + +日期:`2026-05-01` + +## 1. 收尾目标 + +本次收尾关闭 `SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md` 中 `WP-CW Custom World` 的剩余主链漂移: + +1. `module-custom-world` 已完成 DDD 物理拆分,本次不再重拆领域 crate。 +2. `spacetime-module/src/custom_world/*` 已完成 Agent action 确定性状态编排,本次不改表结构、不改 reducer/procedure 对外名称。 +3. `spacetime-client/src/custom_world.rs` 已具备 profile、gallery、agent session、message、action、operation、card detail、works facade,本次只把 API 和前端主入口对齐到这些稳定 facade。 +4. `api-server` 中 Custom World 图片链路测试必须稳定,不允许因本机 `.env.local` 中真实 DashScope Key 让“缺配置”测试变成真实上游调用。 +5. 前端 RPG 创作资产 client 统一使用 `/api/runtime/custom-world/*` 主链,旧 `/api/custom-world/*` 仅作为过渡入口保留给历史调用。 + +## 2. 主链路由 + +Authenticated 创作链路: + +1. `POST /api/runtime/custom-world/profile` +2. `GET /api/runtime/custom-world-library` +3. `GET /api/runtime/custom-world-library/{profile_id}` +4. `PUT /api/runtime/custom-world-library/{profile_id}` +5. `DELETE /api/runtime/custom-world-library/{profile_id}` +6. `POST /api/runtime/custom-world-library/{profile_id}/publish` +7. `POST /api/runtime/custom-world-library/{profile_id}/unpublish` +8. `POST /api/runtime/custom-world/agent/sessions` +9. `GET /api/runtime/custom-world/agent/sessions/{session_id}` +10. `DELETE /api/runtime/custom-world/agent/sessions/{session_id}` +11. `GET /api/runtime/custom-world/agent/sessions/{session_id}/result-view` +12. `GET /api/runtime/custom-world/works` +13. `GET /api/runtime/custom-world/agent/sessions/{session_id}/cards/{card_id}` +14. `POST /api/runtime/custom-world/agent/sessions/{session_id}/messages` +15. `POST /api/runtime/custom-world/agent/sessions/{session_id}/messages/stream` +16. `POST /api/runtime/custom-world/agent/sessions/{session_id}/actions` +17. `GET /api/runtime/custom-world/agent/sessions/{session_id}/operations/{operation_id}` +18. `POST /api/runtime/custom-world/entity` +19. `POST /api/runtime/custom-world/scene-npc` +20. `POST /api/runtime/custom-world/scene-image` +21. `POST /api/runtime/custom-world/cover-image` +22. `POST /api/runtime/custom-world/cover-upload` + +Public gallery 链路: + +1. `GET /api/runtime/custom-world-gallery` +2. `GET /api/runtime/custom-world-gallery/{owner_user_id}/{profile_id}` +3. `GET /api/runtime/custom-world-gallery/by-code/{code}` + +## 3. 兼容入口边界 + +以下旧入口本次不物理删除,避免影响历史编辑器、旧测试或外部草稿工具,但 RPG 创作主 client 不再使用它们: + +1. `POST /api/custom-world/entity` +2. `POST /api/custom-world/scene-npc` +3. `POST /api/custom-world/scene-image` +4. `POST /api/custom-world/cover-image` +5. `POST /api/custom-world/cover-upload` + +旧入口只转到同一批 Axum handler,不拥有独立业务规则。 + +## 4. 资产与外部副作用边界 + +1. LLM、DashScope 图片生成、OSS 上传仍位于 `api-server + platform-*`,不进入 reducer/procedure。 +2. 成功生成或上传后的资产对象确认、绑定通过 `spacetime-client` 调用 `spacetime-module`,不由前端自行维护真相。 +3. DashScope 缺配置返回 `SERVICE_UNAVAILABLE`;上游请求已发出但失败返回 `UPSTREAM_ERROR`。 +4. 测试中验证“缺配置”必须显式构造无 key 配置,避免受开发机 `.env.local` 影响。 + +## 5. 验收 + +本次收尾后至少执行: + +```powershell +cargo test -p module-custom-world --manifest-path server-rs\Cargo.toml +cargo test -p api-server custom_world --manifest-path server-rs\Cargo.toml +cargo check -p spacetime-module --manifest-path server-rs\Cargo.toml +cargo check -p spacetime-client --manifest-path server-rs\Cargo.toml +npm.cmd run test -- src/services/rpg-creation/rpgCreationGenerationClient.test.ts src/services/rpg-creation/rpgCreationGenerationClient.node.test.ts src/services/rpg-creation/rpgCreationAssetClient.test.ts +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_CW_FULL_CHAIN_CLOSURE_2026-05-01.md +``` + +若本机 Maincloud 常驻服务导致 `npm.cmd run api-server:maincloud` 超时或端口占用,记录 `healthz` 探测结果即可,不改用旧后端重启命令。 + +## 6. 本轮验证结果 + +已通过: + +1. `cargo test -p module-custom-world --manifest-path server-rs\Cargo.toml`:13 个单元测试通过。 +2. `cargo test -p api-server custom_world --manifest-path server-rs\Cargo.toml`:30 个 Custom World 定向测试通过。 +3. `cargo check -p spacetime-module --manifest-path server-rs\Cargo.toml`:通过。 +4. `cargo check -p spacetime-client --manifest-path server-rs\Cargo.toml`:通过。 +5. `npm.cmd run test -- src/services/rpg-creation/rpgCreationGenerationClient.test.ts src/services/rpg-creation/rpgCreationGenerationClient.node.test.ts src/services/rpg-creation/rpgCreationAssetClient.test.ts`:3 个测试文件 7 个用例通过。 +6. `npm.cmd run check:server-rs-ddd`:15 个 module crate 边界检查通过。 +7. `npm.cmd run check:encoding -- docs/technical/README.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md docs/technical/SERVER_RS_DDD_WP_CW_FULL_CHAIN_CLOSURE_2026-05-01.md server-rs/crates/api-server/src/custom_world_ai.rs src/services/rpg-creation/rpgCreationAssetClient.ts src/services/rpg-creation/rpgCreationAssetClient.test.ts`:6 个文件通过。 +8. `npm.cmd run api-server:maincloud`:按常驻服务形态在工具侧超时;超时前 `/healthz` 返回 `200` 与 `{"ok":true,"service":"genarrative-api-server"}`,随后已清理本次 smoke 启动留下的 `api-server`、`cargo`、`node` 子进程并确认 `3100` 端口释放。 + +已知非本轮阻塞: + +1. `cargo fmt --all --check --manifest-path server-rs\Cargo.toml` 仍被当前工作区非 WP-CW 的 `module-story` 格式漂移阻塞;本轮 `api-server` 包格式检查已通过。 diff --git a/docs/technical/SERVER_RS_DDD_WP_DEL_CLEANUP_2026-05-01.md b/docs/technical/SERVER_RS_DDD_WP_DEL_CLEANUP_2026-05-01.md new file mode 100644 index 00000000..1ccb0d9f --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_DEL_CLEANUP_2026-05-01.md @@ -0,0 +1,80 @@ +# WP-DEL 旧层删除与命名收口 + +日期:2026-05-01 + +## 目标 + +本轮 `WP-DEL` 只处理已被新主链完全替代的旧 HTTP contract、旧 route、旧前端 alias 和旧测试夹具,不新增业务能力,不恢复 `server-node` 兼容,不修改 SpacetimeDB 表结构。 + +## 已删除范围 + +1. Runtime Story 旧 HTTP DTO: + - Rust `RuntimeStoryStateResolveRequest` + - Rust/TS `RuntimeStoryBootstrapRequest/Response` + - Rust/TS `RuntimeStoryActionResponse` + - TS 泛型 `RuntimeActionRequest/Response` + - 旧 `RuntimeStorySnapshotPayload` 只作为旧 HTTP 总入口快照结构删除;当前 story session scoped 写侧使用 `StoryRuntimeMutationResponse.projection`。 +2. Runtime Story 前端旧命名: + - `beginRpgRuntimeStorySession` + - `resolveRpgRuntimeStoryAction` + - `getRpgStoryRuntimeProjection` + - `getRpgRuntimeActionSnapshot` + - `rpgRuntimeStoryClient` 聚合对象 + - 其他 `Rpg*RuntimeStory*` alias 统一删除,前端只导出当前主链命名。 +3. Custom World 旧非 runtime 前缀路由: + - `POST /api/custom-world/entity` + - `POST /api/custom-world/scene-npc` + - `POST /api/custom-world/scene-image` + - `POST /api/custom-world/cover-image` + - `POST /api/custom-world/cover-upload` + - 当前主链固定为 `/api/runtime/custom-world/*`。 +4. Puzzle 旧本地下一关入口: + - `POST /api/runtime/puzzle/runs/local-next-level` + - Rust/TS `AdvanceLocalPuzzleNextLevelRequest` + - API 层本地 next-level 拼装 helper 与对应旧测试。 +5. `/generated-*` 资产直读代理: + - `GET /generated-character-drafts/{*path}` + - `GET /generated-characters/{*path}` + - `GET /generated-animations/{*path}` + - `GET /generated-big-fish-assets/{*path}` + - `GET /generated-puzzle-assets/{*path}` + - `GET /generated-custom-world-scenes/{*path}` + - `GET /generated-custom-world-covers/{*path}` + - `GET /generated-qwen-sprites/{*path}` + - 旧 `api-server` 同源代理模块 `legacy_generated_assets.rs` + - Vite 与发布静态服务器中的 `/generated-*` 直读转发配置。 + +## 保留边界 + +1. `/generated-*` 字符串仍可作为历史 DTO 与测试夹具里的 `legacyPublicPath` 标识,OSS object key 前缀和 `LegacyAssetPrefix` 继续保留;但浏览器、Vite、本地发布静态服务器和 `api-server` 不再提供 `/generated-*` 裸读入口。 +2. 正式读取契约固定为 `asset_object`、`GET /api/assets/read-url`、`ResolvedAssetImage/useResolvedAssetReadUrl` 或业务投影里的签名读 URL 字段。 +3. `RuntimeStoryActionRequest` 仍保留为 `module-runtime-story` 内部动作规则输入,不作为旧 HTTP route contract。 +4. `RuntimeStoryViewModel`、`RuntimeStoryPresentation`、`RuntimeStoryPatch` 和 battle presentation 仍作为当前投影/表现构件保留,不再代表旧 `/api/runtime/story/*` 总入口响应。 +5. 历史设计文档中对旧 route/DTO 的引用只作为时间点记录保留,不批量改写旧档案。 + +## 验收门禁 + +计划执行: + +```powershell +cargo fmt --all --manifest-path server-rs\Cargo.toml +cargo fmt --all --check --manifest-path server-rs\Cargo.toml +npm.cmd run check:server-rs-ddd +cargo check --workspace --manifest-path server-rs\Cargo.toml +cargo test --workspace --exclude spacetime-module --manifest-path server-rs\Cargo.toml +npm.cmd run test -- src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts src/services/ai.test.ts src/services/puzzle-runtime +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_DEL_CLEANUP_2026-05-01.md docs/technical/README.md docs/technical/SERVER_RS_DDD_G1_CONTRACT_AND_ROUTE_MATRIX_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/shared-contracts/README.md server-rs/crates/api-server/README.md server-rs/crates/module-puzzle/README.md +``` + +实际结果: + +1. `cargo fmt --all --manifest-path server-rs\Cargo.toml`:通过。 +2. `cargo fmt --all --check --manifest-path server-rs\Cargo.toml`:通过。 +3. `npm.cmd run check:server-rs-ddd`:通过,15 个 module crate 边界检查通过。 +4. `cargo check --workspace --manifest-path server-rs\Cargo.toml`:通过;仅保留既有 runtime chat prompt dead code warning。 +5. `cargo test --workspace --exclude spacetime-module --manifest-path server-rs\Cargo.toml`:通过;`api-server` 207 个测试通过、4 个 live/外部依赖测试按既有条件忽略。 +6. `npm.cmd run test -- src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts src/services/ai.test.ts src/services/puzzle-runtime`:通过,4 个测试文件 45 个用例通过。 +7. `npm.cmd run check:encoding`:通过,全量 2816 个文件通过 UTF-8/乱码检查。 +8. `npm.cmd run spacetime:generate -- --rust-only`:通过;SpacetimeDB CLI 在 Windows 长路径格式化阶段触发已知失败后,项目脚本使用短路径分批 `rustfmt` fallback 完成。 +9. `cargo check -p spacetime-module --manifest-path server-rs\Cargo.toml`:通过。 +10. `npm.cmd run api-server:maincloud`:通过;后端拉起后 `GET http://127.0.0.1:3100/healthz` 返回 `200 {"ok":true,"service":"genarrative-api-server"}`,随后已清理本次 smoke 进程并确认 3100 端口释放。启动期 Maincloud 订阅恢复出现两条 `503 Service Unavailable` 警告,不影响 healthz smoke。 diff --git a/docs/technical/SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md index 242700c1..1fd9338e 100644 --- a/docs/technical/SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md +++ b/docs/technical/SERVER_RS_DDD_WP_FE_C_RPG_RUNTIME_SHELL_TEST_FIXTURE_2026-04-29.md @@ -13,12 +13,14 @@ - `npcUi/characterChatUi/inventoryUi/battleRewardUi/questUi/npcChatQuestOfferUi/goalUi` mock 对齐当前 hooks 暴露的稳定 UI 对象形状。 3. 保持 `RpgRuntimeShell` 正式组件行为不变,只消除测试夹具对旧组件接线形状的依赖。 -## 边界 +## 2026-05-01 收尾切片 -1. 本次不改 `src/services/**`、`src/hooks/**` 或任何后端接口。 -2. 本次不迁移 `beginRuntimeStorySession`、`resolveRuntimeStoryAction` 等尚未稳定的新写接口。 -3. 本次不新增 UI 文案,不改组件视觉布局。 -4. 本次不删除旧 runtime story contract 或旧 client alias;这些仍等待 `WP-DEL` 串行收口。 +`WP-FE-S/H` 写侧切到新 story session scoped 主链后,本切片只确认组件层夹具继续消费 hooks 出口,不在组件中补业务规则: + +1. `RpgRuntimeShell` 正式组件不拼接 `/api/runtime/story/*` 或 `/api/story/*` 路径。 +2. 组件测试 mock 继续对齐 `hydratedSnapshot/nextStory` 与当前 UI 对象形状。 +3. 不新增 UI 文案,不改视觉布局;服务端动作表现差异只通过 hooks 状态进入组件。 +4. 旧 runtime story contract 和旧 alias 仍由 `WP-DEL` 统一删除。 ## 验收 diff --git a/docs/technical/SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md index 634b5e72..2cb21e4e 100644 --- a/docs/technical/SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md +++ b/docs/technical/SERVER_RS_DDD_WP_FE_H_RPG_RUNTIME_STORY_HOOKS_PROJECTION_2026-04-29.md @@ -13,13 +13,20 @@ 5. 更新 `runtimeStoryCoordinator.test.ts` mock 命名,确保 hooks 测试明确断言读取侧走 projection client。 6. 补齐 `src/hooks/useGameFlow.customWorld.test.tsx` 的 `beginRpgRuntimeStorySession` 测试桩,避免 hooks 全量测试在 Node 环境直接 fetch 相对路径,同时保持自定义世界开局 hooks 仍消费服务端快照。 -## 边界 +## 2026-05-01 收尾切片 -1. 本次不迁移 `beginRuntimeStorySession`,因为新开局接口尚未返回可直接进入游戏的完整 `GameState` 快照。 -2. 本次不迁移 `resolveRuntimeStoryAction`,因为完整动作结算的新 session scoped 写接口尚未稳定。 -3. 本次不改 components,不新增 UI 文案,不在组件层拼 API 请求。 -4. 本次不改 `api-server`、`spacetime-client`、`spacetime-module` 或共享契约。 -5. 本次不删除旧 runtime story contract、旧 client alias 或兼容测试;这些仍等待 `WP-DEL` 串行收口。 +后端补齐 `/api/story/sessions/runtime` 与 `/api/story/sessions/{storySessionId}/actions/resolve` 后,hooks 写侧同步收口: + +1. `resolveServerRuntimeChoice` 不再调用旧 `/api/runtime/story/actions/resolve` client。 +2. 正式动作只提交 `storySessionId/functionId/actionText/payload`,并从新 `StoryRuntimeProjectionResponse.gameState` 水合快照。 +3. `resumeServerRuntimeStory` 继续以服务端投影刷新 story/runtime 版本,同时保留本地 UI 临时态职责。 +4. 组件层仍不拼 API,不新增规则说明文案;`WP-FE-C` 只消费 hooks 返回的 `hydratedSnapshot/nextStory`。 + +边界: + +1. 本次保留旧 client alias 名称,避免大面积 import churn;行为已经切到新主链。 +2. battle presentation 的逐帧表现只在响应仍含 presentation 时触发;新 projection 写侧先提交最终快照和故事,后续战斗表现由 story battle 专用接口继续增强。 +3. 旧 runtime story contract 的物理删除仍属于 `WP-DEL`。 ## 验收 diff --git a/docs/technical/SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md index 2b64fc44..bf13ffea 100644 --- a/docs/technical/SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md +++ b/docs/technical/SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md @@ -32,26 +32,27 @@ GET /api/story/sessions/{storySessionId}/runtime-projection 6. `src/services/rpg-runtime/index.ts` 已导出新 client 函数与结果类型,供后续 `WP-FE-H` 迁 hook 时直接接入。 7. 为满足 `WP-FE-S` 的 `src/services` 全量验收,补回 `src/services/customWorldAgentGenerationProgress.ts` 缺失的“建立场景连接”阶段,使草稿生成进度重新对齐既有 13 步文档与测试口径。 -## 未完成边界 +## 2026-05-01 收尾切片 -暂未迁移: +本次收尾把先前“等待后端写接口”的缺口正式关闭,执行口径如下: -1. `beginRuntimeStorySession` 仍调用旧 `/api/runtime/story/sessions`,因为当前新 `/api/story/sessions` 只创建 story session,不返回前端开局所需的完整 `GameState` 快照。新 `beginStorySession` 已作为稳定 client 先行提供,hook 是否切换等待后端开局投影组合稳定。 -2. `resolveRuntimeStoryAction` 仍调用旧 `/api/runtime/story/actions/resolve`,因为当前新主链尚未提供等价的 session scoped 动作结算接口与快照回包。新 `continueStorySession` 只覆盖 story event 续写,不等价于完整 runtime action settle。 -3. hooks/components 不在本轮大改;`WP-FE-H` 等后端写接口稳定后再迁。 +1. 不恢复旧 `/api/runtime/story/*` compat route;前端 runtime story 开局、动作结算和读取统一走 `/api/story/sessions*`。 +2. `/api/story/sessions/runtime` 作为新开局 BFF:后端生成 `runtimeSessionId/storySessionId`,写入 SpacetimeDB `runtime_snapshot`,并返回可水合的 runtime projection。 +3. `/api/story/sessions/{storySessionId}/actions/resolve` 作为 session scoped 动作入口:前端只提交 function id、动作文案和 payload,后端基于已持久化 snapshot 更新 `currentStory/storyHistory/runtimeActionVersion` 后返回 projection。 +4. `StoryRuntimeProjectionResponse` 补齐 `gameState`,让前端从后端投影水合 `HydratedSavedGameSnapshot`,不再消费旧 `snapshot/viewModel/presentation/patches` 组合。 +5. `beginRuntimeStorySession` 与 `resolveRuntimeStoryAction` 保留前端导出名以减少调用面震荡,但实现已切到新 story session scoped 主链。 -## 后续依赖 +收尾后的旧层边界: -1. `WP-API/WP-RS` 需要提供新的 story session scoped 开局接口,返回可直接进入游戏的 `GameState` 或明确的前端投影组合。 -2. `WP-API/WP-RS` 需要提供新的 story session scoped 动作结算接口,返回新投影及必要的状态更新结果。 -3. 上述接口稳定后,`WP-FE-S` 再删除旧 `/api/runtime/story/*` 写接口调用,`WP-FE-H` 迁 hooks,最后由 `WP-DEL` 物理删除旧 runtime story contract 与 compat 测试。 +1. `packages/shared/src/contracts/rpgRuntimeStoryState.ts` 中的 view model / presentation / patch 类型暂不物理删除,留给 `WP-DEL` 统一清理。 +2. 后续更完整的 battle/forge/NPC/quest 跨域结算仍由 `WP-RS/WP-ST/WP-SC/WP-API` 增量增强,但前端不再回退旧 runtime story 写路径。 ## 验收命令 ```powershell npm.cmd run test -- src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts npm.cmd run test -- src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts -npm.cmd run test -- src/services/customWorldAgentGenerationProgress.test.ts -npm.cmd run test -- src/services -npm.cmd run check:encoding -- packages/shared/src/contracts/story.ts src/services/rpg-runtime/rpgRuntimeStoryClient.ts src/services/rpg-runtime/index.ts src/services/customWorldAgentGenerationProgress.ts src/services/customWorldAgentGenerationProgress.test.ts src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts src/hooks/rpg-runtime-story/inventoryActions.ts src/persistence/runtimeSnapshot.ts src/persistence/runtimeSnapshotTypes.ts src/types/game.ts docs/technical/SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md +npm.cmd run test -- src/hooks/rpg-runtime-story/storyChoiceRuntime.test.ts +npm.cmd run test -- src/components/rpg-runtime-shell/RpgRuntimeShell.test.tsx +npm.cmd run check:encoding -- packages/shared/src/contracts/story.ts src/services/rpg-runtime/rpgRuntimeStoryClient.ts src/services/rpg-runtime/index.ts src/hooks/rpg-runtime-story/rpgRuntimeStoryGateway.ts src/hooks/rpg-runtime-story/inventoryActions.ts src/hooks/rpg-runtime-story/storyChoiceRuntime.ts src/hooks/rpg-runtime-story/runtimeStoryCoordinator.test.ts docs/technical/SERVER_RS_DDD_WP_FE_S_RPG_RUNTIME_STORY_CLIENT_MIGRATION_2026-04-29.md ``` diff --git a/docs/technical/SERVER_RS_DDD_WP_PZ_RUNTIME_BACKEND_TRUTH_CLOSURE_2026-05-01.md b/docs/technical/SERVER_RS_DDD_WP_PZ_RUNTIME_BACKEND_TRUTH_CLOSURE_2026-05-01.md new file mode 100644 index 00000000..6c62776d --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_PZ_RUNTIME_BACKEND_TRUTH_CLOSURE_2026-05-01.md @@ -0,0 +1,76 @@ +# WP-PZ Puzzle 运行态后端真相源收尾 + +日期:`2026-05-01` + +## 1. 收尾目标 + +本轮收尾只关闭正式平台入口的 Puzzle 运行态链路,不扩大到 `/puzzle` 调试直达页和后续旧接口物理删除。 + +必须达成: + +1. 平台内从作品详情或结果页进入拼图玩法时,开局、交换、拖动、通关排行榜和下一关全部调用 Rust API,再由 `spacetime-client` 进入 SpacetimeDB procedure。 +2. `PlatformEntryFlowShellImpl` 不再导入 `puzzleLocalRuntime`,不再在浏览器侧裁决棋盘、合并、拆分、通关或排行榜。 +3. 结果页“试玩当前草稿”先把当前草稿轻量字段写回 `puzzle_work_profile`,再启动后端 run;该预览 run 只允许草稿 owner 启动。 +4. 发布作品的公开开局规则不放松:非 owner 只能启动已发布作品。 +5. 本轮不新增表字段,不修改 `migration.rs`。 + +## 2. 后端口径 + +`start_puzzle_run_tx` 继续以 `puzzle_work_profile` 作为入口 profile 真相源: + +1. `Published` 作品维持既有公开可玩语义。 +2. `Draft` 作品仅当 `owner_user_id` 与请求用户一致时可启动,用于结果页预览。 +3. 草稿预览 run 不计入公开作品播放次数,也不写入 played work 记录。 +4. 下一关推荐仍只从已发布 gallery 中选择候选,草稿只作为当前入口关卡,不参与公共推荐池。 +5. 排行榜写入只面向已发布作品;草稿预览通关时后端返回当前 run 快照,不生成公开榜单记录。 + +## 3. 前端口径 + +正式平台入口只保留表现态: + +1. `PuzzleRuntimeShell` 继续只接收后端 run snapshot 与回调。 +2. 作品详情开局调用 `startPuzzleRun`。 +3. 结果页试玩调用 `updatePuzzleWork` 同步草稿,再调用 `startPuzzleRun` 启动 owner draft run。 +4. 交换、拖动、下一关和排行榜分别调用 `swapPuzzlePieces`、`dragPuzzlePieceOrGroup`、`advancePuzzleNextLevel`、`submitPuzzleLeaderboard`。 +5. `/puzzle` 调试直达页仍可保留本地 runtime,用于开发期视觉和交互调试;它不再是正式平台链路。 + +## 4. 验收命令 + +本轮修改完成后至少执行: + +1. `npm.cmd run check:encoding` +2. `npm.cmd run check:server-rs-ddd` +3. `cargo fmt --all --check --manifest-path server-rs\Cargo.toml` +4. `cargo test -p module-puzzle --manifest-path server-rs\Cargo.toml` +5. `cargo check -p spacetime-module --manifest-path server-rs\Cargo.toml` +6. `cargo check -p spacetime-client --manifest-path server-rs\Cargo.toml` +7. `cargo check -p shared-contracts --manifest-path server-rs\Cargo.toml` +8. `cargo check -p api-server --manifest-path server-rs\Cargo.toml` +9. `npm.cmd run typecheck` +10. `npm.cmd run api-server:maincloud` + +若全量 workspace 测试受其他工作包既有失败影响,本轮只记录失败归属,不把 WP-PZ 已收口代码回退。 + +## 5. 本轮验证结果 + +已通过: + +1. `npm.cmd run check:encoding` +2. `npm.cmd run check:server-rs-ddd` +3. `cargo test -p module-puzzle --manifest-path server-rs\Cargo.toml` +4. `cargo check -p module-puzzle --features spacetime-types --target wasm32-unknown-unknown --manifest-path server-rs\Cargo.toml` +5. `cargo check -p shared-contracts --manifest-path server-rs\Cargo.toml` +6. `cargo check -p spacetime-module --manifest-path server-rs\Cargo.toml` +7. `cargo check -p spacetime-client --manifest-path server-rs\Cargo.toml` +8. `cargo check -p api-server --manifest-path server-rs\Cargo.toml` +9. `npm.cmd run test -- src/components/puzzle-runtime src/services/puzzle-runtime` +10. `cargo fmt --manifest-path server-rs\Cargo.toml --package module-puzzle --check` +11. `cargo fmt --manifest-path server-rs\Cargo.toml --package spacetime-module --check` +12. `cargo fmt --manifest-path server-rs\Cargo.toml --package spacetime-client --check` +13. `cargo fmt --manifest-path server-rs\Cargo.toml --package api-server --check` + +未作为本轮阻塞: + +1. `cargo fmt --all --check --manifest-path server-rs\Cargo.toml` 当前会检查到非 WP-PZ 的 `module-runtime-story/src/bootstrap.rs` 与 `module-runtime-story/src/session_action.rs` 未跟踪新增文件格式差异。 +2. `npm.cmd run typecheck` 当前失败在非 WP-PZ 文件:`src/data/sceneEncounterPreviews.ts` 的 `slot` 可能为 `undefined`、`src/services/ai.ts` 缺少 `hasMixedNarrativeLanguage`、`src/services/rpg-creation/rpgCreationAssetClient.test.ts` 的 Custom World scene image 请求字段不匹配。 +3. `npm.cmd run api-server:maincloud` 已完成编译并尝试启动,但 Maincloud 连接出现 `503 Service Unavailable`,随后 `api-server.exe` 以 `0xffffffff` 退出;本地 `3100` 未留下可用监听,残留进程已确认清理。 diff --git a/docs/technical/SERVER_RS_DDD_WP_RPG_GAMEPLAY_CLOSURE_2026-05-01.md b/docs/technical/SERVER_RS_DDD_WP_RPG_GAMEPLAY_CLOSURE_2026-05-01.md new file mode 100644 index 00000000..3eebef78 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_RPG_GAMEPLAY_CLOSURE_2026-05-01.md @@ -0,0 +1,38 @@ +# WP-RPG Gameplay 全域收口记录(2026-05-01) + +## 1. 收口目标 + +本次关闭 `SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md` 中 `WP-RPG Gameplay 域` 的剩余项:领域文件已完成物理拆分后,战斗胜利奖励、任务交付奖励、宝箱奖励仍由 `spacetime-module/src/gameplay/mod.rs` 直接拼装背包、成长和章节账本命令。 + +本次目标是把这些跨域组合规则收回 RPG 领域层,让 SpacetimeDB adapter 只负责事务内幂等检查、执行 mutation、写表和查询。 + +## 2. 已完成内容 + +1. `module-story` 新增 `RpgGameplaySettlementPlan`,作为 RPG story/gameplay 跨域结算计划。 +2. `build_combat_victory_settlement_plan` 收口战斗胜利后的背包掉落、敌对经验和章节 hostile 账本计划。 +3. `build_quest_turn_in_settlement_plan` 收口任务交付后的奖励物品、任务经验和章节 quest 账本计划。 +4. `build_treasure_settlement_plan` 收口宝箱奖励到背包 mutation 的计划生成,并保持宝箱记录本身仍由 `module-runtime-item` 建模。 +5. `spacetime-module/src/gameplay/mod.rs` 改为消费 `RpgGameplaySettlementPlan`,不再在 adapter 中重复映射战斗/任务奖励物品、稀有度、装备槽和物品来源。 +6. `spacetime-module` 继续保留事务幂等检查、`inventory_slot` 写回、`player_progression` 经验发放和 `chapter_progression` 可选记账。 +7. 未修改 SpacetimeDB 表结构、reducer/procedure 签名、绑定 shape 或 `migration.rs`。 + +## 3. 边界说明 + +1. 本次不接 `WP-RS Runtime Story 去兼容层` 的完整动作写接口,不修改 `/api/story/*` 路由和前端 hooks。 +2. 本次不新增 public/event table;结算计划仍是纯领域对象,由 adapter 转换为已有表写入。 +3. `WP-ST/WP-SC/WP-API/WP-FE` 后续若继续做 runtime story 写侧,只能消费本次的领域计划或对应 facade,不应在 BFF/前端重新拼奖励规则。 +4. 章节计划不存在时仍不反向阻断战斗胜利或任务交付主链,只跳过章节账本写入。 + +## 4. 验证 + +已执行: + +```powershell +cargo fmt -p module-story -p spacetime-module --manifest-path server-rs\Cargo.toml +cargo test -p module-story --manifest-path server-rs\Cargo.toml +cargo test -p module-combat -p module-inventory -p module-npc -p module-progression -p module-quest -p module-runtime-item -p module-story --manifest-path server-rs\Cargo.toml +cargo check -p spacetime-module --manifest-path server-rs\Cargo.toml +npm.cmd run check:server-rs-ddd +``` + +结果:通过。RPG 七个领域 crate 共 49 个单元测试通过,`spacetime-module` 编译通过,DDD 边界检查通过 15 个 module crate。 diff --git a/docs/technical/SERVER_RS_DDD_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md index 8f3c2c49..c88b845c 100644 --- a/docs/technical/SERVER_RS_DDD_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md +++ b/docs/technical/SERVER_RS_DDD_WP_RS_COMPAT_RESIDUE_AUDIT_2026-04-29.md @@ -2,7 +2,7 @@ ## 背景 -`WP-RS Runtime Story 去兼容层` 已完成 crate 迁名、旧 HTTP compat 路由下线、runtime projection 契约/API/读取侧迁移等多轮推进。当前仍存在一些 `compat` 或旧 `/api/runtime/story/*` 命名残留,需要区分“可以立即清理的工程语义残留”和“必须等待后端写接口稳定后再删除的运行依赖”。 +`WP-RS Runtime Story 去兼容层` 已完成 crate 迁名、旧 HTTP compat 路由下线、runtime projection 契约/API/读取侧迁移等多轮推进。2026-05-01 收尾后,运行时开局和动作写侧也已迁到 story session scoped route;当前仍存在一些 `compat` 或旧 `/api/runtime/story/*` 命名残留,需要区分“可以立即清理的工程语义残留”和“历史展示 DTO / 旧命名的物理删除窗口”。 本次切片只处理前者,并冻结后者的交接清单。 @@ -29,22 +29,21 @@ 1. 清理 `module-runtime-story` 运行代码注释中的 `compat` 定位,将 crate 口径改为 runtime story 主链纯规则收口。 2. 保留“历史 payload”相关说明,因为这些字段仍是实际输入兼容范围,不属于旧层命名。 -3. 更新 `module-runtime-story/README.md`,明确后续要迁的是旧 `/api/runtime/story/*` 写侧能力,而不是继续扩展兼容桥。 +3. 更新 `module-runtime-story/README.md`,明确旧 `/api/runtime/story/*` 写侧能力已迁到 session scoped 新接口,后续不再扩展兼容桥。 ## 残留清单 以下残留本次不删除: -1. `src/services/rpg-runtime/rpgRuntimeStoryClient.ts` 仍指向 `/api/runtime/story`,原因是开局和动作写侧 session scoped 新接口尚未完成。 -2. `src/services/rpg-runtime/rpgRuntimeStoryClient.test.ts` 仍断言旧写接口路径,需等待新写接口和 service contract 稳定后由 `WP-FE-S` 修改。 -3. `packages/shared/src/contracts/rpgRuntimeStoryState.ts` 和 `server-rs/crates/shared-contracts/src/runtime_story.rs` 仍保留 `RuntimeStoryActionResponse`,原因是前端旧写侧仍消费该响应形状。 -4. `src/hooks/rpg-runtime-story/**` 仍保留部分旧调用面兼容注释,需等 `WP-FE-H` 在 service 迁移后统一收口。 +1. `packages/shared/src/contracts/rpgRuntimeStoryState.ts` 和 `server-rs/crates/shared-contracts/src/runtime_story.rs` 仍保留历史 view model / presentation / patch DTO,原因是 story battle 表现、历史测试和 `WP-DEL` 物理删除窗口仍需统一评估。 +2. `src/hooks/rpg-runtime-story/**` 仍保留部分旧命名兼容注释,但运行写链路已经通过 `storySessionId` scoped client,不再调用旧 `/api/runtime/story/*`。 +3. 历史文档中仍会记录旧 compat 路由阶段,用于审计时间线;当前执行入口以 `SERVER_RS_DDD_WP_RS_RUNTIME_STORY_CLOSURE_2026-05-01.md` 为准。 ## 后续边界 -1. 后端新写接口稳定前,不删除旧前端写 client。 -2. `WP-FE-S` 先迁 service,再由 `WP-FE-H` 迁 hooks,最后 `WP-FE-C` 接组件。 -3. `WP-DEL` 只能在旧接口无运行引用后删除 `RuntimeStoryActionResponse` 等旧 contract。 +1. 不恢复旧前端写 client,也不重新挂载旧 `/api/runtime/story/*`。 +2. `WP-DEL` 只能在确认 story battle 等展示语义不再依赖旧 DTO 后,删除 `RuntimeStoryActionResponse` 等历史 contract。 +3. 历史文档清理只改当前状态说明,不抹掉已执行过的审计记录。 ## 验收 diff --git a/docs/technical/SERVER_RS_DDD_WP_RS_RUNTIME_STORY_CLOSURE_2026-05-01.md b/docs/technical/SERVER_RS_DDD_WP_RS_RUNTIME_STORY_CLOSURE_2026-05-01.md new file mode 100644 index 00000000..5c0817dd --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_RS_RUNTIME_STORY_CLOSURE_2026-05-01.md @@ -0,0 +1,47 @@ +# WP-RS Runtime Story 收尾设计 + +日期:2026-05-01 + +## 目标 + +本轮收尾只关闭运行代码中的旧 `/api/runtime/story/*` 写链路,不恢复旧 compat route,不兼容 `server-node`,也不新增 SpacetimeDB 表结构。 + +## 新路由 + +1. `POST /api/story/sessions/runtime` + - 用于前端进入运行时故事。 + - 后端生成 `runtimeSessionId` 与 `storySessionId`,创建 story session,写入 runtime snapshot。 + - 响应返回 `StoryRuntimeMutationResponse { projection }`,其中 `projection.gameState` 是前端水合快照的唯一状态来源。 + +2. `POST /api/story/sessions/{story_session_id}/actions/resolve` + - 用于正式运行时选项结算。 + - 路径中的 `story_session_id` 是唯一会话边界;请求体必须携带同一个 `storySessionId`,不再接受 `sessionId` 作为主键兜底。 + - 后端读取 story session 与当前用户 runtime snapshot,校验 snapshot 中 `runtimeSessionId` 必须匹配 story session。 + - 后端调用 `module-runtime-story` 纯规则结算动作,推进 `runtimeActionVersion`,写回 runtime snapshot,并用 `continue_story` 记录本轮 narrative event。 + - 响应同样返回 `StoryRuntimeMutationResponse { projection }`,不返回旧 `viewModel / presentation / patches / snapshot` 组合。 + +## 契约收口 + +本轮新增 story contract 下的运行时写侧 DTO: + +1. `BeginStoryRuntimeSessionRequest` +2. `ResolveStoryRuntimeActionRequest` +3. `StoryRuntimeMutationResponse` +4. `StoryRuntimeProjectionResponse.gameState` + +旧 `StoryRuntimeBootstrapRequest/Response` 与 `StoryRuntimeActionRequest/Response` 已从 `packages/shared/src/contracts/story.ts` 和 `server-rs/crates/shared-contracts/src/story.rs` 移除。`runtime_story` 下的 view model、presentation、patch 类型暂作为历史展示 DTO 和 story battle 表现输入保留;它们不是旧 HTTP route。 + +## 不做事项 + +1. 不恢复 `/api/runtime/story/*`。 +2. 不新增或修改 SpacetimeDB table,因此不改 `migration.rs`。 +3. 不把前端本地快照重新作为动作真相上传。 +4. 不把 Node/Express/PostgreSQL 路径作为兼容目标。 + +## 完成判定 + +1. `src/services/rpg-runtime/rpgRuntimeStoryClient.ts` 不再包含 `/api/runtime/story`。 +2. 前端 `beginRpgRuntimeStorySession` 和 `resolveRpgRuntimeStoryAction` 只调用 `/api/story/*`。 +3. `server-rs/crates/api-server/src/app.rs` 仍保持旧 `/api/runtime/story/*` 未挂载测试。 +4. 共享 story 写侧契约和测试不再引用旧 `StoryRuntimeBootstrapRequest/Response` 与 `StoryRuntimeActionResponse`。 +5. 文档进度从“旧写接口等待迁移”更新为“写链路已切到 story session scoped route”。 diff --git a/docs/technical/SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md b/docs/technical/SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md index ea300f96..d0fdd1bf 100644 --- a/docs/technical/SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md +++ b/docs/technical/SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md @@ -4,7 +4,7 @@ `WP-SC Spacetime Client` 位于 `spacetime-module` 和 `api-server` 之间,只负责把 SpacetimeDB 生成绑定、procedure / reducer 调用、row snapshot 和错误语义收口成 BFF 可消费的 typed facade。 -当前 `spacetime-client` 已经具备连接池、生成绑定、多个领域 facade 和 mapper,但错误映射仍散落在各 facade 中,且 README 仍停留在早期占位说明。由于 `WP-ST` 尚未完成所有新 facade,本次不预判新的 table、reducer、procedure 或 row shape,只先收口已存在调用层的基础设施。 +当前 `spacetime-client` 已经具备连接池、生成绑定、多个领域 facade 和 mapper。本文件用于冻结并关闭当前稳定 SpacetimeDB facade 范围内的 `WP-SC` 收口:不预判新的 table、reducer、procedure 或 row shape,只把已存在调用层、错误 helper、mapper 与 README 状态闭环。 ## 2. 本次目标 @@ -119,3 +119,36 @@ npm.cmd run check:server-rs-ddd npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md server-rs/crates/spacetime-client/src/story_runtime.rs npm.cmd run api-server:maincloud ``` + +## 9. 收尾关闭口径(2026-05-01) + +当前稳定 facade 范围内,`WP-SC` 已完成: + +1. `SpacetimeClientError` 统一 helper 补齐: + - `from_sdk_error` + - `validation_failed` + - `reducer_failed` + - `procedure_failed` + - `missing_snapshot` +2. reducer callback 的业务错误统一走 `reducer_failed`,保持 API 层可继续按 `Runtime` 分类映射为调用方输入或状态错误。 +3. combat、inventory、runtime、story 等 facade 的本地命令构造 / 校验错误统一走 `validation_failed`,避免各文件继续散落 `Runtime(error.to_string())`。 +4. story runtime projection source 已接入 runtime inventory typed facade,并已有定向测试覆盖 session guard、inventory guard、背包/装备覆盖和 option 解析。 +5. `spacetime-client/README.md` 已从早期占位口径更新为当前完成口径,明确后续只随 `WP-ST` 新 facade 稳定后按领域增量接线。 + +关闭边界: + +1. 本次不修改 `spacetime-module`、`migration.rs`、生成绑定、HTTP route、shared contract 或前端。 +2. `WP-ST` 后续若新增或调整 SpacetimeDB facade,需要另开对应领域切片继续补 typed facade / row mapper;这不再阻塞当前 `WP-SC` 工作包关闭。 +3. `WP-RS/WP-API/WP-FE/WP-DEL` 仍可因旧 runtime story 写侧和前端迁移保持进行中,但不应再把 `spacetime-client` 基础设施视为未完成。 + +最终验收命令: + +```powershell +cargo fmt -p spacetime-client --manifest-path server-rs/Cargo.toml --check +cargo test -p spacetime-client --manifest-path server-rs/Cargo.toml +cargo check -p spacetime-client --manifest-path server-rs/Cargo.toml +cargo check -p api-server --manifest-path server-rs/Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding -- docs/technical/SERVER_RS_DDD_WP_SC_SPACETIME_CLIENT_REFACTOR_2026-04-29.md docs/technical/SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md docs/technical/README.md server-rs/crates/spacetime-client/README.md server-rs/crates/spacetime-client/src/lib.rs server-rs/crates/spacetime-client/src/ai.rs server-rs/crates/spacetime-client/src/combat.rs server-rs/crates/spacetime-client/src/inventory.rs server-rs/crates/spacetime-client/src/runtime.rs server-rs/crates/spacetime-client/src/story.rs +npm.cmd run api-server:maincloud +``` diff --git a/docs/technical/SERVER_RS_DDD_WP_ST_CLOSURE_2026-05-01.md b/docs/technical/SERVER_RS_DDD_WP_ST_CLOSURE_2026-05-01.md new file mode 100644 index 00000000..05ea9c06 --- /dev/null +++ b/docs/technical/SERVER_RS_DDD_WP_ST_CLOSURE_2026-05-01.md @@ -0,0 +1,66 @@ +# WP-ST SpacetimeDB Adapter 收尾记录(2026-05-01) + +## 1. 收尾目标 + +本次关闭 `SERVER_RS_DDD_PARALLEL_TASKLIST_2026-04-29.md` 中 `WP-ST SpacetimeDB Adapter` 当前稳定范围:已经落地的表、reducer、procedure、event table 和上下文 adapter 必须完成 `migration.rs`、表目录、Rust bindings 与自动门禁闭环。 + +收尾后,`WP-ST` 不再以“多个上下文和绑定生成仍待推进”的口径挂起;后续只有新增 table / reducer / procedure 或 row shape 时,才按增量切片重新认领。 + +## 2. 已完成内容 + +1. `asset_event` 已作为 `public event` 表进入资产主链,覆盖资产对象确认和实体槽位绑定变更事件。 +2. `asset_event` 已对齐 `migration.rs`、`SPACETIMEDB_TABLE_CATALOG.md` 和 Rust `module_bindings`。 +3. `SPACETIMEDB_TABLE_CATALOG.md` 补齐 `database_migration_operator`、邀请/推荐/会员/充值等 profile 表,以及 `asset_event` 的结构、索引和查询模板。 +4. `scripts/check-server-rs-ddd-boundaries.mjs` 新增 SpacetimeDB 表漂移检查,自动核对 table accessor、`migration.rs` 白名单和表目录项,阻止新增表漏迁移或漏文档。 +5. `scripts/generate-spacetime-bindings.mjs` 在 Windows 下继续先输出短临时目录;当 SpacetimeDB CLI 已生成文件但自身 formatter 失败时,由脚本分批 `rustfmt` 后再同步生成目录。 +6. `spacetime-client/src/module_bindings` 已通过 `npm.cmd run spacetime:generate -- --rust-only` 重新生成,包含 `asset_event_table.rs`、`asset_event_type.rs` 和 `asset_event_kind_type.rs`。 +7. `spacetime-client/README.md` 已同步生成物维护口径:禁止手写 generated code,格式化 fallback 只能由仓库生成脚本托管。 + +## 3. 边界说明 + +1. 本次收尾不把未稳定的新 story action 写接口、前端迁移或旧 compat 删除并入 `WP-ST`。 +2. `database_migration_operator` 是迁移权限表,本身不导出到业务迁移包;其他业务表必须纳入 `migration.rs`。 +3. `spacetime-client/src/module_bindings/**` 仍是生成物,不承载手写 facade 或领域规则。 +4. 后续若新增 SpacetimeDB 表,必须同时更新表目录、`migration.rs`、绑定生成结果,并让 DDD 边界检查通过。 + +## 4. 验收命令 + +本轮收尾至少执行: + +```powershell +npm.cmd run spacetime:generate -- --rust-only +cargo fmt --all --check --manifest-path server-rs\Cargo.toml +cargo check -p spacetime-module --manifest-path server-rs\Cargo.toml +cargo check -p spacetime-client --manifest-path server-rs\Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding +npm.cmd run api-server:maincloud +``` + +执行结果以本次任务清单记录为准。 + +## 5. 本次执行结果 + +已执行并通过: + +```powershell +npm.cmd run spacetime:generate -- --rust-only +cargo fmt --all --check --manifest-path server-rs\Cargo.toml +cargo check -p spacetime-module --manifest-path server-rs\Cargo.toml +cargo check -p spacetime-client --manifest-path server-rs\Cargo.toml +cargo test -p module-assets --manifest-path server-rs\Cargo.toml +cargo test -p spacetime-client --manifest-path server-rs\Cargo.toml +cargo check -p api-server --manifest-path server-rs\Cargo.toml +npm.cmd run check:server-rs-ddd +npm.cmd run check:encoding +npm.cmd run api-server:maincloud +``` + +结果: + +1. Rust bindings 已生成并同步,`asset_event` 三个生成文件已落盘。 +2. `spacetime-module` 与 `spacetime-client` 编译通过,`module-assets` 8 个测试通过,`spacetime-client` 10 个测试通过。 +3. DDD 边界检查通过 15 个 module crate,编码检查通过 2815 个文件。 +4. `api-server:maincloud` 启动后 `/healthz` 返回 `200 {"ok":true,"service":"genarrative-api-server"}`,本次启动进程已清理并释放 `3100` 端口。 + +已知 warning:`module-runtime-story` 与 `api-server` 仍有非本次 WP-ST 引入的未使用 helper / prompt warning,后续按对应工作包收口,不阻塞本次 WP-ST 关闭。 diff --git a/docs/technical/SPACETIMEDB_TABLE_CATALOG.md b/docs/technical/SPACETIMEDB_TABLE_CATALOG.md index d5a2bc1c..f33d11ae 100644 --- a/docs/technical/SPACETIMEDB_TABLE_CATALOG.md +++ b/docs/technical/SPACETIMEDB_TABLE_CATALOG.md @@ -22,15 +22,30 @@ spacetime sql "SELECT * FROM custom_world_gallery_entry" | 领域 | 表 | | --- | --- | +| 迁移权限 | `database_migration_operator` | | 认证 | `auth_store_snapshot`, `user_account`, `auth_identity`, `refresh_session` | -| 运行时档案 | `runtime_setting`, `runtime_snapshot`, `user_browse_history`, `profile_dashboard_state`, `profile_wallet_ledger`, `profile_redeem_code`, `profile_redeem_code_usage`, `profile_played_world`, `profile_save_archive` | +| 运行时档案 | `runtime_setting`, `runtime_snapshot`, `user_browse_history`, `profile_dashboard_state`, `profile_wallet_ledger`, `profile_redeem_code`, `profile_redeem_code_usage`, `profile_invite_code`, `profile_referral_relation`, `profile_played_world`, `profile_membership`, `profile_recharge_order`, `profile_save_archive` | | RPG 运行时 | `story_session`, `story_event`, `npc_state`, `inventory_slot`, `battle_state`, `treasure_record`, `quest_record`, `quest_log`, `player_progression`, `chapter_progression` | | 世界创作 | `custom_world_profile`, `custom_world_session`, `custom_world_agent_session`, `custom_world_agent_message`, `custom_world_agent_operation`, `custom_world_draft_card`, `custom_world_gallery_entry` | | 拼图 | `puzzle_agent_session`, `puzzle_agent_message`, `puzzle_work_profile`, `puzzle_event`, `puzzle_runtime_run`, `puzzle_leaderboard_entry` | | 大鱼吃小鱼 | `big_fish_creation_session`, `big_fish_agent_message`, `big_fish_asset_slot`, `big_fish_event`, `big_fish_runtime_run` | -| 资产 | `asset_object`, `asset_entity_binding` | +| 资产 | `asset_object`, `asset_entity_binding`, `asset_event` | | AI 任务 | `ai_task`, `ai_task_stage`, `ai_text_chunk`, `ai_result_reference`, `ai_task_event` | +## 迁移权限表 + +### `database_migration_operator` + +- 作用:数据库迁移操作员授权表,用于限制导出、导入和增量导入等 private 表迁移能力,避免任意登录身份读取或覆盖全库真相。 +- 结构:`operator_identity PK: Identity`, `created_at: Timestamp`, `created_by: Identity`, `note: String`。 +- 索引:主键 `operator_identity`。 +- 迁移说明:该表是迁移权限本身,不纳入 `migration.rs` 的业务表导出白名单。 + +```sql +SELECT * FROM database_migration_operator; +SELECT * FROM database_migration_operator WHERE operator_identity = ''; +``` + ## 认证表 ### `auth_store_snapshot` @@ -154,6 +169,29 @@ SELECT * FROM profile_redeem_code_usage WHERE code = ''; SELECT * FROM profile_redeem_code_usage WHERE user_id = ''; ``` +### `profile_invite_code` + +- 作用:用户邀请中心的邀请码主表,保存用户当前稳定邀请码。 +- 结构:`user_id PK: String`, `invite_code: String`, `created_at: Timestamp`, `updated_at: Timestamp`。 +- 索引:主键 `user_id`,唯一索引 `invite_code`。 + +```sql +SELECT * FROM profile_invite_code WHERE user_id = ''; +SELECT * FROM profile_invite_code WHERE invite_code = ''; +``` + +### `profile_referral_relation` + +- 作用:邀请关系表,记录被邀请人与邀请人之间的一次性绑定关系,以及双方奖励是否已发放。 +- 结构:`invitee_user_id PK: String`, `inviter_user_id: String`, `invite_code: String`, `inviter_reward_granted: bool`, `invitee_reward_granted: bool`, `bound_at: Timestamp`。 +- 索引:`inviter_user_id`, `(inviter_user_id, bound_at)`。 + +```sql +SELECT * FROM profile_referral_relation WHERE invitee_user_id = ''; +SELECT * FROM profile_referral_relation WHERE inviter_user_id = ''; +SELECT * FROM profile_referral_relation WHERE inviter_user_id = '' ORDER BY bound_at DESC; +``` + ### `profile_played_world` - 作用:记录用户玩过的世界及最后游玩时间,用于个人页历史和继续游戏入口。 @@ -166,6 +204,27 @@ SELECT * FROM profile_played_world WHERE user_id = '' AND world_key = ' SELECT * FROM profile_played_world WHERE user_id = '' ORDER BY last_played_at DESC; ``` +### `profile_membership` + +- 作用:用户会员状态表,保存会员状态、档位、起止时间和最近更新时间。 +- 结构:`user_id PK: String`, `status: RuntimeProfileMembershipStatus`, `tier: RuntimeProfileMembershipTier`, `started_at: Timestamp`, `expires_at: Timestamp`, `updated_at: Timestamp`。 +- 索引:主键 `user_id`。 + +```sql +SELECT * FROM profile_membership WHERE user_id = ''; +``` + +### `profile_recharge_order` + +- 作用:充值订单表,记录用户购买叙世币或会员的订单、支付渠道、支付时间、积分变更和会员到期时间。 +- 结构:`order_id PK: String`, `user_id: String`, `product_id: String`, `product_title: String`, `kind: RuntimeProfileRechargeProductKind`, `amount_cents: u64`, `status: RuntimeProfileRechargeOrderStatus`, `payment_channel: String`, `paid_at: Timestamp`, `created_at: Timestamp`, `points_delta: i64`, `membership_expires_at: Option`。 +- 索引:`user_id`, `(user_id, created_at)`。 + +```sql +SELECT * FROM profile_recharge_order WHERE order_id = ''; +SELECT * FROM profile_recharge_order WHERE user_id = '' ORDER BY created_at DESC; +``` + ### `profile_save_archive` - 作用:用户存档列表,保存世界信息、封面、当前状态 JSON 和剧情 JSON。 @@ -537,6 +596,19 @@ SELECT * FROM asset_entity_binding WHERE entity_kind = '' AND entit SELECT * FROM asset_entity_binding WHERE asset_object_id = ''; ``` +### `asset_event` + +- 作用:资产事件表,目前记录对象确认和实体槽位绑定变更事实,供订阅端、BFF 或审计流程感知资产主链变化;正式资产状态仍以 `asset_object` 和 `asset_entity_binding` 为准。 +- 可见性:`public event`。 +- 结构:`event_id PK: String`, `asset_object_id: String`, `binding_id: Option`, `event_kind: AssetEventKind`, `asset_kind: String`, `owner_user_id: Option`, `profile_id: Option`, `entity_kind: Option`, `entity_id: Option`, `slot: Option`, `occurred_at: Timestamp`。 +- 索引:`asset_object_id`, `owner_user_id`, `profile_id`。 + +```sql +SELECT * FROM asset_event WHERE asset_object_id = '' ORDER BY occurred_at ASC; +SELECT * FROM asset_event WHERE owner_user_id = '' ORDER BY occurred_at DESC; +SELECT * FROM asset_event WHERE profile_id = '' ORDER BY occurred_at DESC; +``` + ## AI 任务表 ### `ai_task` @@ -599,5 +671,6 @@ SELECT * FROM ai_task_event WHERE owner_user_id = '' ORDER BY occurred_ ## 当前维护风险 -- `story_session`、`story_event`、`npc_state`、`inventory_slot`、`battle_state`、`treasure_record`、`quest_record`、`quest_log`、`player_progression`、`chapter_progression` 在 `src/lib.rs` 与 `src/gameplay/mod.rs` 都能看到表定义。当前编译入口以 `src/lib.rs` 为准;后续完成拆分时,需要删除重复定义或正式挂载子模块,并同步更新本文。 -- `custom_world/*` 子模块中也保留了一份表骨架;当前生成绑定与 `src/lib.rs` 对齐,包含 `public_work_code`、`author_public_user_code` 和 `custom_world_gallery_entry.public_work_code` 索引。维护时不要只看子模块文件。 +- `scripts/check-server-rs-ddd-boundaries.mjs` 已检查 `spacetime-module/src/**` 中的表定义、`migration.rs` 白名单和本文目录项是否一致;新增或删除表后必须先让该检查通过。 +- `database_migration_operator` 是迁移权限表,不导出到业务迁移包;除此之外的业务表都必须进入 `migration.rs`。 +- `spacetime-client/src/module_bindings/**` 是生成物,表、reducer 或 procedure 发生 shape 变化后必须通过 `npm.cmd run spacetime:generate -- --rust-only` 或对应 SpacetimeDB CLI 流程重新生成,不要手写修改。 diff --git a/packages/shared/src/contracts/puzzleRuntimeSession.ts b/packages/shared/src/contracts/puzzleRuntimeSession.ts index 1009f22e..36ddf022 100644 --- a/packages/shared/src/contracts/puzzleRuntimeSession.ts +++ b/packages/shared/src/contracts/puzzleRuntimeSession.ts @@ -70,11 +70,6 @@ export interface StartPuzzleRunRequest { profileId: string; } -export interface AdvanceLocalPuzzleNextLevelRequest { - run: PuzzleRunSnapshot; - sourceSessionId?: string | null; -} - export interface PuzzleRunResponse { run: PuzzleRunSnapshot; } diff --git a/packages/shared/src/contracts/rpgRuntimeContracts.test.ts b/packages/shared/src/contracts/rpgRuntimeContracts.test.ts index d402dcf8..bad8aef4 100644 --- a/packages/shared/src/contracts/rpgRuntimeContracts.test.ts +++ b/packages/shared/src/contracts/rpgRuntimeContracts.test.ts @@ -6,9 +6,8 @@ import { SERVER_RUNTIME_FUNCTION_IDS, TASK5_RUNTIME_OPTION_SCOPES, TASK6_RUNTIME_FUNCTION_IDS, - type RuntimeStoryActionRequest, } from './rpgRuntimeStoryAction'; -import type { RuntimeStoryStateRequest } from './rpgRuntimeStoryState'; +import { type RuntimeStoryActionRequest } from './rpgRuntimeStoryState'; describe('RPG runtime shared contracts', () => { test('拆分后的 runtime story action 契约继续导出常量与类型', () => { @@ -40,12 +39,7 @@ describe('RPG runtime shared contracts', () => { targetStatus: {}, }; - const stateRequest: RuntimeStoryStateRequest = { - sessionId: 'runtime-session-2', - }; - expect(payload.playerMessage).toBe('近况如何?'); - expect(stateRequest.sessionId).toBe('runtime-session-2'); expect(QUEST_NARRATIVE_TYPES).toContain('relationship'); }); }); diff --git a/packages/shared/src/contracts/rpgRuntimeStoryAction.ts b/packages/shared/src/contracts/rpgRuntimeStoryAction.ts index 8c796d8f..6ed91ec5 100644 --- a/packages/shared/src/contracts/rpgRuntimeStoryAction.ts +++ b/packages/shared/src/contracts/rpgRuntimeStoryAction.ts @@ -14,26 +14,6 @@ export type RuntimeAction< payload?: TPayload; }; -export type RuntimeActionRequest< - TAction extends RuntimeAction = RuntimeAction, -> = { - sessionId: string; - clientVersion?: number; - action: TAction; -}; - -export type RuntimeActionResponse< - TViewModel = JsonObject, - TPresentation = JsonObject, - TPatch = JsonObject, -> = { - sessionId: string; - serverVersion: number; - viewModel: TViewModel; - presentation: TPresentation; - patches: TPatch[]; -}; - export const TASK5_RUNTIME_FUNCTION_IDS = [ 'story_continue_adventure', 'story_opening_camp_dialogue', diff --git a/packages/shared/src/contracts/rpgRuntimeStoryState.ts b/packages/shared/src/contracts/rpgRuntimeStoryState.ts index 90dbfc25..25d97af8 100644 --- a/packages/shared/src/contracts/rpgRuntimeStoryState.ts +++ b/packages/shared/src/contracts/rpgRuntimeStoryState.ts @@ -3,10 +3,7 @@ * 该文件只负责 view model、presentation、patch 与 snapshot 回包结构。 */ import type { JsonObject } from './common'; -import type { SavedGameSnapshot, SavedGameSnapshotInput } from './runtime'; import type { - RuntimeActionRequest, - RuntimeActionResponse, RuntimeStoryChoiceAction, RuntimeStoryChoicePayload, RuntimeStoryOptionInteraction, @@ -206,8 +203,10 @@ export type RuntimeStoryPatch = }; export type RuntimeStoryActionRequest = - RuntimeActionRequest & { - snapshot?: SavedGameSnapshotInput; + { + sessionId: string; + clientVersion?: number; + action: RuntimeStoryChoiceAction; }; export type RuntimeStoryAiRequestOptions = { @@ -224,55 +223,3 @@ export type RuntimeStoryAiRequest = { recentActionResult?: string | null; requestOptions?: RuntimeStoryAiRequestOptions; }; - -export type RuntimeStoryStateRequest< - TSnapshotGameState = JsonObject, - TSnapshotCurrentStory = JsonObject, -> = { - sessionId: string; - clientVersion?: number; - snapshot?: SavedGameSnapshotInput< - TSnapshotGameState, - string, - TSnapshotCurrentStory - >; -}; - -export type RuntimeStoryBootstrapRequest< - TProfile = JsonObject, - TCharacter = JsonObject, -> = { - worldType: string; - customWorldProfile?: TProfile | null; - character: TCharacter; - runtimeMode?: 'play' | 'preview' | 'test'; - disablePersistence?: boolean; -}; - -export type RuntimeStoryBootstrapResponse< - TSnapshotGameState = JsonObject, - TSnapshotCurrentStory = JsonObject, -> = { - sessionId: string; - serverVersion: number; - snapshot: SavedGameSnapshot< - TSnapshotGameState, - string, - TSnapshotCurrentStory - >; -}; - -export type RuntimeStoryActionResponse< - TSnapshotGameState = JsonObject, - TSnapshotCurrentStory = JsonObject, -> = RuntimeActionResponse< - RuntimeStoryViewModel, - RuntimeStoryPresentation, - RuntimeStoryPatch -> & { - snapshot: SavedGameSnapshot< - TSnapshotGameState, - string, - TSnapshotCurrentStory - >; -}; diff --git a/packages/shared/src/contracts/story.ts b/packages/shared/src/contracts/story.ts index 9820018c..cd7629a9 100644 --- a/packages/shared/src/contracts/story.ts +++ b/packages/shared/src/contracts/story.ts @@ -1,4 +1,5 @@ import type { JsonObject } from './common'; +import type { SavedGameSnapshotInput } from './runtime'; /** * story session 主链共享契约。 @@ -12,12 +13,32 @@ export type BeginStorySessionRequest = { openingSummary?: string | null; }; +export type BeginStoryRuntimeSessionRequest< + TProfile = JsonObject, + TCharacter = JsonObject, +> = { + worldType: string; + customWorldProfile?: TProfile | null; + character: TCharacter; + runtimeMode?: 'play' | 'preview' | 'test'; + disablePersistence?: boolean; +}; + export type ContinueStoryRequest = { storySessionId: string; narrativeText: string; choiceFunctionId?: string | null; }; +export type ResolveStoryRuntimeActionRequest = { + storySessionId: string; + clientVersion?: number; + functionId: string; + actionText: string; + targetId?: string | null; + payload?: JsonObject | null; +}; + export type StorySessionPayload = { storySessionId: string; runtimeSessionId: string; @@ -52,6 +73,11 @@ export type StorySessionStateResponse = { storyEvents: StoryEventPayload[]; }; +export type StoryRuntimeSnapshotPayload< + TGameState = JsonObject, + TCurrentStory = JsonObject, +> = SavedGameSnapshotInput; + export type StoryRuntimeProjectionRequest = { storySessionId: string; clientVersion?: number; @@ -94,6 +120,7 @@ export type StoryRuntimeProjectionResponse = { storySession: StorySessionPayload; storyEvents: StoryEventPayload[]; serverVersion: number; + gameState: JsonObject; actor: StoryRuntimeActorProjection; inventory: StoryRuntimeInventoryProjection; options: StoryRuntimeOptionProjection[]; @@ -102,3 +129,7 @@ export type StoryRuntimeProjectionResponse = { actionResultText?: string | null; toast?: string | null; }; + +export type StoryRuntimeMutationResponse = { + projection: StoryRuntimeProjectionResponse; +}; diff --git a/scripts/check-server-rs-ddd-boundaries.mjs b/scripts/check-server-rs-ddd-boundaries.mjs index 3963e951..00feb8b7 100644 --- a/scripts/check-server-rs-ddd-boundaries.mjs +++ b/scripts/check-server-rs-ddd-boundaries.mjs @@ -3,6 +3,15 @@ import { basename, join, relative } from 'node:path'; const repoRoot = process.cwd(); const cratesDir = join(repoRoot, 'server-rs', 'crates'); +const spacetimeModuleSrcDir = join(cratesDir, 'spacetime-module', 'src'); +const spacetimeMigrationPath = join(spacetimeModuleSrcDir, 'migration.rs'); +const spacetimeTableCatalogPath = join( + repoRoot, + 'docs', + 'technical', + 'SPACETIMEDB_TABLE_CATALOG.md', +); +const migrationExcludedTables = new Set(['database_migration_operator']); const requiredModuleFiles = [ 'domain.rs', 'commands.rs', @@ -79,6 +88,113 @@ function listRustFiles(dir) { return files; } +function collectSpacetimeTables() { + if (!existsSync(spacetimeModuleSrcDir)) { + return []; + } + + const tableByAccessor = new Map(); + const tablePattern = + /#\[spacetimedb::table\(([\s\S]*?)\)\]\s*(?:#\[[^\]]+\]\s*)*(?:pub\s+)?struct\s+([A-Za-z0-9_]+)/gu; + + for (const rustFile of listRustFiles(spacetimeModuleSrcDir)) { + const text = readText(rustFile); + let match; + while ((match = tablePattern.exec(text)) !== null) { + const accessorMatch = /accessor\s*=\s*([A-Za-z0-9_]+)/u.exec(match[1]); + if (!accessorMatch) { + continue; + } + + const accessor = accessorMatch[1]; + const relativePath = normalizePath(relative(repoRoot, rustFile)); + const previous = tableByAccessor.get(accessor); + if (previous) { + failures.push( + `SpacetimeDB table accessor ${accessor} 重复定义于 ${previous.path} 与 ${relativePath}`, + ); + continue; + } + + tableByAccessor.set(accessor, { + accessor, + structName: match[2], + path: relativePath, + }); + } + } + + return [...tableByAccessor.values()].sort((left, right) => + left.accessor.localeCompare(right.accessor), + ); +} + +function collectMigrationTables() { + if (!existsSync(spacetimeMigrationPath)) { + return new Set(); + } + + const migrationText = readText(spacetimeMigrationPath); + const macroMatch = + /macro_rules!\s+migration_tables\s*\{[\s\S]*?\$macro_name!\s*\{([\s\S]*?)\n\s*\}\s*\n\s*\};\s*\n\}/u.exec( + migrationText, + ); + if (!macroMatch) { + failures.push('migration.rs 无法解析 migration_tables! 白名单'); + return new Set(); + } + + return new Set( + [...macroMatch[1].matchAll(/\b([a-z][a-z0-9_]*)\b/gu)] + .map((match) => match[1]) + .filter((name) => !['arg'].includes(name)), + ); +} + +function collectCatalogTables() { + if (!existsSync(spacetimeTableCatalogPath)) { + return new Set(); + } + + const catalogText = readText(spacetimeTableCatalogPath); + return new Set( + [...catalogText.matchAll(/^### `([^`]+)`/gmu)].map((match) => match[1]), + ); +} + +function checkSpacetimeTableCatalogAndMigration() { + const tables = collectSpacetimeTables(); + const tableNames = new Set(tables.map((table) => table.accessor)); + const migrationTables = collectMigrationTables(); + const catalogTables = collectCatalogTables(); + + for (const table of tables) { + if (!migrationExcludedTables.has(table.accessor) && !migrationTables.has(table.accessor)) { + failures.push( + `${table.path}: SpacetimeDB 表 ${table.accessor} 缺少 migration.rs 白名单`, + ); + } + + if (!catalogTables.has(table.accessor)) { + failures.push( + `${table.path}: SpacetimeDB 表 ${table.accessor} 缺少 SPACETIMEDB_TABLE_CATALOG.md 目录项`, + ); + } + } + + for (const tableName of migrationTables) { + if (!tableNames.has(tableName)) { + failures.push(`migration.rs 白名单包含不存在的 SpacetimeDB 表 ${tableName}`); + } + } + + for (const tableName of catalogTables) { + if (!tableNames.has(tableName)) { + failures.push(`SPACETIMEDB_TABLE_CATALOG.md 包含不存在的 SpacetimeDB 表 ${tableName}`); + } + } +} + function collectModuleCrates() { return readdirSync(cratesDir) .filter((name) => name.startsWith('module-')) @@ -144,6 +260,8 @@ for (const crateName of moduleCrates) { } } +checkSpacetimeTableCatalogAndMigration(); + if (failures.length > 0) { console.error('server-rs DDD boundary check failed:'); for (const failure of failures) { diff --git a/scripts/deploy-rust-remote.sh b/scripts/deploy-rust-remote.sh index 954bde1d..ea4e4e73 100644 --- a/scripts/deploy-rust-remote.sh +++ b/scripts/deploy-rust-remote.sh @@ -381,12 +381,6 @@ const indexPath = path.join(webRoot, 'index.html'); const proxyPrefixes = [ '/api/', '/api', - '/generated-character-drafts', - '/generated-characters', - '/generated-animations', - '/generated-custom-world-scenes', - '/generated-custom-world-covers', - '/generated-qwen-sprites', '/healthz', ]; diff --git a/scripts/generate-spacetime-bindings.mjs b/scripts/generate-spacetime-bindings.mjs index 32a2acef..633c627c 100644 --- a/scripts/generate-spacetime-bindings.mjs +++ b/scripts/generate-spacetime-bindings.mjs @@ -55,7 +55,7 @@ for (const target of selectedTargets) { await recreateTempDir(tempOutDir); console.log(`[spacetime:generate] 生成 ${target.name} bindings 到短路径: ${tempOutDir}`); - await run('spacetime', buildGenerateArgs(target, tempOutDir)); + await generateBindings(target, tempOutDir); const fileCount = await countFiles(tempOutDir); if (fileCount === 0) { @@ -141,7 +141,79 @@ function buildGenerateArgs(target, outDir) { return generateArgs; } -function run(command, commandArgs) { +async function generateBindings(target, outDir) { + const result = await run('spacetime', buildGenerateArgs(target, outDir), { + allowGeneratedFormatFailure: target.lang === 'rust', + }); + + if (result.generatedFormatFailed) { + // Windows 下 SpacetimeDB CLI 2.1.0 会把所有 Rust 文件一次性传给 formatter; + // 这里只接管“文件已生成但 CLI 格式化失败”的尾段,并仍然只同步生成目录。 + console.warn( + `[spacetime:generate] ${target.name} bindings 已生成,但 SpacetimeDB CLI 自带格式化失败;改用短路径分批 rustfmt。`, + ); + await formatRustBindings(outDir); + } +} + +async function formatRustBindings(outDir) { + const rustFiles = await collectRustFiles(outDir); + if (rustFiles.length === 0) { + throw new Error(`Rust bindings 未生成任何 .rs 文件,无法格式化: ${outDir}`); + } + + for (const chunk of chunkCommandArgs(rustFiles)) { + await run('rustfmt', ['--edition', '2024', ...chunk]); + } +} + +async function collectRustFiles(dir) { + const files = []; + const entries = await readdir(dir, {withFileTypes: true}); + + for (const entry of entries) { + const entryPath = path.join(dir, entry.name); + + if (entry.isDirectory()) { + files.push(...(await collectRustFiles(entryPath))); + continue; + } + + if (entry.isFile() && entry.name.endsWith('.rs')) { + files.push(entryPath); + } + } + + return files; +} + +function chunkCommandArgs(argsToChunk) { + // Windows CreateProcess 受命令行长度限制;分批能避免 bindings 文件变多后再次失败。 + const maxCommandLineChars = process.platform === 'win32' ? 20_000 : 100_000; + const chunks = []; + let current = []; + let currentLength = 0; + + for (const arg of argsToChunk) { + const argLength = arg.length + 3; + if (current.length > 0 && currentLength + argLength > maxCommandLineChars) { + chunks.push(current); + current = []; + currentLength = 0; + } + + current.push(arg); + currentLength += argLength; + } + + if (current.length > 0) { + chunks.push(current); + } + + return chunks; +} + +function run(command, commandArgs, options = {}) { return new Promise((resolve, reject) => { const child = spawn(command, commandArgs, { cwd: REPO_ROOT, @@ -171,13 +243,20 @@ function run(command, commandArgs) { return; } - if (output.includes('Could not format generated files')) { + const generatedFormatFailed = output.includes('Could not format generated files'); + + if (generatedFormatFailed && options.allowGeneratedFormatFailure) { + resolve({generatedFormatFailed}); + return; + } + + if (generatedFormatFailed) { reject(new Error(`${command} 生成后格式化失败。`)); return; } if (code === 0) { - resolve(); + resolve({generatedFormatFailed: false}); return; } diff --git a/server-rs/Cargo.lock b/server-rs/Cargo.lock index 39a92dd4..f1606205 100644 --- a/server-rs/Cargo.lock +++ b/server-rs/Cargo.lock @@ -1637,6 +1637,11 @@ dependencies = [ name = "module-story" version = "0.1.0" dependencies = [ + "module-combat", + "module-inventory", + "module-progression", + "module-quest", + "module-runtime-item", "serde", "shared-kernel", "spacetimedb", diff --git a/server-rs/README.md b/server-rs/README.md index f0d15e65..947cca84 100644 --- a/server-rs/README.md +++ b/server-rs/README.md @@ -58,7 +58,7 @@ 1. `crates/spacetime-module` 的表、reducer、view 聚合入口 2. `module-auth` 的身份表、JWT 与 refresh cookie 主链 -3. `platform-oss` 的浏览器直传签名、旧 `/generated-*` 前缀映射与对象 URL 解析能力 +3. `platform-oss` 的浏览器直传签名、旧 `/generated-*` 前缀到 OSS object key 的映射与对象 URL 解析能力;`/generated-*` 不再作为可裸读 HTTP 路由 当前本地脚本补充说明: diff --git a/server-rs/crates/api-server/README.md b/server-rs/crates/api-server/README.md index 585e274c..57e4d4b3 100644 --- a/server-rs/crates/api-server/README.md +++ b/server-rs/crates/api-server/README.md @@ -46,12 +46,10 @@ 22. 接入 `POST /api/assets/sts-upload-credentials` 禁用式 STS 写权限 contract 23. 接入 `custom-world-library`、`custom-world-gallery` 与 agent `publish_world` 首批 Axum facade 24. 接入 custom world agent `session create / session snapshot` Axum facade -25. 接入旧 `runtime story` 兼容接口: - - `POST /api/runtime/story/state/resolve` - - `GET /api/runtime/story/state/{session_id}` - - `POST /api/runtime/story/actions/resolve` - - `POST /api/runtime/story/initial` - - `POST /api/runtime/story/continue` +25. 旧 `runtime story` 兼容接口已下线并保持未挂载;运行时故事写链路改为: + - `POST /api/story/sessions/runtime` + - `GET /api/story/sessions/{story_session_id}/runtime-projection` + - `POST /api/story/sessions/{story_session_id}/actions/resolve` 26. 接入 `POST /api/assets/character-visual/generate` 27. 接入 `GET /api/assets/character-visual/jobs/{task_id}` 28. 接入 `POST /api/assets/character-visual/publish` @@ -62,7 +60,8 @@ 33. 接入 `POST /api/assets/character-animation/generate` 34. 接入 `GET /api/assets/character-animation/jobs/{task_id}` 35. 接入 `POST /api/assets/character-animation/publish` -36. 接入旧 `/generated-character-drafts/*`、`/generated-characters/*`、`/generated-animations/*`、`/generated-custom-world-scenes/*`、`/generated-custom-world-covers/*`、`/generated-qwen-sprites/*` 到 OSS 私有读代理 +36. 下线旧 `/generated-*` 资产直读代理;生成资产读取统一走 `/api/assets/read-url` 或 asset object projection +37. 下线旧 `/api/custom-world/*` 非 runtime 前缀和 `/api/runtime/puzzle/runs/local-next-level`,并用路由回归测试确认这些旧入口保持 `404` 后续与本 crate 直接相关的任务包括: @@ -87,12 +86,13 @@ 19. [x] 接入 `/api/assets/sts-upload-credentials` 20. [x] 接入 `custom world library / gallery / publish_world` 首批 facade 21. [x] 接入 `custom world agent session create / snapshot` facade -22. [x] 接入旧 `runtime story` compat facade +22. [x] 下线旧 `runtime story` compat facade,并接入 story session scoped runtime story 写读入口 23. [x] 接入 `character-visual generate / jobs / publish` 第一批 OSS 主链兼容 facade 24. [x] 接入 `character-animation templates / import-video` 第一批 OSS 草稿兼容 facade 25. [x] 接入 `character-workflow-cache get / save` 第一批 OSS JSON 草稿兼容 facade 26. [x] 接入 `character-animation generate / jobs / publish` 第一批 OSS 主链兼容 facade -27. [x] 接入旧 `/generated-*` 路径到 OSS 私有读同源代理 +27. [x] 下线旧 `/generated-*` 路径 OSS 私有读同源代理 +28. [x] 下线旧 `/api/custom-world/*` 非 runtime 前缀和 Puzzle `local-next-level` 兼容入口 当前 tracing 约定: @@ -161,10 +161,12 @@ 12. 当前微信回调不会把第三方 token 直接透传给前端或 SpacetimeDB,而是统一换成系统签发的 JWT。 13. 当前 `/api/assets/sts-upload-credentials` 按“服务器上传、Web 只下载”口径固定返回 `403`,不向浏览器下发 OSS 写权限。 14. 当前 `/api/runtime/custom-world/agent/sessions` 与 `/api/runtime/custom-world/agent/sessions/{session_id}` 只提供 deterministic session 骨架与 snapshot 读取,不承诺 message submit、operation query、card detail 的完整能力。 -15. 当前 `/api/runtime/story/*` 已在 Rust 侧补齐 compat handler,但内部仍是 `runtime_snapshot` 驱动的兼容桥与确定性动作编排,不应误判为真正的 SpacetimeDB `resolve_story_action` 真相链已完成。 +15. 当前 `/api/runtime/story/*` 不再挂载;runtime story 开局、读取和动作结算通过 `/api/story/sessions/runtime`、`/api/story/sessions/{story_session_id}/runtime-projection` 与 `/api/story/sessions/{story_session_id}/actions/resolve` 进入 BFF,并由 `module-runtime-story`、story session 和 runtime snapshot 投影共同闭合。 16. 当前 `/api/assets/character-visual/*` 第一批只保证旧接口 contract、OSS 草稿/正式对象、`asset_object` 与 `asset_entity_binding` 主链可用;真实图片模型、workflow cache 与本地角色覆盖写回仍在后续阶段。 17. 当前 `/api/assets/character-animation/import-video` 第一批只接受 `data:video/*;base64,...` 并写入 OSS 草稿区,不读取旧本地 `public/` 路径,也不创建正式 `asset_object`。 18. 当前 `/api/assets/character-workflow-cache/*` 第一批只把工作流 JSON 草稿写入 OSS,不迁移历史本地缓存,也不创建正式 `asset_object`。 19. 当前 `/api/assets/character-animation/generate` 第一批只用 Rust 占位产物打通 `AiTaskService + OSS` 草稿链;`image-sequence` 写 SVG 帧,视频类策略优先复用参考视频或仓库内可播放占位视频,不代表真实上游视频模型已完成迁移。 20. 当前 `/api/assets/character-animation/publish` 会把前端提交帧、动作级 manifest 与总 manifest 写入 OSS,并只把总 manifest 确认为 `asset_object` 后绑定到 `character / animation_set`。 -21. 当前旧 `/generated-*` 读取兼容层只代理受支持 generated 前缀到 OSS 私有读签名,不回退仓库 `public/`,Stage 1 不支持视频 Range 分片。 +21. 当前旧 `/generated-*` 直读兼容层已下线;`/generated-*` 只作为 `legacyPublicPath` / OSS object key 标识,读取必须通过 `/api/assets/read-url` 或业务投影中的签名读 URL。 +22. 当前旧 `/api/custom-world/entity`、`/api/custom-world/scene-npc`、`/api/custom-world/scene-image`、`/api/custom-world/cover-image`、`/api/custom-world/cover-upload` 不再挂载,RPG 创作资产入口统一使用 `/api/runtime/custom-world/*`。 +23. 当前旧 `/api/runtime/puzzle/runs/local-next-level` 不再挂载,正式 Puzzle 运行态只通过 `/api/runtime/puzzle/runs/{run_id}/next-level` 进入后端真相源。 diff --git a/server-rs/crates/api-server/src/app.rs b/server-rs/crates/api-server/src/app.rs index 91c59692..57191bc2 100644 --- a/server-rs/crates/api-server/src/app.rs +++ b/server-rs/crates/api-server/src/app.rs @@ -67,12 +67,6 @@ use crate::{ }, error_middleware::normalize_error_response, health::health_check, - legacy_generated_assets::{ - proxy_generated_animations, proxy_generated_big_fish_assets, - proxy_generated_character_drafts, proxy_generated_characters, - proxy_generated_custom_world_covers, proxy_generated_custom_world_scenes, - proxy_generated_puzzle_assets, proxy_generated_qwen_sprites, - }, llm::proxy_llm_chat_completions, login_options::auth_login_options, logout::logout, @@ -81,12 +75,11 @@ use crate::{ password_management::{change_password, reset_password}, phone_auth::{phone_login, send_phone_code}, puzzle::{ - advance_local_puzzle_next_level, advance_puzzle_next_level, create_puzzle_agent_session, - delete_puzzle_work, drag_puzzle_piece_or_group, execute_puzzle_agent_action, - get_puzzle_agent_session, get_puzzle_gallery_detail, get_puzzle_run, - get_puzzle_work_detail, get_puzzle_works, list_puzzle_gallery, put_puzzle_work, - start_puzzle_run, stream_puzzle_agent_message, submit_puzzle_agent_message, - submit_puzzle_leaderboard, swap_puzzle_pieces, + advance_puzzle_next_level, create_puzzle_agent_session, delete_puzzle_work, + drag_puzzle_piece_or_group, execute_puzzle_agent_action, get_puzzle_agent_session, + get_puzzle_gallery_detail, get_puzzle_run, get_puzzle_work_detail, get_puzzle_works, + list_puzzle_gallery, put_puzzle_work, start_puzzle_run, stream_puzzle_agent_message, + submit_puzzle_agent_message, submit_puzzle_leaderboard, swap_puzzle_pieces, }, refresh_session::refresh_session, request_context::{attach_request_context, resolve_request_id}, @@ -117,7 +110,8 @@ use crate::{ create_story_battle, create_story_npc_battle, get_story_battle_state, resolve_story_battle, }, story_sessions::{ - begin_story_session, continue_story, get_story_runtime_projection, get_story_session_state, + begin_story_runtime_session, begin_story_session, continue_story, + get_story_runtime_projection, get_story_session_state, resolve_story_runtime_action, }, wechat_auth::{bind_wechat_phone, handle_wechat_callback, start_wechat_login}, }; @@ -193,38 +187,6 @@ pub fn build_router(state: AppState) -> Router { "/api/auth/public-users/by-id/{user_id}", get(get_public_user_by_id), ) - .route( - "/generated-character-drafts/{*path}", - get(proxy_generated_character_drafts), - ) - .route( - "/generated-characters/{*path}", - get(proxy_generated_characters), - ) - .route( - "/generated-animations/{*path}", - get(proxy_generated_animations), - ) - .route( - "/generated-big-fish-assets/{*path}", - get(proxy_generated_big_fish_assets), - ) - .route( - "/generated-puzzle-assets/{*path}", - get(proxy_generated_puzzle_assets), - ) - .route( - "/generated-custom-world-scenes/{*path}", - get(proxy_generated_custom_world_scenes), - ) - .route( - "/generated-custom-world-covers/{*path}", - get(proxy_generated_custom_world_covers), - ) - .route( - "/generated-qwen-sprites/{*path}", - get(proxy_generated_qwen_sprites), - ) .route( "/api/auth/me", get(auth_me).route_layer(middleware::from_fn_with_state( @@ -738,13 +700,6 @@ pub fn build_router(state: AppState) -> Router { require_bearer_auth, )), ) - .route( - "/api/runtime/puzzle/runs/local-next-level", - post(advance_local_puzzle_next_level).route_layer(middleware::from_fn_with_state( - state.clone(), - require_bearer_auth, - )), - ) .route( "/api/runtime/puzzle/runs/{run_id}", get(get_puzzle_run).route_layer(middleware::from_fn_with_state( @@ -787,13 +742,6 @@ pub fn build_router(state: AppState) -> Router { require_bearer_auth, )), ) - .route( - "/api/custom-world/entity", - post(generate_custom_world_entity).route_layer(middleware::from_fn_with_state( - state.clone(), - require_bearer_auth, - )), - ) .route( "/api/runtime/custom-world/entity", post(generate_custom_world_entity).route_layer(middleware::from_fn_with_state( @@ -801,13 +749,6 @@ pub fn build_router(state: AppState) -> Router { require_bearer_auth, )), ) - .route( - "/api/custom-world/scene-npc", - post(generate_custom_world_scene_npc).route_layer(middleware::from_fn_with_state( - state.clone(), - require_bearer_auth, - )), - ) .route( "/api/runtime/custom-world/scene-npc", post(generate_custom_world_scene_npc).route_layer(middleware::from_fn_with_state( @@ -816,19 +757,12 @@ pub fn build_router(state: AppState) -> Router { )), ) .route( - "/api/custom-world/scene-image", + "/api/runtime/custom-world/scene-image", post(generate_custom_world_scene_image).route_layer(middleware::from_fn_with_state( state.clone(), require_bearer_auth, )), ) - .route( - "/api/custom-world/cover-image", - post(generate_custom_world_cover_image).route_layer(middleware::from_fn_with_state( - state.clone(), - require_bearer_auth, - )), - ) .route( "/api/runtime/custom-world/cover-image", post(generate_custom_world_cover_image).route_layer(middleware::from_fn_with_state( @@ -836,13 +770,6 @@ pub fn build_router(state: AppState) -> Router { require_bearer_auth, )), ) - .route( - "/api/custom-world/cover-upload", - post(upload_custom_world_cover_image).route_layer(middleware::from_fn_with_state( - state.clone(), - require_bearer_auth, - )), - ) .route( "/api/runtime/custom-world/cover-upload", post(upload_custom_world_cover_image).route_layer(middleware::from_fn_with_state( @@ -944,6 +871,13 @@ pub fn build_router(state: AppState) -> Router { require_bearer_auth, )), ) + .route( + "/api/story/sessions/runtime", + post(begin_story_runtime_session).route_layer(middleware::from_fn_with_state( + state.clone(), + require_bearer_auth, + )), + ) .route( "/api/story/sessions/{story_session_id}/state", get(get_story_session_state).route_layer(middleware::from_fn_with_state( @@ -958,6 +892,13 @@ pub fn build_router(state: AppState) -> Router { require_bearer_auth, )), ) + .route( + "/api/story/sessions/{story_session_id}/actions/resolve", + post(resolve_story_runtime_action).route_layer(middleware::from_fn_with_state( + state.clone(), + require_bearer_auth, + )), + ) .route( "/api/story/sessions/continue", post(continue_story).route_layer(middleware::from_fn_with_state( @@ -1263,6 +1204,85 @@ mod tests { } } + #[tokio::test] + async fn deleted_old_routes_are_not_mounted() { + let app = build_router(AppState::new(AppConfig::default()).expect("state should build")); + + // 中文注释:旧 custom-world 非 runtime 前缀没有任何新路由可匹配, + // 因此必须稳定返回 404,避免前端继续误用旧入口。 + for uri in [ + "/api/custom-world/entity", + "/api/custom-world/scene-npc", + "/api/custom-world/scene-image", + "/api/custom-world/cover-image", + "/api/custom-world/cover-upload", + ] { + let response = app + .clone() + .oneshot( + Request::builder() + .method("POST") + .uri(uri) + .header("x-genarrative-response-envelope", "v1") + .body(Body::empty()) + .expect("deleted old route request should build"), + ) + .await + .expect("deleted old route request should be handled"); + + assert_eq!(response.status(), StatusCode::NOT_FOUND); + } + + let response = app + .oneshot( + Request::builder() + .method("POST") + .uri("/api/runtime/puzzle/runs/local-next-level") + .header("x-genarrative-response-envelope", "v1") + .body(Body::empty()) + .expect("deleted old puzzle route request should build"), + ) + .await + .expect("deleted old puzzle route request should be handled"); + + // 中文注释:该路径会被现有 GET /runs/{run_id} 的动态段识别, + // 但 POST 方法没有挂载,返回 405 代表旧 local-next-level handler 已移除。 + assert_eq!(response.status(), StatusCode::METHOD_NOT_ALLOWED); + } + + #[tokio::test] + async fn generated_asset_read_proxy_routes_are_not_mounted() { + let app = build_router(AppState::new(AppConfig::default()).expect("state should build")); + + // 中文注释:生成资产仍可作为 legacyPublicPath 传给 /api/assets/read-url, + // 但不能再通过 /generated-* 同源路由裸读 OSS 对象。 + for uri in [ + "/generated-character-drafts/hero/visual/candidate.png", + "/generated-characters/hero/visual/master.png", + "/generated-animations/hero/idle/frame01.png", + "/generated-big-fish-assets/session-1/level/image.png", + "/generated-puzzle-assets/session-1/candidate/image.png", + "/generated-custom-world-scenes/world-1/camp/scene.png", + "/generated-custom-world-covers/world-1/cover.webp", + "/generated-qwen-sprites/master/candidate-01.png", + ] { + let response = app + .clone() + .oneshot( + Request::builder() + .method("GET") + .uri(uri) + .header("x-genarrative-response-envelope", "v1") + .body(Body::empty()) + .expect("generated asset proxy route request should build"), + ) + .await + .expect("generated asset proxy route request should be handled"); + + assert_eq!(response.status(), StatusCode::NOT_FOUND); + } + } + #[tokio::test] async fn internal_auth_claims_rejects_missing_bearer_token() { let app = build_router(AppState::new(AppConfig::default()).expect("state should build")); diff --git a/server-rs/crates/api-server/src/custom_world_ai.rs b/server-rs/crates/api-server/src/custom_world_ai.rs index 3789b764..c1efe352 100644 --- a/server-rs/crates/api-server/src/custom_world_ai.rs +++ b/server-rs/crates/api-server/src/custom_world_ai.rs @@ -442,6 +442,8 @@ pub async fn generate_custom_world_scene_image( let owner_user_id = authenticated.claims().user_id().to_string(); let normalized = normalize_scene_image_request(payload) .map_err(|error| custom_world_ai_error_response(&request_context, error))?; + require_dashscope_settings(&state) + .map_err(|error| custom_world_ai_error_response(&request_context, error))?; let asset_id = format!("custom-scene-{}", current_utc_millis()); let asset = execute_billable_asset_operation( &state, @@ -703,6 +705,8 @@ pub async fn generate_custom_world_cover_image( trim_to_option(payload.profile.name.as_deref()).unwrap_or_else(|| "world".to_string()); let entity_id = profile_id.clone().unwrap_or_else(|| world_name.clone()); let size = trim_to_option(payload.size.as_deref()).unwrap_or_else(|| "1600*900".to_string()); + require_dashscope_settings(&state) + .map_err(|error| custom_world_ai_error_response(&request_context, error))?; let asset_id = format!("custom-cover-{}", current_utc_millis()); let asset = execute_billable_asset_operation( &state, @@ -2440,10 +2444,16 @@ mod tests { serde_json::from_slice(&body).expect("body should be valid json") } + fn build_state_without_dashscope_key() -> AppState { + let mut config = AppConfig::default(); + config.dashscope_api_key = None; + AppState::new(config).expect("state should build") + } + #[tokio::test] async fn scene_image_returns_service_unavailable_when_dashscope_missing() { - let state = AppState::new(AppConfig::default()).expect("state should build"); - let request_context = build_request_context("POST /api/custom-world/scene-image"); + let state = build_state_without_dashscope_key(); + let request_context = build_request_context("POST /api/runtime/custom-world/scene-image"); let authenticated = build_authenticated(&state); let response = generate_custom_world_scene_image( @@ -2512,15 +2522,27 @@ mod tests { }; let normalized = normalize_scene_image_request(payload).expect("payload should normalize"); + let manual_prompt = build_custom_world_scene_image_prompt(SceneImagePromptParams { + profile: SceneImagePromptProfile { + name: "雾海群岛", + subtitle: "失落航线", + tone: "潮湿、神秘、低魔奇幻", + player_goal: "找到王冠并阻止海妖复苏", + summary: "玩家在雾海中追查沉没王冠。", + setting_text: "群岛被永恒雾潮包围。", + }, + landmark: SceneImagePromptLandmark { + name: "礁石神殿", + description: "古老礁石上的半沉神殿。", + }, + user_prompt: "破碎神殿矗立在蓝绿色雾潮中,潮湿石阶上有幽光贝壳。", + has_reference_image: false, + fallback_landmark_name: Some("礁石神殿"), + fallback_world_name: "雾海群岛", + }); - assert!(normalized.prompt.contains("世界名:雾海群岛")); - assert!(normalized.prompt.contains("世界副标题:失落航线")); - assert!(normalized.prompt.contains("场景名称:礁石神殿")); - assert!( - normalized - .prompt - .contains("本次想要生成的画面内容:破碎神殿") - ); + assert_eq!(normalized.prompt, manual_prompt); + assert!(normalized.prompt.contains("破碎神殿矗立在蓝绿色雾潮中")); assert_ne!( normalized.prompt, "破碎神殿矗立在蓝绿色雾潮中,潮湿石阶上有幽光贝壳。" @@ -2585,8 +2607,8 @@ mod tests { #[tokio::test] async fn cover_image_returns_service_unavailable_when_dashscope_missing() { - let state = AppState::new(AppConfig::default()).expect("state should build"); - let request_context = build_request_context("POST /api/custom-world/cover-image"); + let state = build_state_without_dashscope_key(); + let request_context = build_request_context("POST /api/runtime/custom-world/cover-image"); let authenticated = build_authenticated(&state); let response = generate_custom_world_cover_image( @@ -2622,7 +2644,7 @@ mod tests { #[tokio::test] async fn cover_upload_rejects_invalid_data_url_before_touching_oss() { let state = AppState::new(AppConfig::default()).expect("state should build"); - let request_context = build_request_context("POST /api/custom-world/cover-upload"); + let request_context = build_request_context("POST /api/runtime/custom-world/cover-upload"); let authenticated = build_authenticated(&state); let response = upload_custom_world_cover_image( diff --git a/server-rs/crates/api-server/src/legacy_generated_assets.rs b/server-rs/crates/api-server/src/legacy_generated_assets.rs deleted file mode 100644 index 37ae7c47..00000000 --- a/server-rs/crates/api-server/src/legacy_generated_assets.rs +++ /dev/null @@ -1,246 +0,0 @@ -use axum::{ - extract::{Path, State}, - http::{HeaderMap, HeaderName, HeaderValue, StatusCode, header}, - response::{IntoResponse, Response}, -}; -use platform_oss::{LegacyAssetPrefix, OssSignedGetObjectUrlRequest}; -use serde_json::json; - -use crate::{http_error::AppError, platform_errors::map_oss_error, state::AppState}; - -const CACHE_CONTROL_VALUE: &str = "private, max-age=60"; -const ASSET_OBJECT_KEY_HEADER: &str = "x-genarrative-asset-object-key"; - -pub async fn proxy_generated_character_drafts( - State(state): State, - Path(path): Path, -) -> Response { - proxy_legacy_generated_asset(state, LegacyAssetPrefix::CharacterDrafts, path).await -} - -pub async fn proxy_generated_characters( - State(state): State, - Path(path): Path, -) -> Response { - proxy_legacy_generated_asset(state, LegacyAssetPrefix::Characters, path).await -} - -pub async fn proxy_generated_animations( - State(state): State, - Path(path): Path, -) -> Response { - proxy_legacy_generated_asset(state, LegacyAssetPrefix::Animations, path).await -} - -pub async fn proxy_generated_big_fish_assets( - State(state): State, - Path(path): Path, -) -> Response { - proxy_legacy_generated_asset(state, LegacyAssetPrefix::BigFishAssets, path).await -} - -pub async fn proxy_generated_puzzle_assets( - State(state): State, - Path(path): Path, -) -> Response { - proxy_legacy_generated_asset(state, LegacyAssetPrefix::PuzzleAssets, path).await -} - -pub async fn proxy_generated_custom_world_scenes( - State(state): State, - Path(path): Path, -) -> Response { - proxy_legacy_generated_asset(state, LegacyAssetPrefix::CustomWorldScenes, path).await -} - -pub async fn proxy_generated_custom_world_covers( - State(state): State, - Path(path): Path, -) -> Response { - proxy_legacy_generated_asset(state, LegacyAssetPrefix::CustomWorldCovers, path).await -} - -pub async fn proxy_generated_qwen_sprites( - State(state): State, - Path(path): Path, -) -> Response { - proxy_legacy_generated_asset(state, LegacyAssetPrefix::QwenSprites, path).await -} - -async fn proxy_legacy_generated_asset( - state: AppState, - prefix: LegacyAssetPrefix, - path: String, -) -> Response { - match read_legacy_generated_asset(&state, prefix, path).await { - Ok(response) => response, - Err(error) => error.into_response(), - } -} - -async fn read_legacy_generated_asset( - state: &AppState, - prefix: LegacyAssetPrefix, - path: String, -) -> Result { - let oss_client = state.oss_client().ok_or_else(|| { - AppError::from_status(StatusCode::SERVICE_UNAVAILABLE).with_details(json!({ - "provider": "aliyun-oss", - "reason": "OSS 未完成环境变量配置", - })) - })?; - let object_key = build_generated_object_key(prefix, path.as_str())?; - let signed = oss_client - .sign_get_object_url(OssSignedGetObjectUrlRequest { - object_key: object_key.clone(), - expire_seconds: Some(60), - }) - .map_err(map_legacy_generated_oss_error)?; - let upstream_response = reqwest::Client::new() - .get(signed.signed_url) - .send() - .await - .map_err(|error| { - AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({ - "provider": "aliyun-oss", - "message": format!("读取 OSS 旧 generated 资源失败:{error}"), - })) - })?; - - if upstream_response.status() == reqwest::StatusCode::NOT_FOUND { - return Err( - AppError::from_status(StatusCode::NOT_FOUND).with_details(json!({ - "provider": "aliyun-oss", - "objectKey": object_key, - })), - ); - } - - let status = upstream_response.status(); - let content_type = upstream_response - .headers() - .get(header::CONTENT_TYPE) - .cloned(); - if !status.is_success() { - return Err(map_legacy_generated_upstream_status(status, object_key)); - } - - let bytes = upstream_response.bytes().await.map_err(|error| { - AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({ - "provider": "aliyun-oss", - "message": format!("读取 OSS 旧 generated 资源内容失败:{error}"), - })) - })?; - - // 旧 generated 路径会被 /