Files
Genarrative/server-rs/crates/module-game-distribution/src/errors.rs
T
kdletters 328ac31844 恢复游戏分发完整实现(特性分支)
- 主站:游戏广场、详情、在线游玩、网页发布与作者中心,以及共享契约与客户端服务
- 后端:module-game-distribution 领域层、SpacetimeDB 表/迁移/绑定、spacetime-client facade、api-server 路由与发行网关
- 后台:游戏审核页(待审列表、通过/拒绝、安全下架)
- AGC:发布面板、本地导出包读取命令与发布服务,含默认跳过的真实链路测试
- 运维:发行来源 nginx 模板与门禁、game-distribution:publish 灰度发布开关、OSS PutObject 受控重试
- 文档:主规范、里程碑与实施计划、决策日志与踩坑记录
2026-09-20 20:49:42 +08:00

106 lines
4.0 KiB
Rust

use std::{error::Error, fmt};
use crate::{GameVersionStatus, GameVisibility};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum GameDistributionFieldError {
MissingGameId,
MissingVersionId,
MissingOwnerUserId,
MissingActorUserId,
MissingIdempotencyKey,
MissingRequestDigest,
InvalidPackageDigest,
InvalidPackageBytes,
MissingReviewReason,
}
impl fmt::Display for GameDistributionFieldError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let message = match self {
Self::MissingGameId => "gameId 不能为空",
Self::MissingVersionId => "versionId 不能为空",
Self::MissingOwnerUserId => "ownerUserId 不能为空",
Self::MissingActorUserId => "actorUserId 不能为空",
Self::MissingIdempotencyKey => "Idempotency-Key 不能为空",
Self::MissingRequestDigest => "request digest 不能为空",
Self::InvalidPackageDigest => "packageSha256 必须是 64 位十六进制摘要",
Self::InvalidPackageBytes => "packageBytes 必须大于 0",
Self::MissingReviewReason => "拒绝审核必须提供理由",
};
formatter.write_str(message)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum GameDistributionError {
Field(GameDistributionFieldError),
GameNotFound,
VersionNotFound,
GameAlreadyExists,
VersionAlreadyExists,
Forbidden,
IdempotencyConflict,
IdempotencyOutcomeMismatch,
InvalidVersionTransition {
from: GameVersionStatus,
to: GameVersionStatus,
},
PublicationConflict {
expected: u64,
actual: u64,
},
PackageMismatch,
GameSuspended,
GameNotPublished,
VersionNotPublished,
VersionAlreadyPublished,
ReviewActionNotAllowed,
}
impl fmt::Display for GameDistributionError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Field(field) => field.fmt(formatter),
Self::GameNotFound => formatter.write_str("游戏不存在"),
Self::VersionNotFound => formatter.write_str("游戏版本不存在"),
Self::GameAlreadyExists => formatter.write_str("游戏已存在"),
Self::VersionAlreadyExists => formatter.write_str("游戏版本已存在"),
Self::Forbidden => formatter.write_str("无权操作该游戏"),
Self::IdempotencyConflict => formatter.write_str("幂等键对应的请求摘要不一致"),
Self::IdempotencyOutcomeMismatch => formatter.write_str("幂等记录结果类型不一致"),
Self::InvalidVersionTransition { from, to } => {
write!(formatter, "版本状态不能从 {from:?} 变为 {to:?}")
}
Self::PublicationConflict { expected, actual } => {
write!(
formatter,
"公开状态版本冲突:期望 {expected},实际 {actual}"
)
}
Self::PackageMismatch => formatter.write_str("发行包摘要或字节数与版本声明不一致"),
Self::GameSuspended => formatter.write_str("游戏已被管理员下架"),
Self::GameNotPublished => formatter.write_str("游戏当前未公开"),
Self::VersionNotPublished => formatter.write_str("游戏版本当前未公开"),
Self::VersionAlreadyPublished => formatter.write_str("游戏版本已经公开"),
Self::ReviewActionNotAllowed => formatter.write_str("当前版本不允许审核操作"),
}
}
}
impl Error for GameDistributionError {}
impl From<GameDistributionFieldError> for GameDistributionError {
fn from(value: GameDistributionFieldError) -> Self {
Self::Field(value)
}
}
#[allow(dead_code)]
fn _visibility_is_exhaustive(visibility: GameVisibility) -> bool {
matches!(
visibility,
GameVisibility::Unpublished | GameVisibility::Published | GameVisibility::Suspended
)
}