feat(游戏共创): 共创主题(三)后台写路径(5 条 admin 路由 + 事务 + 幂等)
Project CI / AI game creator shell Rust crates (pull_request) Successful in 5m59s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m47s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m43s
Project CI / Backend tests (pull_request) Successful in 9m27s
Project CI / Frontend tests (pull_request) Successful in 3m13s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m24s
Project CI / Repository checks (pull_request) Successful in 5m4s
Project CI / Native shell tests (pull_request) Successful in 6m50s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 5m59s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m47s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m43s
Project CI / Backend tests (pull_request) Successful in 9m27s
Project CI / Frontend tests (pull_request) Successful in 3m13s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m24s
Project CI / Repository checks (pull_request) Successful in 5m4s
Project CI / Native shell tests (pull_request) Successful in 6m50s
按 §3.10.8:后台 UI 不在本轮,本轮只落**接口与事务**。
- **鉴权(照仓库既有 admin 体系,不自造)**:5 条路由挂在既有 `/admin/api/game-distribution/*` 支,统一 `route_layer(require_admin_auth)`,handler 取 `Extension<AuthenticatedAdmin>`;未带 / 失效会话 → **401 `UNAUTHORIZED`**(与同组 games/reviews 路由同码同形),非 admin role → 403。模块侧第二层:写 procedure 与后台列表都要求受信服务身份(`require_editor_generation_runtime_service_identity`),只有 api-server 能调。
- **5 条路由**:`POST /themes`(创建;服务端生成 `theme-{uuid}`;要求 `Idempotency-Key`,摘要只覆盖客户端可见请求体,**不含重试时重新生成的 `theme_id`**)、`PUT /themes/{theme_id}`(整体覆盖 name/summary/badge/sortOrder/status,生效即刷新 `updated_at`,重放不写库不刷新)、`PUT /themes/{theme_id}/members/{root_game_id}`(**不要求幂等键**——确定性主键天然幂等;可带 `sortOrder`)、`DELETE …/members/{root_game_id}`(不要求幂等键;成员不存在也算 200,响应刻意不含「之前存不存在」→ 重复调用逐字节相同)、`GET /themes?limit=&status=`(**含 draft/archived**,status 白名单 `all|draft|published|archived`,limit 缺省与上限 200、超界截断、无游标)。
- **成员只允许根作品**:用 `game_distribution_theme_root_acceptable(has_lineage_row)`,非根 → **409 `THEME_MEMBER_NOT_ROOT`**;作品不存在 → 404 `THEME_MEMBER_GAME_NOT_FOUND`;主题不存在 → 404 `THEME_NOT_FOUND`。**不做「成员必须已公开」的前置校验**(运营可先挂草稿根,作品公开后自动出现在公开投影里);归档主题**不清理**成员行(与公开投影「跳过不删行」同一口径)。
- **领域错误码**:`module-game-distribution/src/errors.rs` 新增 5 个带码错误(`ThemeNotFound` / `ThemeBadRequest{reason}` / `ThemeIdempotencyConflict` / `ThemeMemberNotRoot{game_id}` / `ThemeMemberGameNotFound{game_id}`),Display 一律 `CODE: 文案`,并由测试钉住「码后必须跟冒号」;api-server 集中映射,不落 axum 默认 422 纯文本。
- **纯函数补充**:后台 status 过滤器白名单(`all` + 三态的单一实现)、文本上限常量(名称 40 / 简介 200 / 角标 16,按 UI 一行与卡片摘要的量级取,写进注释)与 `game_distribution_theme_text_violation`(trim 后判空名与超长,返回可拼进 `THEME_BAD_REQUEST:` 的原因)。
- **测试**:`theme.rs` +2 纯函数(status 过滤器白名单、文本违规判定)、`domain.rs` +1(错误码前缀)、spacetime-module +8 事务结构断言、client mapper +3、api-server +6(401 同码同形、创建/编辑/成员增删的形状与错误码、后台列表含 draft、重复 PUT 幂等、非根成员被拒)→ `cargo test -p api-server game_distribution` **85 passed**、`module-game-distribution` **100 passed**、`spacetime-module` **286 passed**、`spacetime-client` 37 passed。
- **门禁**:wasm build 0;`cargo check --all-targets` 0;`cargo test -p api-server game_distribution` 85 passed;`cargo test -p module-game-distribution` 100 passed;`cargo test -p spacetime-module` 286 passed / 1 ignored;`cargo test -p spacetime-client` 37 passed;DTO parity 58 组 / 10 构建器;`check:spacetime-schema` 0(96 tables);`check:encoding` 0(5414 files);`cargo fmt --all -- --check` 0;`git diff --check` 0。
- 生成绑定:新增 15 个 `module_bindings/*theme*/admin_theme*` 文件 + 入口声明(无重排);其余 202 个文件的 rustfmt 漂移按上一轮做法逐字节还原,未跑任何 git 写命令。
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -437,4 +437,55 @@ mod fork_authorization_tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 共创主题同理:后台写接口的错误码同样靠前缀被 HTTP 层识别,文案格式一变就静默退化成通用 4xx。
|
||||
///
|
||||
/// 这里钉的是**领域侧**的半张表(字面量),另一半(`shared-contracts` 的常量 + 状态码)由
|
||||
/// api-server 的映射测试钉;两半都对着同一组字符串,因此任何一侧改字都会红。
|
||||
#[test]
|
||||
fn theme_errors_are_prefixed_with_their_machine_code() {
|
||||
let cases = [
|
||||
(GameDistributionError::ThemeNotFound, "THEME_NOT_FOUND"),
|
||||
(
|
||||
GameDistributionError::ThemeBadRequest {
|
||||
reason: "主题名不能为空".to_string(),
|
||||
},
|
||||
"THEME_BAD_REQUEST",
|
||||
),
|
||||
(
|
||||
GameDistributionError::ThemeIdempotencyConflict,
|
||||
"THEME_IDEMPOTENCY_CONFLICT",
|
||||
),
|
||||
(
|
||||
GameDistributionError::ThemeMemberNotRoot {
|
||||
game_id: "game_child".to_string(),
|
||||
},
|
||||
"THEME_MEMBER_NOT_ROOT",
|
||||
),
|
||||
(
|
||||
GameDistributionError::ThemeMemberGameNotFound {
|
||||
game_id: "game_missing".to_string(),
|
||||
},
|
||||
"THEME_MEMBER_GAME_NOT_FOUND",
|
||||
),
|
||||
];
|
||||
for (error, code) in cases {
|
||||
let message = error.to_string();
|
||||
assert!(
|
||||
message.starts_with(code),
|
||||
"expected {code} prefix, got {message}"
|
||||
);
|
||||
// 前缀后面必须真的跟冒号:api-server 用 `split(':').next()` 取码,缺了冒号就会把整句
|
||||
// 文案当成码,落到未登记分支变成 `THEME_ERROR`。
|
||||
assert!(
|
||||
message.starts_with(&format!("{code}:")),
|
||||
"expected {code} to be followed by ':', got {message}"
|
||||
);
|
||||
}
|
||||
// 空名原因要原样透出:400 只有一个状态码,区分「空名」与「超长」只有靠这句。
|
||||
let empty_name = GameDistributionError::ThemeBadRequest {
|
||||
reason: "主题名不能为空".to_string(),
|
||||
};
|
||||
assert!(empty_name.to_string().contains("主题名不能为空"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +79,31 @@ pub enum GameDistributionError {
|
||||
ForkSourceVersionMismatch,
|
||||
/// 共创授权:已存在的作品不能再声明改编来源。
|
||||
ForkDeclarationOnExistingGame,
|
||||
/// 共创主题:后台按 `theme_id` 找不到主题。
|
||||
///
|
||||
/// 与公开侧的「主题不存在 / 未发布」文案**不是**同一条:公开侧刻意不带错误码,也不确认未发布
|
||||
/// 主题的存在性(见 `spacetime-module` 的 `GAME_DISTRIBUTION_THEME_NOT_FOUND_MESSAGE`)。
|
||||
ThemeNotFound,
|
||||
/// 共创主题:请求参数非法(空名 / 超长 / 状态不在白名单)。
|
||||
///
|
||||
/// 带 `reason` 而不是固定文案:空名与「名字超过 40 个字符」都要让运营一眼看懂改哪里,而 400
|
||||
/// 这一个状态码不足以区分它们。
|
||||
ThemeBadRequest {
|
||||
reason: String,
|
||||
},
|
||||
/// 共创主题:同 `Idempotency-Key` 不同请求摘要。
|
||||
///
|
||||
/// 与通用的「幂等键对应的请求摘要不一致」是同一条判定,只是换成主题自己的码:客户端可以按码
|
||||
/// 分支,而不是从 409 的文案里猜这是哪一类冲突。
|
||||
ThemeIdempotencyConflict,
|
||||
/// 共创主题:目标作品有血缘行(不是根作品),不能作为成员。
|
||||
ThemeMemberNotRoot {
|
||||
game_id: String,
|
||||
},
|
||||
/// 共创主题:作为成员的目标作品不存在。
|
||||
ThemeMemberGameNotFound {
|
||||
game_id: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl fmt::Display for GameDistributionError {
|
||||
@@ -129,6 +154,23 @@ impl fmt::Display for GameDistributionError {
|
||||
.write_str("FORK_SOURCE_VERSION_MISMATCH: 改编来源版本不是该作品的当前公开版本"),
|
||||
Self::ForkDeclarationOnExistingGame => formatter
|
||||
.write_str("FORK_DECLARATION_ON_EXISTING_GAME: 已存在的作品不能再声明改编来源"),
|
||||
// 共创主题同理:文案以错误码开头,HTTP 层按码映射(不做子串猜测)。
|
||||
Self::ThemeNotFound => formatter.write_str("THEME_NOT_FOUND: 主题不存在"),
|
||||
Self::ThemeBadRequest { reason } => {
|
||||
write!(formatter, "THEME_BAD_REQUEST: {reason}")
|
||||
}
|
||||
Self::ThemeIdempotencyConflict => formatter
|
||||
.write_str("THEME_IDEMPOTENCY_CONFLICT: 同一个 Idempotency-Key 对应了不同的请求"),
|
||||
Self::ThemeMemberNotRoot { game_id } => write!(
|
||||
formatter,
|
||||
"THEME_MEMBER_NOT_ROOT: 作品 {game_id} 有血缘行(不是根作品),不能作为主题成员"
|
||||
),
|
||||
Self::ThemeMemberGameNotFound { game_id } => {
|
||||
write!(
|
||||
formatter,
|
||||
"THEME_MEMBER_GAME_NOT_FOUND: 作品 {game_id} 不存在"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,13 +59,16 @@ pub use release::{
|
||||
normalize_release_asset_path, release_asset_content_type,
|
||||
};
|
||||
pub use theme::{
|
||||
GAME_DISTRIBUTION_THEME_PAGE_LIMIT_DEFAULT, GAME_DISTRIBUTION_THEME_PAGE_LIMIT_MAX,
|
||||
GAME_DISTRIBUTION_THEME_STATUS_ARCHIVED, GAME_DISTRIBUTION_THEME_STATUS_DRAFT,
|
||||
GAME_DISTRIBUTION_THEME_STATUS_PUBLISHED, GAME_DISTRIBUTION_THEME_STATUSES,
|
||||
GAME_DISTRIBUTION_THEME_ADMIN_STATUS_ALL, GAME_DISTRIBUTION_THEME_BADGE_MAX_CHARS,
|
||||
GAME_DISTRIBUTION_THEME_NAME_MAX_CHARS, GAME_DISTRIBUTION_THEME_PAGE_LIMIT_DEFAULT,
|
||||
GAME_DISTRIBUTION_THEME_PAGE_LIMIT_MAX, GAME_DISTRIBUTION_THEME_STATUS_ARCHIVED,
|
||||
GAME_DISTRIBUTION_THEME_STATUS_DRAFT, GAME_DISTRIBUTION_THEME_STATUS_PUBLISHED,
|
||||
GAME_DISTRIBUTION_THEME_STATUSES, GAME_DISTRIBUTION_THEME_SUMMARY_MAX_CHARS,
|
||||
GameDistributionThemeMemberPageItem, GameDistributionThemePageItem,
|
||||
encode_game_distribution_theme_cursor, game_distribution_theme_member_id,
|
||||
game_distribution_theme_member_visible, game_distribution_theme_page_limit,
|
||||
game_distribution_theme_public_visible, game_distribution_theme_root_acceptable,
|
||||
game_distribution_theme_status_valid, page_public_themes, page_theme_members,
|
||||
encode_game_distribution_theme_cursor, game_distribution_theme_admin_status_filter_valid,
|
||||
game_distribution_theme_member_id, game_distribution_theme_member_visible,
|
||||
game_distribution_theme_page_limit, game_distribution_theme_public_visible,
|
||||
game_distribution_theme_root_acceptable, game_distribution_theme_status_valid,
|
||||
game_distribution_theme_text_violation, page_public_themes, page_theme_members,
|
||||
parse_game_distribution_theme_cursor, sort_public_themes, sort_theme_members,
|
||||
};
|
||||
|
||||
@@ -32,6 +32,71 @@ pub fn game_distribution_theme_status_valid(status: &str) -> bool {
|
||||
GAME_DISTRIBUTION_THEME_STATUSES.contains(&status)
|
||||
}
|
||||
|
||||
/// 后台列表 `status` 过滤里代表「全量」的取值。
|
||||
///
|
||||
/// 与 `GAME_DISTRIBUTION_THEME_STATUSES` 并列而不是塞进白名单:`all` 是**过滤视图**的取值,
|
||||
/// 不是主题行上的合法状态(没有任何一行的 `status` 会是 `all`);混进去会让
|
||||
/// [`game_distribution_theme_status_valid`] 反过来接受一个落不进表的字符串。
|
||||
pub const GAME_DISTRIBUTION_THEME_ADMIN_STATUS_ALL: &str = "all";
|
||||
|
||||
/// 后台列表 `status` 过滤值是否合法:`all` + 三个真实状态。
|
||||
///
|
||||
/// 单一实现,避免「api-server 放行、事务里当全量」这类两侧各写一遍白名单的漂移;非法值由
|
||||
/// api-server 映射 400(`THEME_BAD_REQUEST`)。
|
||||
pub fn game_distribution_theme_admin_status_filter_valid(status: &str) -> bool {
|
||||
status == GAME_DISTRIBUTION_THEME_ADMIN_STATUS_ALL
|
||||
|| game_distribution_theme_status_valid(status)
|
||||
}
|
||||
|
||||
/// 主题名长度上限(字符数,不是字节数)。
|
||||
///
|
||||
/// 设计文档(§3.10.8 / 里程碑 E)只写了「`name` 必填非空」,没有给上限。这里取 40:主题名与
|
||||
/// 作品标题(`title` 40)处在同一层级——都是卡片上一行标题——沿用同一量级可以让「一屏能放下
|
||||
/// 什么」在两条列表里一致;再长会被前端截断,运营写了也白写。
|
||||
pub const GAME_DISTRIBUTION_THEME_NAME_MAX_CHARS: usize = 40;
|
||||
|
||||
/// 主题简介长度上限(字符数)。
|
||||
///
|
||||
/// 设计文档没写上限。取 200:主题简介是主题页头部的一段引导文案,比作品简介(120)略长
|
||||
/// (主题是运营叙事,需要多说一句「这一组作品为什么放在一起」),又远小于作品详细介绍
|
||||
/// (2000)——它不是正文,多出的字数只会把成员清单挤出首屏。
|
||||
pub const GAME_DISTRIBUTION_THEME_SUMMARY_MAX_CHARS: usize = 200;
|
||||
|
||||
/// 主题角标长度上限(字符数)。
|
||||
///
|
||||
/// 设计文档没写上限。取 16:角标是卡片角上的短标签(「精选」「编辑推荐」「新春」),16 个字符
|
||||
/// 足够表达一个标签,同时短到不可能被当成第二段正文——真要写长文案的位置是 `summary`。
|
||||
pub const GAME_DISTRIBUTION_THEME_BADGE_MAX_CHARS: usize = 16;
|
||||
|
||||
/// 后台主题文本字段的校验:合法返回 `None`,否则返回可直接拼进 `THEME_BAD_REQUEST:` 的原因。
|
||||
///
|
||||
/// 规则本身(长度上限、空名)只写在这里一处:api-server 与事务都调它,因此「HTTP 挡住的」
|
||||
/// 与「落库挡住的」永远是同一套判据。三个字段都先 `trim` 再判定:一串空格与空名同罪,调用方
|
||||
/// 忘掉 trim 也不会把「只有空白的主题名」写进库(长度也按 trim 后的字符数算,尾部空格不占额度)。
|
||||
pub fn game_distribution_theme_text_violation(
|
||||
name: &str,
|
||||
summary: &str,
|
||||
badge: &str,
|
||||
) -> Option<&'static str> {
|
||||
let name = name.trim();
|
||||
let summary = summary.trim();
|
||||
let badge = badge.trim();
|
||||
let name_chars = name.chars().count();
|
||||
if name_chars == 0 {
|
||||
return Some("主题名不能为空");
|
||||
}
|
||||
if name_chars > GAME_DISTRIBUTION_THEME_NAME_MAX_CHARS {
|
||||
return Some("主题名不能超过 40 个字符");
|
||||
}
|
||||
if summary.chars().count() > GAME_DISTRIBUTION_THEME_SUMMARY_MAX_CHARS {
|
||||
return Some("主题简介不能超过 200 个字符");
|
||||
}
|
||||
if badge.chars().count() > GAME_DISTRIBUTION_THEME_BADGE_MAX_CHARS {
|
||||
return Some("主题角标不能超过 16 个字符");
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// 主题是否应出现在公开投影(公开列表 / 公开详情 / 作品详情的 `themes`)里。
|
||||
///
|
||||
/// 等价于 `status == "published"`:`draft` 与 `archived` 一律不可见(后台仍可见全量)。与
|
||||
@@ -347,6 +412,88 @@ mod tests {
|
||||
assert!(!game_distribution_theme_root_acceptable(true));
|
||||
}
|
||||
|
||||
/// 后台过滤白名单 = `all` + 三个真实状态;`all` 自己**不是**能落进主题行的状态。
|
||||
#[test]
|
||||
fn admin_status_filter_accepts_all_plus_real_statuses_only() {
|
||||
assert!(game_distribution_theme_admin_status_filter_valid(
|
||||
GAME_DISTRIBUTION_THEME_ADMIN_STATUS_ALL
|
||||
));
|
||||
for status in GAME_DISTRIBUTION_THEME_STATUSES {
|
||||
assert!(
|
||||
game_distribution_theme_admin_status_filter_valid(status),
|
||||
"{status}"
|
||||
);
|
||||
}
|
||||
for status in ["", "ALL", "deleted", "published ", " archived"] {
|
||||
assert!(
|
||||
!game_distribution_theme_admin_status_filter_valid(status),
|
||||
"{status}"
|
||||
);
|
||||
}
|
||||
// 两条白名单刻意分开:`all` 只服务过滤视图,混进状态白名单会让它被当成可落库的值。
|
||||
assert!(!game_distribution_theme_status_valid(
|
||||
GAME_DISTRIBUTION_THEME_ADMIN_STATUS_ALL
|
||||
));
|
||||
}
|
||||
|
||||
/// 后台主题文本校验:空名 / 超长各有明确原因;边界取「刚好合法」,且按**字符**而不是字节计数。
|
||||
#[test]
|
||||
fn theme_text_validation_pins_limits_at_the_boundary() {
|
||||
assert_eq!(
|
||||
game_distribution_theme_text_violation("", "", ""),
|
||||
Some("主题名不能为空")
|
||||
);
|
||||
assert_eq!(
|
||||
game_distribution_theme_text_violation(" ", "", ""),
|
||||
Some("主题名不能为空"),
|
||||
"调用方传 trim 后的值:一串空格与空名同罪"
|
||||
);
|
||||
|
||||
// 40 个中文字符 = 120 字节:按字节计数的实现会在这里红。
|
||||
let max_name = "主".repeat(GAME_DISTRIBUTION_THEME_NAME_MAX_CHARS);
|
||||
assert_eq!(
|
||||
game_distribution_theme_text_violation(&max_name, "", ""),
|
||||
None
|
||||
);
|
||||
let too_long_name = "主".repeat(GAME_DISTRIBUTION_THEME_NAME_MAX_CHARS + 1);
|
||||
assert_eq!(
|
||||
game_distribution_theme_text_violation(&too_long_name, "", ""),
|
||||
Some("主题名不能超过 40 个字符")
|
||||
);
|
||||
|
||||
let max_summary = "简".repeat(GAME_DISTRIBUTION_THEME_SUMMARY_MAX_CHARS);
|
||||
assert_eq!(
|
||||
game_distribution_theme_text_violation("主题", &max_summary, ""),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
game_distribution_theme_text_violation("主题", &format!("{max_summary}简"), ""),
|
||||
Some("主题简介不能超过 200 个字符")
|
||||
);
|
||||
|
||||
let max_badge = "标".repeat(GAME_DISTRIBUTION_THEME_BADGE_MAX_CHARS);
|
||||
assert_eq!(
|
||||
game_distribution_theme_text_violation("主题", "", &max_badge),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
game_distribution_theme_text_violation("主题", "", &format!("{max_badge}标")),
|
||||
Some("主题角标不能超过 16 个字符")
|
||||
);
|
||||
|
||||
// 三个上限各自独立:简介/角标超长不会因为名字合法而被放过。
|
||||
assert_eq!(
|
||||
game_distribution_theme_text_violation("主题", &max_summary, &format!("{max_badge}标")),
|
||||
Some("主题角标不能超过 16 个字符")
|
||||
);
|
||||
|
||||
// 长度按 trim 后的字符数算:40 个字 + 首尾空格仍然合法(空格不占额度)。
|
||||
assert_eq!(
|
||||
game_distribution_theme_text_violation(&format!(" {max_name} "), "", ""),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
/// `limit` 语义:`0` 取默认 20(不是「返回 0 条」),超界截断到 50(不是报错)。
|
||||
#[test]
|
||||
fn page_limit_defaults_zero_and_truncates_overflow() {
|
||||
|
||||
@@ -23,10 +23,11 @@ mod error_reports;
|
||||
pub mod external_api_key;
|
||||
pub mod game_distribution;
|
||||
pub use game_distribution::{
|
||||
GameDistributionAdminGameListRecordInput, GameDistributionAdminUserReviewListRecordInput,
|
||||
GameDistributionApproveRecordInput, GameDistributionCancelVersionRecordInput,
|
||||
GameDistributionCollectGameRecordInput, GameDistributionConfirmPackageRecordInput,
|
||||
GameDistributionConfirmProjectBundleRecordInput, GameDistributionCreateGameRecordInput,
|
||||
GameDistributionAdminGameListRecordInput, GameDistributionAdminThemeListRecordInput,
|
||||
GameDistributionAdminUserReviewListRecordInput, GameDistributionApproveRecordInput,
|
||||
GameDistributionCancelVersionRecordInput, GameDistributionCollectGameRecordInput,
|
||||
GameDistributionConfirmPackageRecordInput, GameDistributionConfirmProjectBundleRecordInput,
|
||||
GameDistributionCreateGameRecordInput, GameDistributionCreateThemeRecordInput,
|
||||
GameDistributionCreateVersionRecordInput, GameDistributionDeleteGameRecordInput,
|
||||
GameDistributionFailUploadRecordInput, GameDistributionGetGameRecordInput,
|
||||
GameDistributionOwnerGameListRecordInput, GameDistributionPlayCountIncrementRecordInput,
|
||||
@@ -34,8 +35,9 @@ pub use game_distribution::{
|
||||
GameDistributionRestoreRecordInput, GameDistributionReviewGameListRecordInput,
|
||||
GameDistributionReviewModerationRecordInput, GameDistributionSetForkAuthorizationRecordInput,
|
||||
GameDistributionSubmitReviewRecordInput, GameDistributionSuspendRecordInput,
|
||||
GameDistributionThemeMemberRemoveRecordInput, GameDistributionThemeMemberUpsertRecordInput,
|
||||
GameDistributionUncollectGameRecordInput, GameDistributionUnpublishRecordInput,
|
||||
GameDistributionUpdateMetadataRecordInput,
|
||||
GameDistributionUpdateMetadataRecordInput, GameDistributionUpdateThemeRecordInput,
|
||||
};
|
||||
#[path = "active/external_generation.rs"]
|
||||
pub mod external_generation;
|
||||
|
||||
@@ -96,11 +96,13 @@ pub use self::external_generation::{
|
||||
ExternalGenerationQueueStatsRecord,
|
||||
};
|
||||
pub use self::game_distribution::{
|
||||
GameDistributionAdminGameRecord, GameDistributionAdminUserReviewDetailRecord,
|
||||
GameDistributionAdminUserReviewListRecord, GameDistributionAdminUserReviewRecord,
|
||||
GameDistributionAdminVersionRecord, GameDistributionCollectionMutationRecord,
|
||||
GameDistributionDerivedGamesRecord, GameDistributionForkSourceRecord,
|
||||
GameDistributionGameRecord, GameDistributionLineageNodeRecord, GameDistributionLineageRecord,
|
||||
GameDistributionAdminGameRecord, GameDistributionAdminThemeMemberRecord,
|
||||
GameDistributionAdminThemeMutationRecord, GameDistributionAdminThemeRecord,
|
||||
GameDistributionAdminUserReviewDetailRecord, GameDistributionAdminUserReviewListRecord,
|
||||
GameDistributionAdminUserReviewRecord, GameDistributionAdminVersionRecord,
|
||||
GameDistributionCollectionMutationRecord, GameDistributionDerivedGamesRecord,
|
||||
GameDistributionForkSourceRecord, GameDistributionGameRecord,
|
||||
GameDistributionLineageNodeRecord, GameDistributionLineageRecord,
|
||||
GameDistributionLineageTreeRecord, GameDistributionOwnerGameRecord,
|
||||
GameDistributionPublicGameRecord, GameDistributionRatingSummaryRecord,
|
||||
GameDistributionReviewGameListRecord, GameDistributionReviewGameRecord,
|
||||
@@ -173,8 +175,11 @@ pub(crate) use self::external_generation::{
|
||||
};
|
||||
pub(crate) use self::game_distribution::{
|
||||
ensure_admin_user_review_result, ensure_user_review_result, map_admin_user_review,
|
||||
map_game_distribution_admin_game_list_result, map_game_distribution_collection_list_result,
|
||||
map_game_distribution_collection_mutation_result,
|
||||
map_game_distribution_admin_game_list_result, map_game_distribution_admin_theme_list_result,
|
||||
map_game_distribution_admin_theme_member_remove_result,
|
||||
map_game_distribution_admin_theme_member_upsert_result,
|
||||
map_game_distribution_admin_theme_mutation_result,
|
||||
map_game_distribution_collection_list_result, map_game_distribution_collection_mutation_result,
|
||||
map_game_distribution_collection_state_result, map_game_distribution_derived_games_result,
|
||||
map_game_distribution_fork_source_result, map_game_distribution_game_result,
|
||||
map_game_distribution_lineage_result, map_game_distribution_owner_game_list_result,
|
||||
|
||||
@@ -385,6 +385,40 @@ pub struct GameDistributionThemeDetailRecord {
|
||||
pub roots: Vec<GameDistributionPublicGameRecord>,
|
||||
}
|
||||
|
||||
/// 后台主题行记录:公开投影看不到的列(运营排序、状态、时间戳)与**成员行总数**都在这里。
|
||||
///
|
||||
/// `member_count` 与公开记录的 `member_count` 语义**刻意不同**:这里是成员行总数(含当前对外
|
||||
/// 不可见的成员),公开侧是当前可见成员数。后台没有「不该被探测」的顾虑,运营恰恰需要知道
|
||||
/// 自己挂了几行;公开侧报总数则会泄露「存在草稿 / 被下架成员」。
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct GameDistributionAdminThemeRecord {
|
||||
pub theme_id: String,
|
||||
pub name: String,
|
||||
pub summary: String,
|
||||
pub badge: String,
|
||||
pub sort_order: i64,
|
||||
pub status: String,
|
||||
pub member_count: u64,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
/// 后台创建 / 更新主题后的权威投影:`replayed` 为 true 表示命中同键幂等收据(本次没有写库)。
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct GameDistributionAdminThemeMutationRecord {
|
||||
pub theme: GameDistributionAdminThemeRecord,
|
||||
pub replayed: bool,
|
||||
}
|
||||
|
||||
/// 后台主题成员行记录(PUT 落成的那一行)。
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct GameDistributionAdminThemeMemberRecord {
|
||||
pub theme_id: String,
|
||||
pub root_game_id: String,
|
||||
pub sort_order: i64,
|
||||
pub created_at: String,
|
||||
}
|
||||
|
||||
fn map_game(
|
||||
value: crate::module_bindings::GameDistributionGameSnapshot,
|
||||
) -> GameDistributionGameRecord {
|
||||
@@ -697,6 +731,82 @@ pub(crate) fn map_game_distribution_theme_refs_result(
|
||||
Ok(result.themes.into_iter().map(map_theme_reference).collect())
|
||||
}
|
||||
|
||||
/// 后台主题行(列表条目与写接口返回值共用同一个形状)。
|
||||
fn map_admin_theme(
|
||||
value: crate::module_bindings::GameDistributionAdminThemeSnapshot,
|
||||
) -> GameDistributionAdminThemeRecord {
|
||||
GameDistributionAdminThemeRecord {
|
||||
theme_id: value.theme_id,
|
||||
name: value.name,
|
||||
summary: value.summary,
|
||||
badge: value.badge,
|
||||
sort_order: value.sort_order,
|
||||
status: value.status,
|
||||
member_count: value.member_count,
|
||||
created_at: shared_kernel::format_timestamp_micros(value.created_at_micros),
|
||||
updated_at: shared_kernel::format_timestamp_micros(value.updated_at_micros),
|
||||
}
|
||||
}
|
||||
|
||||
/// 后台创建 / 更新主题:`ok == false` 走既有错误映射(400 / 404 / 409)。
|
||||
///
|
||||
/// `ok == true` 却缺主题行属于服务端契约违背:**不能**降级成「成功但没有内容」——那会让 handler
|
||||
/// 把一个空壳当正常主题渲染;这里直接折成错误,让问题在第一次调用就暴露。
|
||||
pub(crate) fn map_game_distribution_admin_theme_mutation_result(
|
||||
result: crate::module_bindings::GameDistributionAdminThemeMutationResult,
|
||||
) -> Result<GameDistributionAdminThemeMutationRecord, SpacetimeClientError> {
|
||||
if !result.ok {
|
||||
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
||||
}
|
||||
let theme = result.theme.ok_or_else(|| {
|
||||
SpacetimeClientError::procedure_failed(Some("主题写接口未回传主题行".to_string()))
|
||||
})?;
|
||||
Ok(GameDistributionAdminThemeMutationRecord {
|
||||
theme: map_admin_theme(theme),
|
||||
replayed: result.replayed,
|
||||
})
|
||||
}
|
||||
|
||||
/// 后台增 / 改成员:`ok == false` 走既有错误映射;成功时必须有成员行(PUT 的语义就是「落成一行」)。
|
||||
pub(crate) fn map_game_distribution_admin_theme_member_upsert_result(
|
||||
result: crate::module_bindings::GameDistributionAdminThemeMemberMutationResult,
|
||||
) -> Result<GameDistributionAdminThemeMemberRecord, SpacetimeClientError> {
|
||||
if !result.ok {
|
||||
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
||||
}
|
||||
let member = result.member.ok_or_else(|| {
|
||||
SpacetimeClientError::procedure_failed(Some("主题成员写接口未回传成员行".to_string()))
|
||||
})?;
|
||||
Ok(GameDistributionAdminThemeMemberRecord {
|
||||
theme_id: member.theme_id,
|
||||
root_game_id: member.root_game_id,
|
||||
sort_order: member.sort_order,
|
||||
created_at: shared_kernel::format_timestamp_micros(member.created_at_micros),
|
||||
})
|
||||
}
|
||||
|
||||
/// 后台移除成员:成功时**没有**成员行(行已经不在库里),因此这里只关心 `ok`。
|
||||
///
|
||||
/// 刻意不回传「之前存不存在」:成员不存在也算成功,重复 DELETE 的响应因此逐字节相同。
|
||||
pub(crate) fn map_game_distribution_admin_theme_member_remove_result(
|
||||
result: crate::module_bindings::GameDistributionAdminThemeMemberMutationResult,
|
||||
) -> Result<(), SpacetimeClientError> {
|
||||
if !result.ok {
|
||||
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 后台主题列表:含 `draft` / `archived`(可见性过滤只发生在公开路径上)。
|
||||
pub(crate) fn map_game_distribution_admin_theme_list_result(
|
||||
result: crate::module_bindings::GameDistributionAdminThemeListResult,
|
||||
) -> Result<Vec<GameDistributionAdminThemeRecord>, SpacetimeClientError> {
|
||||
if !result.ok {
|
||||
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
||||
}
|
||||
Ok(result.themes.into_iter().map(map_admin_theme).collect())
|
||||
}
|
||||
|
||||
pub(crate) fn map_game_distribution_admin_game_list_result(
|
||||
result: crate::module_bindings::GameDistributionAdminGameListResult,
|
||||
) -> Result<(Vec<GameDistributionAdminGameRecord>, Option<String>), SpacetimeClientError> {
|
||||
@@ -915,3 +1025,143 @@ mod theme_result_tests {
|
||||
assert_eq!(list_error.to_string(), "主题列表游标格式无效");
|
||||
}
|
||||
}
|
||||
|
||||
/// 后台主题写接口结果的映射。
|
||||
///
|
||||
/// 三条纪律:① `ok = false` 必须折成 `Procedure` 错误(api-server 才能按码映射 400 / 404 / 409);
|
||||
/// ② `ok = true` 却没有载荷是**契约违背**,直接失败,不能降级成「成功但没内容」;
|
||||
/// ③ 时间戳在这里由微秒折成 `seconds.microsZ` 文本(与后台作品行同一口径)。
|
||||
#[cfg(test)]
|
||||
mod admin_theme_result_tests {
|
||||
use super::*;
|
||||
|
||||
const CREATED_MICROS: i64 = 1_700_000_000_123_456;
|
||||
const UPDATED_MICROS: i64 = 1_700_000_100_654_321;
|
||||
|
||||
fn admin_theme_snapshot() -> crate::module_bindings::GameDistributionAdminThemeSnapshot {
|
||||
crate::module_bindings::GameDistributionAdminThemeSnapshot {
|
||||
theme_id: "theme_a".to_string(),
|
||||
name: "共创主题".to_string(),
|
||||
summary: "运营简介".to_string(),
|
||||
badge: "精选".to_string(),
|
||||
sort_order: 7,
|
||||
status: "draft".to_string(),
|
||||
member_count: 3,
|
||||
created_at_micros: CREATED_MICROS,
|
||||
updated_at_micros: UPDATED_MICROS,
|
||||
}
|
||||
}
|
||||
|
||||
fn mutation_result(
|
||||
ok: bool,
|
||||
theme: Option<crate::module_bindings::GameDistributionAdminThemeSnapshot>,
|
||||
) -> crate::module_bindings::GameDistributionAdminThemeMutationResult {
|
||||
crate::module_bindings::GameDistributionAdminThemeMutationResult {
|
||||
ok,
|
||||
replayed: false,
|
||||
theme,
|
||||
error_message: (!ok).then(|| "THEME_NOT_FOUND: 主题不存在".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn member_result(
|
||||
ok: bool,
|
||||
member: Option<crate::module_bindings::GameDistributionAdminThemeMemberSnapshot>,
|
||||
) -> crate::module_bindings::GameDistributionAdminThemeMemberMutationResult {
|
||||
crate::module_bindings::GameDistributionAdminThemeMemberMutationResult {
|
||||
ok,
|
||||
member,
|
||||
error_message: (!ok).then(|| "THEME_MEMBER_NOT_ROOT: 不是根作品".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_theme_mutation_keeps_row_status_and_iso_timestamps() {
|
||||
let mut result = mutation_result(true, Some(admin_theme_snapshot()));
|
||||
result.replayed = true;
|
||||
let record = map_game_distribution_admin_theme_mutation_result(result).expect("成功写结果");
|
||||
assert!(record.replayed, "重放必须原样透传");
|
||||
assert_eq!(record.theme.theme_id, "theme_a");
|
||||
// 后台行**必须**保留 draft / archived(公开投影那条路径才过滤可见性)。
|
||||
assert_eq!(record.theme.status, "draft");
|
||||
assert_eq!(record.theme.sort_order, 7);
|
||||
assert_eq!(record.theme.member_count, 3);
|
||||
assert_eq!(record.theme.created_at, "1700000000.123456Z");
|
||||
assert_eq!(record.theme.updated_at, "1700000100.654321Z");
|
||||
|
||||
let error = map_game_distribution_admin_theme_mutation_result(mutation_result(false, None))
|
||||
.expect_err("失败必须是错误");
|
||||
assert_eq!(error.to_string(), "THEME_NOT_FOUND: 主题不存在");
|
||||
|
||||
// `ok = true` 却没有行是契约违背:不能降级成「成功但没有内容」。
|
||||
let missing =
|
||||
map_game_distribution_admin_theme_mutation_result(mutation_result(true, None))
|
||||
.expect_err("缺载荷必须失败关闭");
|
||||
assert!(missing.to_string().contains("未回传主题行"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_theme_member_mutation_separates_upsert_from_idempotent_remove() {
|
||||
let member = crate::module_bindings::GameDistributionAdminThemeMemberSnapshot {
|
||||
theme_id: "theme_a".to_string(),
|
||||
root_game_id: "game_root".to_string(),
|
||||
sort_order: 2,
|
||||
created_at_micros: CREATED_MICROS,
|
||||
};
|
||||
let record = map_game_distribution_admin_theme_member_upsert_result(member_result(
|
||||
true,
|
||||
Some(member),
|
||||
))
|
||||
.expect("PUT 成功");
|
||||
assert_eq!(record.root_game_id, "game_root");
|
||||
assert_eq!(record.sort_order, 2);
|
||||
assert_eq!(record.created_at, "1700000000.123456Z");
|
||||
|
||||
let upsert_error =
|
||||
map_game_distribution_admin_theme_member_upsert_result(member_result(false, None))
|
||||
.expect_err("非根作品必须是错误");
|
||||
assert_eq!(
|
||||
upsert_error.to_string(),
|
||||
"THEME_MEMBER_NOT_ROOT: 不是根作品"
|
||||
);
|
||||
let upsert_missing =
|
||||
map_game_distribution_admin_theme_member_upsert_result(member_result(true, None))
|
||||
.expect_err("PUT 成功却没有成员行是契约违背");
|
||||
assert!(upsert_missing.to_string().contains("未回传成员行"));
|
||||
|
||||
// DELETE 成功时**本来就没有**成员行(行已经不在库里):这不是契约违背。
|
||||
map_game_distribution_admin_theme_member_remove_result(member_result(true, None))
|
||||
.expect("成员不存在也算成功(幂等删除)");
|
||||
let remove_error =
|
||||
map_game_distribution_admin_theme_member_remove_result(member_result(false, None))
|
||||
.expect_err("主题不存在仍必须是错误");
|
||||
assert_eq!(
|
||||
remove_error.to_string(),
|
||||
"THEME_MEMBER_NOT_ROOT: 不是根作品"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_theme_list_maps_drafts_and_fails_closed() {
|
||||
let themes = map_game_distribution_admin_theme_list_result(
|
||||
crate::module_bindings::GameDistributionAdminThemeListResult {
|
||||
ok: true,
|
||||
themes: vec![admin_theme_snapshot()],
|
||||
error_message: None,
|
||||
},
|
||||
)
|
||||
.expect("列表结果");
|
||||
assert_eq!(themes.len(), 1);
|
||||
assert_eq!(themes[0].status, "draft");
|
||||
|
||||
let error = map_game_distribution_admin_theme_list_result(
|
||||
crate::module_bindings::GameDistributionAdminThemeListResult {
|
||||
ok: false,
|
||||
themes: Vec::new(),
|
||||
error_message: Some("THEME_BAD_REQUEST: 未知的主题状态过滤值 hidden".to_string()),
|
||||
},
|
||||
)
|
||||
.expect_err("非法过滤值必须是错误(api-server 才能映射 400)");
|
||||
assert!(error.to_string().contains("THEME_BAD_REQUEST"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,6 +293,60 @@ pub struct GameDistributionUncollectGameRecordInput {
|
||||
pub user_id: String,
|
||||
}
|
||||
|
||||
/// 后台创建共创主题。`theme_id` 由 api-server 生成(`theme-` + 随机后缀):请求体里没有这个字段,
|
||||
/// 因此外部调用方无法指定主题 ID,而幂等收据仍能记住「首次创建的是哪一行」。
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct GameDistributionCreateThemeRecordInput {
|
||||
pub theme_id: String,
|
||||
pub name: String,
|
||||
pub summary: String,
|
||||
pub badge: String,
|
||||
pub sort_order: i64,
|
||||
pub status: String,
|
||||
pub admin_user_id: String,
|
||||
pub idempotency_key: String,
|
||||
pub request_digest: String,
|
||||
pub now_micros: i64,
|
||||
}
|
||||
|
||||
/// 后台整体覆盖主题字段(改名 / 简介 / 角标 / 排序 / 状态)。
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct GameDistributionUpdateThemeRecordInput {
|
||||
pub theme_id: String,
|
||||
pub name: String,
|
||||
pub summary: String,
|
||||
pub badge: String,
|
||||
pub sort_order: i64,
|
||||
pub status: String,
|
||||
pub admin_user_id: String,
|
||||
pub idempotency_key: String,
|
||||
pub request_digest: String,
|
||||
pub now_micros: i64,
|
||||
}
|
||||
|
||||
/// 后台增 / 改主题成员;成员身份由 `(theme_id, root_game_id)` 决定,因此不需要幂等键。
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct GameDistributionThemeMemberUpsertRecordInput {
|
||||
pub theme_id: String,
|
||||
pub root_game_id: String,
|
||||
pub sort_order: i64,
|
||||
pub now_micros: i64,
|
||||
}
|
||||
|
||||
/// 后台移除主题成员;按确定性主键删除,成员不存在也算成功,因此不需要幂等键。
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct GameDistributionThemeMemberRemoveRecordInput {
|
||||
pub theme_id: String,
|
||||
pub root_game_id: String,
|
||||
}
|
||||
|
||||
/// 后台主题列表:`status` 为 `all` / `draft` / `published` / `archived`(缺省 = 全量),无游标。
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct GameDistributionAdminThemeListRecordInput {
|
||||
pub status: Option<String>,
|
||||
pub limit: u32,
|
||||
}
|
||||
|
||||
impl SpacetimeClient {
|
||||
pub async fn list_admin_game_distribution_user_reviews(
|
||||
&self,
|
||||
@@ -921,6 +975,163 @@ impl SpacetimeClient {
|
||||
.await
|
||||
}
|
||||
|
||||
/// 后台创建共创主题(`POST /admin/api/game-distribution/themes`)。
|
||||
///
|
||||
/// `theme_id` 由 api-server 生成后传入(请求体里没有这个字段);命中同键幂等收据时
|
||||
/// `replayed = true`,且回的是**库里首次创建的那一行**。
|
||||
pub async fn create_game_distribution_theme(
|
||||
&self,
|
||||
input: GameDistributionCreateThemeRecordInput,
|
||||
) -> Result<GameDistributionAdminThemeMutationRecord, SpacetimeClientError> {
|
||||
let input = crate::module_bindings::GameDistributionCreateThemeInput {
|
||||
theme_id: input.theme_id,
|
||||
name: input.name,
|
||||
summary: input.summary,
|
||||
badge: input.badge,
|
||||
sort_order: input.sort_order,
|
||||
status: input.status,
|
||||
admin_user_id: input.admin_user_id,
|
||||
idempotency_key: input.idempotency_key,
|
||||
request_digest: input.request_digest,
|
||||
now_micros: input.now_micros,
|
||||
};
|
||||
self.call_after_connect(
|
||||
"create_game_distribution_theme",
|
||||
move |connection, sender| {
|
||||
connection
|
||||
.procedures()
|
||||
.create_game_distribution_theme_and_return_then(input, move |_, result| {
|
||||
let mapped = result
|
||||
.map_err(SpacetimeClientError::from_sdk_error)
|
||||
.and_then(map_game_distribution_admin_theme_mutation_result);
|
||||
send_once(&sender, mapped);
|
||||
});
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// 后台整体覆盖主题字段(`PUT /admin/api/game-distribution/themes/{theme_id}`)。
|
||||
///
|
||||
/// `theme_id` 不存在由事务回带码错误(api-server 映射 404);命中幂等收据的重放不写库。
|
||||
pub async fn update_game_distribution_theme(
|
||||
&self,
|
||||
input: GameDistributionUpdateThemeRecordInput,
|
||||
) -> Result<GameDistributionAdminThemeMutationRecord, SpacetimeClientError> {
|
||||
let input = crate::module_bindings::GameDistributionUpdateThemeInput {
|
||||
theme_id: input.theme_id,
|
||||
name: input.name,
|
||||
summary: input.summary,
|
||||
badge: input.badge,
|
||||
sort_order: input.sort_order,
|
||||
status: input.status,
|
||||
admin_user_id: input.admin_user_id,
|
||||
idempotency_key: input.idempotency_key,
|
||||
request_digest: input.request_digest,
|
||||
now_micros: input.now_micros,
|
||||
};
|
||||
self.call_after_connect(
|
||||
"update_game_distribution_theme",
|
||||
move |connection, sender| {
|
||||
connection
|
||||
.procedures()
|
||||
.update_game_distribution_theme_and_return_then(input, move |_, result| {
|
||||
let mapped = result
|
||||
.map_err(SpacetimeClientError::from_sdk_error)
|
||||
.and_then(map_game_distribution_admin_theme_mutation_result);
|
||||
send_once(&sender, mapped);
|
||||
});
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// 后台增 / 改主题成员(`PUT …/members/{root_game_id}`);幂等由确定性主键保证。
|
||||
pub async fn upsert_game_distribution_theme_member(
|
||||
&self,
|
||||
input: GameDistributionThemeMemberUpsertRecordInput,
|
||||
) -> Result<GameDistributionAdminThemeMemberRecord, SpacetimeClientError> {
|
||||
let input = crate::module_bindings::GameDistributionThemeMemberUpsertInput {
|
||||
theme_id: input.theme_id,
|
||||
root_game_id: input.root_game_id,
|
||||
sort_order: input.sort_order,
|
||||
now_micros: input.now_micros,
|
||||
};
|
||||
self.call_after_connect(
|
||||
"upsert_game_distribution_theme_member",
|
||||
move |connection, sender| {
|
||||
connection
|
||||
.procedures()
|
||||
.upsert_game_distribution_theme_member_and_return_then(
|
||||
input,
|
||||
move |_, result| {
|
||||
let mapped = result
|
||||
.map_err(SpacetimeClientError::from_sdk_error)
|
||||
.and_then(map_game_distribution_admin_theme_member_upsert_result);
|
||||
send_once(&sender, mapped);
|
||||
},
|
||||
);
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// 后台移除主题成员(`DELETE …/members/{root_game_id}`);成员不存在也算成功(幂等)。
|
||||
pub async fn remove_game_distribution_theme_member(
|
||||
&self,
|
||||
input: GameDistributionThemeMemberRemoveRecordInput,
|
||||
) -> Result<(), SpacetimeClientError> {
|
||||
let input = crate::module_bindings::GameDistributionThemeMemberRemoveInput {
|
||||
theme_id: input.theme_id,
|
||||
root_game_id: input.root_game_id,
|
||||
};
|
||||
self.call_after_connect(
|
||||
"remove_game_distribution_theme_member",
|
||||
move |connection, sender| {
|
||||
connection
|
||||
.procedures()
|
||||
.remove_game_distribution_theme_member_and_return_then(
|
||||
input,
|
||||
move |_, result| {
|
||||
let mapped = result
|
||||
.map_err(SpacetimeClientError::from_sdk_error)
|
||||
.and_then(map_game_distribution_admin_theme_member_remove_result);
|
||||
send_once(&sender, mapped);
|
||||
},
|
||||
);
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// 后台主题列表:含 `draft` / `archived`,无游标(一次取全,超界截断到上限)。
|
||||
pub async fn list_admin_game_distribution_themes(
|
||||
&self,
|
||||
input: GameDistributionAdminThemeListRecordInput,
|
||||
) -> Result<Vec<GameDistributionAdminThemeRecord>, SpacetimeClientError> {
|
||||
let input = crate::module_bindings::GameDistributionAdminThemeListInput {
|
||||
status: input.status,
|
||||
limit: input.limit,
|
||||
};
|
||||
self.call_after_connect(
|
||||
"list_admin_game_distribution_themes",
|
||||
move |connection, sender| {
|
||||
connection
|
||||
.procedures()
|
||||
.list_admin_game_distribution_themes_and_return_then(
|
||||
input,
|
||||
move |_, result| {
|
||||
let mapped = result
|
||||
.map_err(SpacetimeClientError::from_sdk_error)
|
||||
.and_then(map_game_distribution_admin_theme_list_result);
|
||||
send_once(&sender, mapped);
|
||||
},
|
||||
);
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// 批量累加已公开游戏的游玩次数;非公开游戏由事务静默跳过。
|
||||
pub async fn increment_game_distribution_game_play_counts(
|
||||
&self,
|
||||
|
||||
@@ -172,6 +172,7 @@ pub mod create_editor_project_resource_and_return_procedure;
|
||||
pub mod create_error_report_and_return_procedure;
|
||||
pub mod create_external_api_key_and_return_procedure;
|
||||
pub mod create_game_distribution_game_and_return_procedure;
|
||||
pub mod create_game_distribution_theme_and_return_procedure;
|
||||
pub mod create_game_distribution_version_and_return_procedure;
|
||||
pub mod create_payment_api_key_and_return_procedure;
|
||||
pub mod create_payment_app_and_return_procedure;
|
||||
@@ -423,6 +424,12 @@ pub mod follow_creator_and_return_procedure;
|
||||
pub mod game_distribution_admin_game_list_input_type;
|
||||
pub mod game_distribution_admin_game_list_result_type;
|
||||
pub mod game_distribution_admin_game_snapshot_type;
|
||||
pub mod game_distribution_admin_theme_list_input_type;
|
||||
pub mod game_distribution_admin_theme_list_result_type;
|
||||
pub mod game_distribution_admin_theme_member_mutation_result_type;
|
||||
pub mod game_distribution_admin_theme_member_snapshot_type;
|
||||
pub mod game_distribution_admin_theme_mutation_result_type;
|
||||
pub mod game_distribution_admin_theme_snapshot_type;
|
||||
pub mod game_distribution_admin_user_review_input_type;
|
||||
pub mod game_distribution_admin_user_review_list_input_type;
|
||||
pub mod game_distribution_admin_user_review_result_type;
|
||||
@@ -441,6 +448,7 @@ pub mod game_distribution_collection_type;
|
||||
pub mod game_distribution_confirm_package_input_type;
|
||||
pub mod game_distribution_confirm_project_bundle_input_type;
|
||||
pub mod game_distribution_create_game_input_type;
|
||||
pub mod game_distribution_create_theme_input_type;
|
||||
pub mod game_distribution_create_version_input_type;
|
||||
pub mod game_distribution_delete_game_input_type;
|
||||
pub mod game_distribution_derived_games_input_type;
|
||||
@@ -491,8 +499,10 @@ pub mod game_distribution_theme_detail_input_type;
|
||||
pub mod game_distribution_theme_detail_result_type;
|
||||
pub mod game_distribution_theme_list_input_type;
|
||||
pub mod game_distribution_theme_list_result_type;
|
||||
pub mod game_distribution_theme_member_remove_input_type;
|
||||
pub mod game_distribution_theme_member_table;
|
||||
pub mod game_distribution_theme_member_type;
|
||||
pub mod game_distribution_theme_member_upsert_input_type;
|
||||
pub mod game_distribution_theme_reference_snapshot_type;
|
||||
pub mod game_distribution_theme_refs_input_type;
|
||||
pub mod game_distribution_theme_refs_result_type;
|
||||
@@ -502,6 +512,7 @@ pub mod game_distribution_theme_type;
|
||||
pub mod game_distribution_uncollect_game_input_type;
|
||||
pub mod game_distribution_unpublish_input_type;
|
||||
pub mod game_distribution_update_metadata_input_type;
|
||||
pub mod game_distribution_update_theme_input_type;
|
||||
pub mod game_distribution_user_review_input_type;
|
||||
pub mod game_distribution_user_review_list_input_type;
|
||||
pub mod game_distribution_user_review_result_type;
|
||||
@@ -562,6 +573,7 @@ pub mod initialize_editor_generation_pricing_config_if_missing_and_return_proced
|
||||
pub mod is_game_distribution_collected_and_return_procedure;
|
||||
pub mod list_admin_accounts_and_return_procedure;
|
||||
pub mod list_admin_game_distribution_games_and_return_procedure;
|
||||
pub mod list_admin_game_distribution_themes_and_return_procedure;
|
||||
pub mod list_admin_game_distribution_user_reviews_and_return_procedure;
|
||||
pub mod list_agc_tracking_events_procedure;
|
||||
pub mod list_asset_history_and_return_procedure;
|
||||
@@ -727,6 +739,7 @@ pub mod refund_profile_wallet_points_and_return_procedure;
|
||||
pub mod reject_game_distribution_version_and_return_procedure;
|
||||
pub mod release_profile_recharge_refund_hold_and_return_procedure;
|
||||
pub mod remove_creator_follower_and_return_procedure;
|
||||
pub mod remove_game_distribution_theme_member_and_return_procedure;
|
||||
pub mod rename_editor_project_and_return_procedure;
|
||||
pub mod renew_external_generation_job_lease_and_return_procedure;
|
||||
pub mod repair_editor_asset_media_and_return_procedure;
|
||||
@@ -916,11 +929,13 @@ pub mod update_editor_showcase_asset_display_and_return_procedure;
|
||||
pub mod update_error_report_and_return_procedure;
|
||||
pub mod update_external_generation_job_phase_and_return_procedure;
|
||||
pub mod update_game_distribution_game_metadata_and_return_procedure;
|
||||
pub mod update_game_distribution_theme_and_return_procedure;
|
||||
pub mod update_payment_app_and_return_procedure;
|
||||
pub mod upload_agc_analytics_batch_procedure;
|
||||
pub mod upsert_editor_generation_pricing_config_and_return_procedure;
|
||||
pub mod upsert_editor_showcase_campaign_config_and_return_procedure;
|
||||
pub mod upsert_feature_gate_config_procedure;
|
||||
pub mod upsert_game_distribution_theme_member_and_return_procedure;
|
||||
pub mod upsert_llm_router_account_and_return_procedure;
|
||||
pub mod upsert_runtime_setting_and_return_procedure;
|
||||
pub mod user_account_table;
|
||||
@@ -1097,6 +1112,7 @@ pub use create_editor_project_resource_and_return_procedure::create_editor_proje
|
||||
pub use create_error_report_and_return_procedure::create_error_report_and_return;
|
||||
pub use create_external_api_key_and_return_procedure::create_external_api_key_and_return;
|
||||
pub use create_game_distribution_game_and_return_procedure::create_game_distribution_game_and_return;
|
||||
pub use create_game_distribution_theme_and_return_procedure::create_game_distribution_theme_and_return;
|
||||
pub use create_game_distribution_version_and_return_procedure::create_game_distribution_version_and_return;
|
||||
pub use create_payment_api_key_and_return_procedure::create_payment_api_key_and_return;
|
||||
pub use create_payment_app_and_return_procedure::create_payment_app_and_return;
|
||||
@@ -1348,6 +1364,12 @@ pub use follow_creator_and_return_procedure::follow_creator_and_return;
|
||||
pub use game_distribution_admin_game_list_input_type::GameDistributionAdminGameListInput;
|
||||
pub use game_distribution_admin_game_list_result_type::GameDistributionAdminGameListResult;
|
||||
pub use game_distribution_admin_game_snapshot_type::GameDistributionAdminGameSnapshot;
|
||||
pub use game_distribution_admin_theme_list_input_type::GameDistributionAdminThemeListInput;
|
||||
pub use game_distribution_admin_theme_list_result_type::GameDistributionAdminThemeListResult;
|
||||
pub use game_distribution_admin_theme_member_mutation_result_type::GameDistributionAdminThemeMemberMutationResult;
|
||||
pub use game_distribution_admin_theme_member_snapshot_type::GameDistributionAdminThemeMemberSnapshot;
|
||||
pub use game_distribution_admin_theme_mutation_result_type::GameDistributionAdminThemeMutationResult;
|
||||
pub use game_distribution_admin_theme_snapshot_type::GameDistributionAdminThemeSnapshot;
|
||||
pub use game_distribution_admin_user_review_input_type::GameDistributionAdminUserReviewInput;
|
||||
pub use game_distribution_admin_user_review_list_input_type::GameDistributionAdminUserReviewListInput;
|
||||
pub use game_distribution_admin_user_review_result_type::GameDistributionAdminUserReviewResult;
|
||||
@@ -1366,6 +1388,7 @@ pub use game_distribution_collection_type::GameDistributionCollection;
|
||||
pub use game_distribution_confirm_package_input_type::GameDistributionConfirmPackageInput;
|
||||
pub use game_distribution_confirm_project_bundle_input_type::GameDistributionConfirmProjectBundleInput;
|
||||
pub use game_distribution_create_game_input_type::GameDistributionCreateGameInput;
|
||||
pub use game_distribution_create_theme_input_type::GameDistributionCreateThemeInput;
|
||||
pub use game_distribution_create_version_input_type::GameDistributionCreateVersionInput;
|
||||
pub use game_distribution_delete_game_input_type::GameDistributionDeleteGameInput;
|
||||
pub use game_distribution_derived_games_input_type::GameDistributionDerivedGamesInput;
|
||||
@@ -1416,8 +1439,10 @@ pub use game_distribution_theme_detail_input_type::GameDistributionThemeDetailIn
|
||||
pub use game_distribution_theme_detail_result_type::GameDistributionThemeDetailResult;
|
||||
pub use game_distribution_theme_list_input_type::GameDistributionThemeListInput;
|
||||
pub use game_distribution_theme_list_result_type::GameDistributionThemeListResult;
|
||||
pub use game_distribution_theme_member_remove_input_type::GameDistributionThemeMemberRemoveInput;
|
||||
pub use game_distribution_theme_member_table::*;
|
||||
pub use game_distribution_theme_member_type::GameDistributionThemeMember;
|
||||
pub use game_distribution_theme_member_upsert_input_type::GameDistributionThemeMemberUpsertInput;
|
||||
pub use game_distribution_theme_reference_snapshot_type::GameDistributionThemeReferenceSnapshot;
|
||||
pub use game_distribution_theme_refs_input_type::GameDistributionThemeRefsInput;
|
||||
pub use game_distribution_theme_refs_result_type::GameDistributionThemeRefsResult;
|
||||
@@ -1427,6 +1452,7 @@ pub use game_distribution_theme_type::GameDistributionTheme;
|
||||
pub use game_distribution_uncollect_game_input_type::GameDistributionUncollectGameInput;
|
||||
pub use game_distribution_unpublish_input_type::GameDistributionUnpublishInput;
|
||||
pub use game_distribution_update_metadata_input_type::GameDistributionUpdateMetadataInput;
|
||||
pub use game_distribution_update_theme_input_type::GameDistributionUpdateThemeInput;
|
||||
pub use game_distribution_user_review_input_type::GameDistributionUserReviewInput;
|
||||
pub use game_distribution_user_review_list_input_type::GameDistributionUserReviewListInput;
|
||||
pub use game_distribution_user_review_result_type::GameDistributionUserReviewResult;
|
||||
@@ -1487,6 +1513,7 @@ pub use initialize_editor_generation_pricing_config_if_missing_and_return_proced
|
||||
pub use is_game_distribution_collected_and_return_procedure::is_game_distribution_collected_and_return;
|
||||
pub use list_admin_accounts_and_return_procedure::list_admin_accounts_and_return;
|
||||
pub use list_admin_game_distribution_games_and_return_procedure::list_admin_game_distribution_games_and_return;
|
||||
pub use list_admin_game_distribution_themes_and_return_procedure::list_admin_game_distribution_themes_and_return;
|
||||
pub use list_admin_game_distribution_user_reviews_and_return_procedure::list_admin_game_distribution_user_reviews_and_return;
|
||||
pub use list_agc_tracking_events_procedure::list_agc_tracking_events;
|
||||
pub use list_asset_history_and_return_procedure::list_asset_history_and_return;
|
||||
@@ -1652,6 +1679,7 @@ pub use refund_profile_wallet_points_and_return_procedure::refund_profile_wallet
|
||||
pub use reject_game_distribution_version_and_return_procedure::reject_game_distribution_version_and_return;
|
||||
pub use release_profile_recharge_refund_hold_and_return_procedure::release_profile_recharge_refund_hold_and_return;
|
||||
pub use remove_creator_follower_and_return_procedure::remove_creator_follower_and_return;
|
||||
pub use remove_game_distribution_theme_member_and_return_procedure::remove_game_distribution_theme_member_and_return;
|
||||
pub use rename_editor_project_and_return_procedure::rename_editor_project_and_return;
|
||||
pub use renew_external_generation_job_lease_and_return_procedure::renew_external_generation_job_lease_and_return;
|
||||
pub use repair_editor_asset_media_and_return_procedure::repair_editor_asset_media_and_return;
|
||||
@@ -1841,11 +1869,13 @@ pub use update_editor_showcase_asset_display_and_return_procedure::update_editor
|
||||
pub use update_error_report_and_return_procedure::update_error_report_and_return;
|
||||
pub use update_external_generation_job_phase_and_return_procedure::update_external_generation_job_phase_and_return;
|
||||
pub use update_game_distribution_game_metadata_and_return_procedure::update_game_distribution_game_metadata_and_return;
|
||||
pub use update_game_distribution_theme_and_return_procedure::update_game_distribution_theme_and_return;
|
||||
pub use update_payment_app_and_return_procedure::update_payment_app_and_return;
|
||||
pub use upload_agc_analytics_batch_procedure::upload_agc_analytics_batch;
|
||||
pub use upsert_editor_generation_pricing_config_and_return_procedure::upsert_editor_generation_pricing_config_and_return;
|
||||
pub use upsert_editor_showcase_campaign_config_and_return_procedure::upsert_editor_showcase_campaign_config_and_return;
|
||||
pub use upsert_feature_gate_config_procedure::upsert_feature_gate_config;
|
||||
pub use upsert_game_distribution_theme_member_and_return_procedure::upsert_game_distribution_theme_member_and_return;
|
||||
pub use upsert_llm_router_account_and_return_procedure::upsert_llm_router_account_and_return;
|
||||
pub use upsert_runtime_setting_and_return_procedure::upsert_runtime_setting_and_return;
|
||||
pub use user_account_table::*;
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::game_distribution_admin_theme_mutation_result_type::GameDistributionAdminThemeMutationResult;
|
||||
use super::game_distribution_create_theme_input_type::GameDistributionCreateThemeInput;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
struct CreateGameDistributionThemeAndReturnArgs {
|
||||
pub input: GameDistributionCreateThemeInput,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for CreateGameDistributionThemeAndReturnArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the procedure `create_game_distribution_theme_and_return`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteProcedures`].
|
||||
pub trait create_game_distribution_theme_and_return {
|
||||
fn create_game_distribution_theme_and_return(&self, input: GameDistributionCreateThemeInput) {
|
||||
self.create_game_distribution_theme_and_return_then(input, |_, _| {});
|
||||
}
|
||||
|
||||
fn create_game_distribution_theme_and_return_then(
|
||||
&self,
|
||||
input: GameDistributionCreateThemeInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionAdminThemeMutationResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
impl create_game_distribution_theme_and_return for super::RemoteProcedures {
|
||||
fn create_game_distribution_theme_and_return_then(
|
||||
&self,
|
||||
input: GameDistributionCreateThemeInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionAdminThemeMutationResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, GameDistributionAdminThemeMutationResult>(
|
||||
"create_game_distribution_theme_and_return",
|
||||
CreateGameDistributionThemeAndReturnArgs { input },
|
||||
__callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct GameDistributionAdminThemeListInput {
|
||||
pub status: Option<String>,
|
||||
pub limit: u32,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for GameDistributionAdminThemeListInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::game_distribution_admin_theme_snapshot_type::GameDistributionAdminThemeSnapshot;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct GameDistributionAdminThemeListResult {
|
||||
pub ok: bool,
|
||||
pub themes: Vec<GameDistributionAdminThemeSnapshot>,
|
||||
pub error_message: Option<String>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for GameDistributionAdminThemeListResult {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::game_distribution_admin_theme_member_snapshot_type::GameDistributionAdminThemeMemberSnapshot;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct GameDistributionAdminThemeMemberMutationResult {
|
||||
pub ok: bool,
|
||||
pub member: Option<GameDistributionAdminThemeMemberSnapshot>,
|
||||
pub error_message: Option<String>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for GameDistributionAdminThemeMemberMutationResult {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct GameDistributionAdminThemeMemberSnapshot {
|
||||
pub theme_id: String,
|
||||
pub root_game_id: String,
|
||||
pub sort_order: i64,
|
||||
pub created_at_micros: i64,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for GameDistributionAdminThemeMemberSnapshot {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::game_distribution_admin_theme_snapshot_type::GameDistributionAdminThemeSnapshot;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct GameDistributionAdminThemeMutationResult {
|
||||
pub ok: bool,
|
||||
pub replayed: bool,
|
||||
pub theme: Option<GameDistributionAdminThemeSnapshot>,
|
||||
pub error_message: Option<String>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for GameDistributionAdminThemeMutationResult {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct GameDistributionAdminThemeSnapshot {
|
||||
pub theme_id: String,
|
||||
pub name: String,
|
||||
pub summary: String,
|
||||
pub badge: String,
|
||||
pub sort_order: i64,
|
||||
pub status: String,
|
||||
pub member_count: u64,
|
||||
pub created_at_micros: i64,
|
||||
pub updated_at_micros: i64,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for GameDistributionAdminThemeSnapshot {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct GameDistributionCreateThemeInput {
|
||||
pub theme_id: String,
|
||||
pub name: String,
|
||||
pub summary: String,
|
||||
pub badge: String,
|
||||
pub sort_order: i64,
|
||||
pub status: String,
|
||||
pub admin_user_id: String,
|
||||
pub idempotency_key: String,
|
||||
pub request_digest: String,
|
||||
pub now_micros: i64,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for GameDistributionCreateThemeInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct GameDistributionThemeMemberRemoveInput {
|
||||
pub theme_id: String,
|
||||
pub root_game_id: String,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for GameDistributionThemeMemberRemoveInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct GameDistributionThemeMemberUpsertInput {
|
||||
pub theme_id: String,
|
||||
pub root_game_id: String,
|
||||
pub sort_order: i64,
|
||||
pub now_micros: i64,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for GameDistributionThemeMemberUpsertInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct GameDistributionUpdateThemeInput {
|
||||
pub theme_id: String,
|
||||
pub name: String,
|
||||
pub summary: String,
|
||||
pub badge: String,
|
||||
pub sort_order: i64,
|
||||
pub status: String,
|
||||
pub admin_user_id: String,
|
||||
pub idempotency_key: String,
|
||||
pub request_digest: String,
|
||||
pub now_micros: i64,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for GameDistributionUpdateThemeInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::game_distribution_admin_theme_list_input_type::GameDistributionAdminThemeListInput;
|
||||
use super::game_distribution_admin_theme_list_result_type::GameDistributionAdminThemeListResult;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
struct ListAdminGameDistributionThemesAndReturnArgs {
|
||||
pub input: GameDistributionAdminThemeListInput,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for ListAdminGameDistributionThemesAndReturnArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the procedure `list_admin_game_distribution_themes_and_return`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteProcedures`].
|
||||
pub trait list_admin_game_distribution_themes_and_return {
|
||||
fn list_admin_game_distribution_themes_and_return(
|
||||
&self,
|
||||
input: GameDistributionAdminThemeListInput,
|
||||
) {
|
||||
self.list_admin_game_distribution_themes_and_return_then(input, |_, _| {});
|
||||
}
|
||||
|
||||
fn list_admin_game_distribution_themes_and_return_then(
|
||||
&self,
|
||||
input: GameDistributionAdminThemeListInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionAdminThemeListResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
impl list_admin_game_distribution_themes_and_return for super::RemoteProcedures {
|
||||
fn list_admin_game_distribution_themes_and_return_then(
|
||||
&self,
|
||||
input: GameDistributionAdminThemeListInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionAdminThemeListResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, GameDistributionAdminThemeListResult>(
|
||||
"list_admin_game_distribution_themes_and_return",
|
||||
ListAdminGameDistributionThemesAndReturnArgs { input },
|
||||
__callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::game_distribution_admin_theme_member_mutation_result_type::GameDistributionAdminThemeMemberMutationResult;
|
||||
use super::game_distribution_theme_member_remove_input_type::GameDistributionThemeMemberRemoveInput;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
struct RemoveGameDistributionThemeMemberAndReturnArgs {
|
||||
pub input: GameDistributionThemeMemberRemoveInput,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for RemoveGameDistributionThemeMemberAndReturnArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the procedure `remove_game_distribution_theme_member_and_return`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteProcedures`].
|
||||
pub trait remove_game_distribution_theme_member_and_return {
|
||||
fn remove_game_distribution_theme_member_and_return(
|
||||
&self,
|
||||
input: GameDistributionThemeMemberRemoveInput,
|
||||
) {
|
||||
self.remove_game_distribution_theme_member_and_return_then(input, |_, _| {});
|
||||
}
|
||||
|
||||
fn remove_game_distribution_theme_member_and_return_then(
|
||||
&self,
|
||||
input: GameDistributionThemeMemberRemoveInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionAdminThemeMemberMutationResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
impl remove_game_distribution_theme_member_and_return for super::RemoteProcedures {
|
||||
fn remove_game_distribution_theme_member_and_return_then(
|
||||
&self,
|
||||
input: GameDistributionThemeMemberRemoveInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionAdminThemeMemberMutationResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, GameDistributionAdminThemeMemberMutationResult>(
|
||||
"remove_game_distribution_theme_member_and_return",
|
||||
RemoveGameDistributionThemeMemberAndReturnArgs { input },
|
||||
__callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::game_distribution_admin_theme_mutation_result_type::GameDistributionAdminThemeMutationResult;
|
||||
use super::game_distribution_update_theme_input_type::GameDistributionUpdateThemeInput;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
struct UpdateGameDistributionThemeAndReturnArgs {
|
||||
pub input: GameDistributionUpdateThemeInput,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for UpdateGameDistributionThemeAndReturnArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the procedure `update_game_distribution_theme_and_return`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteProcedures`].
|
||||
pub trait update_game_distribution_theme_and_return {
|
||||
fn update_game_distribution_theme_and_return(&self, input: GameDistributionUpdateThemeInput) {
|
||||
self.update_game_distribution_theme_and_return_then(input, |_, _| {});
|
||||
}
|
||||
|
||||
fn update_game_distribution_theme_and_return_then(
|
||||
&self,
|
||||
input: GameDistributionUpdateThemeInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionAdminThemeMutationResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
impl update_game_distribution_theme_and_return for super::RemoteProcedures {
|
||||
fn update_game_distribution_theme_and_return_then(
|
||||
&self,
|
||||
input: GameDistributionUpdateThemeInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionAdminThemeMutationResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, GameDistributionAdminThemeMutationResult>(
|
||||
"update_game_distribution_theme_and_return",
|
||||
UpdateGameDistributionThemeAndReturnArgs { input },
|
||||
__callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::game_distribution_admin_theme_member_mutation_result_type::GameDistributionAdminThemeMemberMutationResult;
|
||||
use super::game_distribution_theme_member_upsert_input_type::GameDistributionThemeMemberUpsertInput;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
struct UpsertGameDistributionThemeMemberAndReturnArgs {
|
||||
pub input: GameDistributionThemeMemberUpsertInput,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for UpsertGameDistributionThemeMemberAndReturnArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for access to the procedure `upsert_game_distribution_theme_member_and_return`.
|
||||
///
|
||||
/// Implemented for [`super::RemoteProcedures`].
|
||||
pub trait upsert_game_distribution_theme_member_and_return {
|
||||
fn upsert_game_distribution_theme_member_and_return(
|
||||
&self,
|
||||
input: GameDistributionThemeMemberUpsertInput,
|
||||
) {
|
||||
self.upsert_game_distribution_theme_member_and_return_then(input, |_, _| {});
|
||||
}
|
||||
|
||||
fn upsert_game_distribution_theme_member_and_return_then(
|
||||
&self,
|
||||
input: GameDistributionThemeMemberUpsertInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionAdminThemeMemberMutationResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
impl upsert_game_distribution_theme_member_and_return for super::RemoteProcedures {
|
||||
fn upsert_game_distribution_theme_member_and_return_then(
|
||||
&self,
|
||||
input: GameDistributionThemeMemberUpsertInput,
|
||||
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<GameDistributionAdminThemeMemberMutationResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, GameDistributionAdminThemeMemberMutationResult>(
|
||||
"upsert_game_distribution_theme_member_and_return",
|
||||
UpsertGameDistributionThemeMemberAndReturnArgs { input },
|
||||
__callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user