Integrate unfinished server-rs refactor worklists
This commit is contained in:
51
server-rs/crates/module-puzzle/README.md
Normal file
51
server-rs/crates/module-puzzle/README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# module-puzzle 独立模块 package 说明
|
||||
|
||||
日期:`2026-04-29`
|
||||
|
||||
## 1. package 职责
|
||||
|
||||
`module-puzzle` 是拼图创作、作品 profile 与运行态规则模块 package,后续负责:
|
||||
|
||||
1. Puzzle Agent 会话、消息、锚点包与结果草稿的纯领域模型。
|
||||
2. 拼图作品 profile、发布门禁、标签规则与作品列表投影。
|
||||
3. 拼图运行态开局、交换、拖动、合并、拆分、过关和下一关推荐规则。
|
||||
4. 与 `spacetime-module` 的拼图表、reducer、procedure 和事件聚合对接。
|
||||
|
||||
## 2. 当前阶段说明
|
||||
|
||||
当前阶段已经不再只是目录占位,已先固定拼图领域 contract 与最小规则函数。
|
||||
|
||||
当前已落地:
|
||||
|
||||
1. `src/domain.rs` 承接 Puzzle 基础 ID 前缀、标签数量、洗牌次数常量、基础枚举、Agent session、message、anchor、result draft、work profile、runtime board/run 等领域类型。
|
||||
2. `src/commands.rs` 承接 SpacetimeDB procedure/reducer 写入输入。
|
||||
3. `src/application.rs` 承接 procedure 返回包装、标签归一化、草稿编译、发布覆盖、开局、交换、拖动、合并、拆分和下一关推荐的纯规则。
|
||||
4. `src/errors.rs` 承接拼图字段错误与中文错误文案。
|
||||
5. `src/events.rs` 承接草稿变化、作品发布和运行态推进的最小领域事件。
|
||||
6. `spacetime-types` feature 下可供 SpacetimeDB 绑定复用的类型派生。
|
||||
|
||||
当前 crate 仍然只承接:
|
||||
|
||||
1. 拼图领域常量、枚举、快照类型和纯规则。
|
||||
2. 字段校验、标签归一化与运行态规则。
|
||||
3. 后续 `spacetime-module` 聚合表时需要复用的领域边界。
|
||||
|
||||
当前阶段明确不提前进入:
|
||||
|
||||
1. 图片生成、OSS 上传或 AI prompt 编排。
|
||||
2. Axum 路由、SSE 或前端展示状态。
|
||||
3. SpacetimeDB table、reducer、procedure 的直接定义。
|
||||
|
||||
当前设计依据:
|
||||
|
||||
1. [../../../docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_ENUM_REHOME_2026-04-29.md](../../../docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_ENUM_REHOME_2026-04-29.md)
|
||||
2. [../../../docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_SPLIT_2026-04-29.md](../../../docs/technical/SERVER_RS_DDD_WP_PZ_DOMAIN_SPLIT_2026-04-29.md)
|
||||
3. [../../../docs/technical/PUZZLE_CREATION_AND_RUNTIME_MINIMAL_IMPLEMENTATION_2026-04-22.md](../../../docs/technical/PUZZLE_CREATION_AND_RUNTIME_MINIMAL_IMPLEMENTATION_2026-04-22.md)
|
||||
4. [../../../docs/technical/PUZZLE_RESULT_AUTOSAVE_AND_TAG_GATE_FIX_2026-04-28.md](../../../docs/technical/PUZZLE_RESULT_AUTOSAVE_AND_TAG_GATE_FIX_2026-04-28.md)
|
||||
|
||||
## 3. 边界约束
|
||||
|
||||
1. `module-puzzle` 不直接调用图片生成、OSS、HTTP、SSE 或 SpacetimeDB SDK。
|
||||
2. 领域函数不依赖前端临时状态,拼图运行态真相最终由 SpacetimeDB 表承载。
|
||||
3. `api-server` 负责 LLM、图片生成和请求响应映射;`spacetime-module` 负责表、reducer、procedure 与事件。
|
||||
4. 后续迁移必须优先复用现有 `domain.rs`、`commands.rs`、`application.rs`、`events.rs`、`errors.rs` 骨架。
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,181 @@
|
||||
//! 拼图写入命令过渡落位。
|
||||
//! 拼图写入命令。
|
||||
//!
|
||||
//! 用于表达会话消息、作品更新、发布、开局、交换拼图块和过关推进等输入。
|
||||
//! 命令只表达 SpacetimeDB procedure/reducer 需要写入的意图和参数,
|
||||
//! 不包含 HTTP、前端展示或 SpacetimeDB table 操作。
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(feature = "spacetime-types")]
|
||||
use spacetimedb::SpacetimeType;
|
||||
|
||||
use crate::domain::PuzzleAgentStage;
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleAgentSessionCreateInput {
|
||||
pub session_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub seed_text: String,
|
||||
pub welcome_message_id: String,
|
||||
pub welcome_message_text: String,
|
||||
pub created_at_micros: i64,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleAgentSessionGetInput {
|
||||
pub session_id: String,
|
||||
pub owner_user_id: String,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleAgentMessageSubmitInput {
|
||||
pub session_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub user_message_id: String,
|
||||
pub user_message_text: String,
|
||||
pub submitted_at_micros: i64,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleAgentMessageFinalizeInput {
|
||||
pub session_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub assistant_message_id: Option<String>,
|
||||
pub assistant_reply_text: Option<String>,
|
||||
pub stage: PuzzleAgentStage,
|
||||
pub progress_percent: u32,
|
||||
pub anchor_pack_json: String,
|
||||
pub error_message: Option<String>,
|
||||
pub updated_at_micros: i64,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleDraftCompileInput {
|
||||
pub session_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub compiled_at_micros: i64,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleGeneratedImagesSaveInput {
|
||||
pub session_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub candidates_json: String,
|
||||
pub saved_at_micros: i64,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleSelectCoverImageInput {
|
||||
pub session_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub candidate_id: String,
|
||||
pub selected_at_micros: i64,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzlePublishInput {
|
||||
pub session_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub work_id: String,
|
||||
pub profile_id: String,
|
||||
pub author_display_name: String,
|
||||
pub level_name: Option<String>,
|
||||
pub summary: Option<String>,
|
||||
pub theme_tags: Option<Vec<String>>,
|
||||
pub published_at_micros: i64,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleWorksListInput {
|
||||
pub owner_user_id: String,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleWorkGetInput {
|
||||
pub profile_id: String,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleWorkDeleteInput {
|
||||
pub profile_id: String,
|
||||
pub owner_user_id: String,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleWorkUpsertInput {
|
||||
pub profile_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub level_name: String,
|
||||
pub summary: String,
|
||||
pub theme_tags: Vec<String>,
|
||||
pub cover_image_src: Option<String>,
|
||||
pub cover_asset_id: Option<String>,
|
||||
pub updated_at_micros: i64,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleRunStartInput {
|
||||
pub run_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub profile_id: String,
|
||||
pub started_at_micros: i64,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleRunGetInput {
|
||||
pub run_id: String,
|
||||
pub owner_user_id: String,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleRunSwapInput {
|
||||
pub run_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub first_piece_id: String,
|
||||
pub second_piece_id: String,
|
||||
pub swapped_at_micros: i64,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleRunDragInput {
|
||||
pub run_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub piece_id: String,
|
||||
pub target_row: u32,
|
||||
pub target_col: u32,
|
||||
pub dragged_at_micros: i64,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleRunNextLevelInput {
|
||||
pub run_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub advanced_at_micros: i64,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleLeaderboardSubmitInput {
|
||||
pub run_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub profile_id: String,
|
||||
pub grid_size: u32,
|
||||
pub elapsed_ms: u64,
|
||||
pub nickname: String,
|
||||
pub submitted_at_micros: i64,
|
||||
}
|
||||
|
||||
@@ -2,3 +2,353 @@
|
||||
//!
|
||||
//! 后续迁移拼图 Agent 会话、作品 profile 和运行态聚合时,只保留玩法规则;
|
||||
//! 图片生成、发布 HTTP shape 和排行榜适配留在外层。
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(feature = "spacetime-types")]
|
||||
use spacetimedb::SpacetimeType;
|
||||
|
||||
pub const PUZZLE_AGENT_SESSION_ID_PREFIX: &str = "puzzle-session-";
|
||||
pub const PUZZLE_AGENT_MESSAGE_ID_PREFIX: &str = "puzzle-message-";
|
||||
pub const PUZZLE_PROFILE_ID_PREFIX: &str = "puzzle-profile-";
|
||||
pub const PUZZLE_RUN_ID_PREFIX: &str = "puzzle-run-";
|
||||
pub const PUZZLE_MIN_TAG_COUNT: usize = 3;
|
||||
pub const PUZZLE_MAX_TAG_COUNT: usize = 6;
|
||||
pub(crate) const PUZZLE_INITIAL_SHUFFLE_ATTEMPTS: u64 = 64;
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum PuzzleAgentStage {
|
||||
CollectingAnchors,
|
||||
DraftReady,
|
||||
ImageRefining,
|
||||
ReadyToPublish,
|
||||
Published,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum PuzzleAnchorStatus {
|
||||
Missing,
|
||||
Inferred,
|
||||
Confirmed,
|
||||
Locked,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum PuzzleAgentMessageRole {
|
||||
User,
|
||||
Assistant,
|
||||
System,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum PuzzleAgentMessageKind {
|
||||
Chat,
|
||||
Summary,
|
||||
ActionResult,
|
||||
Warning,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum PuzzlePublicationStatus {
|
||||
Draft,
|
||||
Published,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum PuzzleRuntimeLevelStatus {
|
||||
Playing,
|
||||
Cleared,
|
||||
}
|
||||
|
||||
impl PuzzleAgentStage {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::CollectingAnchors => "collecting_anchors",
|
||||
Self::DraftReady => "draft_ready",
|
||||
Self::ImageRefining => "image_refining",
|
||||
Self::ReadyToPublish => "ready_to_publish",
|
||||
Self::Published => "published",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PuzzleAnchorStatus {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Missing => "missing",
|
||||
Self::Inferred => "inferred",
|
||||
Self::Confirmed => "confirmed",
|
||||
Self::Locked => "locked",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PuzzleAgentMessageRole {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::User => "user",
|
||||
Self::Assistant => "assistant",
|
||||
Self::System => "system",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PuzzleAgentMessageKind {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Chat => "chat",
|
||||
Self::Summary => "summary",
|
||||
Self::ActionResult => "action_result",
|
||||
Self::Warning => "warning",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PuzzlePublicationStatus {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Draft => "draft",
|
||||
Self::Published => "published",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PuzzleRuntimeLevelStatus {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Playing => "playing",
|
||||
Self::Cleared => "cleared",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleAnchorItem {
|
||||
pub key: String,
|
||||
pub label: String,
|
||||
pub value: String,
|
||||
pub status: PuzzleAnchorStatus,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleAnchorPack {
|
||||
pub theme_promise: PuzzleAnchorItem,
|
||||
pub visual_subject: PuzzleAnchorItem,
|
||||
pub visual_mood: PuzzleAnchorItem,
|
||||
pub composition_hooks: PuzzleAnchorItem,
|
||||
pub tags_and_forbidden: PuzzleAnchorItem,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleCreatorIntent {
|
||||
pub source_mode: String,
|
||||
pub raw_messages_summary: String,
|
||||
pub theme_promise: String,
|
||||
pub visual_subject: String,
|
||||
pub visual_mood: Vec<String>,
|
||||
pub composition_hooks: Vec<String>,
|
||||
pub theme_tags: Vec<String>,
|
||||
pub forbidden_directives: Vec<String>,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleGeneratedImageCandidate {
|
||||
pub candidate_id: String,
|
||||
pub image_src: String,
|
||||
pub asset_id: String,
|
||||
pub prompt: String,
|
||||
pub actual_prompt: Option<String>,
|
||||
pub source_type: String,
|
||||
pub selected: bool,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleResultDraft {
|
||||
pub level_name: String,
|
||||
pub summary: String,
|
||||
pub theme_tags: Vec<String>,
|
||||
pub forbidden_directives: Vec<String>,
|
||||
pub creator_intent: Option<PuzzleCreatorIntent>,
|
||||
pub anchor_pack: PuzzleAnchorPack,
|
||||
pub candidates: Vec<PuzzleGeneratedImageCandidate>,
|
||||
pub selected_candidate_id: Option<String>,
|
||||
pub cover_image_src: Option<String>,
|
||||
pub cover_asset_id: Option<String>,
|
||||
pub generation_status: String,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleResultPreviewBlocker {
|
||||
pub id: String,
|
||||
pub code: String,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleResultPreviewFinding {
|
||||
pub id: String,
|
||||
pub severity: String,
|
||||
pub code: String,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleResultPreviewEnvelope {
|
||||
pub draft: PuzzleResultDraft,
|
||||
pub blockers: Vec<PuzzleResultPreviewBlocker>,
|
||||
pub quality_findings: Vec<PuzzleResultPreviewFinding>,
|
||||
pub publish_ready: bool,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleAgentMessageSnapshot {
|
||||
pub message_id: String,
|
||||
pub session_id: String,
|
||||
pub role: PuzzleAgentMessageRole,
|
||||
pub kind: PuzzleAgentMessageKind,
|
||||
pub text: String,
|
||||
pub created_at_micros: i64,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleAgentSuggestedAction {
|
||||
pub id: String,
|
||||
pub action_type: String,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleAgentSessionSnapshot {
|
||||
pub session_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub seed_text: String,
|
||||
pub current_turn: u32,
|
||||
pub progress_percent: u32,
|
||||
pub stage: PuzzleAgentStage,
|
||||
pub anchor_pack: PuzzleAnchorPack,
|
||||
pub draft: Option<PuzzleResultDraft>,
|
||||
pub messages: Vec<PuzzleAgentMessageSnapshot>,
|
||||
pub last_assistant_reply: Option<String>,
|
||||
pub published_profile_id: Option<String>,
|
||||
pub suggested_actions: Vec<PuzzleAgentSuggestedAction>,
|
||||
pub result_preview: Option<PuzzleResultPreviewEnvelope>,
|
||||
pub created_at_micros: i64,
|
||||
pub updated_at_micros: i64,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleWorkProfile {
|
||||
pub work_id: String,
|
||||
pub profile_id: String,
|
||||
pub owner_user_id: String,
|
||||
pub source_session_id: Option<String>,
|
||||
pub author_display_name: String,
|
||||
pub level_name: String,
|
||||
pub summary: String,
|
||||
pub theme_tags: Vec<String>,
|
||||
pub cover_image_src: Option<String>,
|
||||
pub cover_asset_id: Option<String>,
|
||||
pub publication_status: PuzzlePublicationStatus,
|
||||
pub updated_at_micros: i64,
|
||||
pub published_at_micros: Option<i64>,
|
||||
pub play_count: u32,
|
||||
pub publish_ready: bool,
|
||||
pub anchor_pack: PuzzleAnchorPack,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleCellPosition {
|
||||
pub row: u32,
|
||||
pub col: u32,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzlePieceState {
|
||||
pub piece_id: String,
|
||||
pub correct_row: u32,
|
||||
pub correct_col: u32,
|
||||
pub current_row: u32,
|
||||
pub current_col: u32,
|
||||
pub merged_group_id: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleMergedGroupState {
|
||||
pub group_id: String,
|
||||
pub piece_ids: Vec<String>,
|
||||
pub occupied_cells: Vec<PuzzleCellPosition>,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleLeaderboardEntry {
|
||||
pub rank: u32,
|
||||
pub nickname: String,
|
||||
pub elapsed_ms: u64,
|
||||
pub is_current_player: bool,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleBoardSnapshot {
|
||||
pub rows: u32,
|
||||
pub cols: u32,
|
||||
pub pieces: Vec<PuzzlePieceState>,
|
||||
pub merged_groups: Vec<PuzzleMergedGroupState>,
|
||||
pub selected_piece_id: Option<String>,
|
||||
pub all_tiles_resolved: bool,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleRuntimeLevelSnapshot {
|
||||
pub run_id: String,
|
||||
pub level_index: u32,
|
||||
pub grid_size: u32,
|
||||
pub profile_id: String,
|
||||
pub level_name: String,
|
||||
pub author_display_name: String,
|
||||
pub theme_tags: Vec<String>,
|
||||
pub cover_image_src: Option<String>,
|
||||
pub board: PuzzleBoardSnapshot,
|
||||
pub status: PuzzleRuntimeLevelStatus,
|
||||
pub started_at_ms: u64,
|
||||
pub cleared_at_ms: Option<u64>,
|
||||
pub elapsed_ms: Option<u64>,
|
||||
pub leaderboard_entries: Vec<PuzzleLeaderboardEntry>,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PuzzleRunSnapshot {
|
||||
pub run_id: String,
|
||||
pub entry_profile_id: String,
|
||||
pub cleared_level_count: u32,
|
||||
pub current_level_index: u32,
|
||||
pub current_grid_size: u32,
|
||||
pub played_profile_ids: Vec<String>,
|
||||
pub previous_level_tags: Vec<String>,
|
||||
pub current_level: Option<PuzzleRuntimeLevelSnapshot>,
|
||||
pub recommended_next_profile_id: Option<String>,
|
||||
pub leaderboard_entries: Vec<PuzzleLeaderboardEntry>,
|
||||
}
|
||||
|
||||
@@ -1,3 +1,39 @@
|
||||
//! 拼图领域错误过渡落位。
|
||||
//! 拼图领域错误。
|
||||
//!
|
||||
//! 错误只表达拼图业务失败,例如标签不足、移动非法或运行态不存在。
|
||||
//! 错误只表达玩法规则失败,例如标签不足、移动非法或运行态不存在;
|
||||
//! HTTP 状态码与 SpacetimeDB 字符串错误由 adapter 映射。
|
||||
|
||||
use std::{error::Error, fmt};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum PuzzleFieldError {
|
||||
MissingText,
|
||||
MissingSessionId,
|
||||
MissingProfileId,
|
||||
MissingRunId,
|
||||
MissingPieceId,
|
||||
MissingAuthorDisplayName,
|
||||
InvalidTagCount,
|
||||
InvalidGridSize,
|
||||
InvalidTargetCell,
|
||||
InvalidOperation,
|
||||
}
|
||||
|
||||
impl fmt::Display for PuzzleFieldError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::MissingText => write!(f, "必填文本缺失"),
|
||||
Self::MissingSessionId => write!(f, "session_id 缺失"),
|
||||
Self::MissingProfileId => write!(f, "profile_id 缺失"),
|
||||
Self::MissingRunId => write!(f, "run_id 缺失"),
|
||||
Self::MissingPieceId => write!(f, "piece_id 缺失"),
|
||||
Self::MissingAuthorDisplayName => write!(f, "author_display_name 缺失"),
|
||||
Self::InvalidTagCount => write!(f, "标签数量不合法"),
|
||||
Self::InvalidGridSize => write!(f, "网格规格不合法"),
|
||||
Self::InvalidTargetCell => write!(f, "目标格子不合法"),
|
||||
Self::InvalidOperation => write!(f, "操作不合法"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for PuzzleFieldError {}
|
||||
|
||||
@@ -1,3 +1,27 @@
|
||||
//! 拼图领域事件过渡落位。
|
||||
//!
|
||||
//! 用于表达草稿变化、作品发布、运行态推进和排行榜候选产生等事实。
|
||||
|
||||
/// 拼图领域事件。
|
||||
///
|
||||
/// 事件只描述已经发生的领域事实,持久化、订阅投影和 HTTP/SSE 通知
|
||||
/// 均由 SpacetimeDB adapter 或 BFF 决定。
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum PuzzleDomainEvent {
|
||||
DraftChanged {
|
||||
session_id: String,
|
||||
owner_user_id: String,
|
||||
occurred_at_micros: i64,
|
||||
},
|
||||
WorkPublished {
|
||||
profile_id: String,
|
||||
owner_user_id: String,
|
||||
occurred_at_micros: i64,
|
||||
},
|
||||
RunAdvanced {
|
||||
run_id: String,
|
||||
owner_user_id: String,
|
||||
level_index: u32,
|
||||
occurred_at_micros: i64,
|
||||
},
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user