refactor(游戏分发): contribution 家族 DTO/记录改名 network/stats

- GameDistributionContribution{Totals,Generation,Child,Response} 与对应 *Record 全部改名 GameDistributionNetworkStats*。
- spacetime-client facade/mapper 与 parity 注册表同步;handler get_game_contribution→get_game_network_stats。
- 路由路径与 JSON 字段名本步不动,路由收敛在 M3 统一处理。
This commit is contained in:
2026-10-07 23:27:21 +08:00
parent b321fc29af
commit c55e2ffe2f
7 changed files with 83 additions and 83 deletions
@@ -238,7 +238,7 @@ export type GameDistributionForksResponse = {
* 单独成结构体而不是把字段摊进响应里:将来加点赞 / 收藏 / 收入只需加字段,
* 递归定义(`inherited(W) = Σ 直接子代 total`、`total = own + inherited`)与投影形状都不动。
*/
export type GameDistributionContributionTotals = {
export type GameDistributionNetworkStatsTotals = {
playCount: number;
};
@@ -249,22 +249,22 @@ export type GameDistributionContributionTotals = {
* `inherited` 恒为 `0`——该代从更深代际继承到的量已计入更深代际那一条,重复计入会让
* 「各代之和」大于祖先的 `inherited`(同一个后代会在多层分解里被重复计数)。
*/
export type GameDistributionContributionGeneration = {
export type GameDistributionNetworkStatsGeneration = {
generation: number;
/** 这一代计入分解的后代节点数。 */
gameCount: number;
own: GameDistributionContributionTotals;
inherited: GameDistributionContributionTotals;
total: GameDistributionContributionTotals;
own: GameDistributionNetworkStatsTotals;
inherited: GameDistributionNetworkStatsTotals;
total: GameDistributionNetworkStatsTotals;
};
/** 直接子代明细的一条:`total` 是该直接子代**含自己整棵子树**的值。 */
export type GameDistributionContributionChild = {
export type GameDistributionNetworkStatsChild = {
gameId: string;
generation: number;
own: GameDistributionContributionTotals;
inherited: GameDistributionContributionTotals;
total: GameDistributionContributionTotals;
own: GameDistributionNetworkStatsTotals;
inherited: GameDistributionNetworkStatsTotals;
total: GameDistributionNetworkStatsTotals;
};
/**
@@ -279,13 +279,13 @@ export type GameDistributionContributionChild = {
* (与族谱接口 `truncated` 同约定:超限如实标注,不静默给半个数)。
* **本期只做计算**:响应里没有资金 / 分成 / 结算字段。
*/
export type GameDistributionContributionResponse = {
export type GameDistributionNetworkStatsResponse = {
gameId: string;
own: GameDistributionContributionTotals;
inherited: GameDistributionContributionTotals;
total: GameDistributionContributionTotals;
byGeneration: GameDistributionContributionGeneration[];
directChildren: GameDistributionContributionChild[];
own: GameDistributionNetworkStatsTotals;
inherited: GameDistributionNetworkStatsTotals;
total: GameDistributionNetworkStatsTotals;
byGeneration: GameDistributionNetworkStatsGeneration[];
directChildren: GameDistributionNetworkStatsChild[];
/** 参与计算的后代节点数(不含根)。 */
nodeCount: number;
truncated: boolean;
@@ -67,15 +67,15 @@ const PAIRS = [
['GameDistributionForksResponse', 'GameDistributionForksResponse'],
// 贡献归集与归因(作者视角只读,2026-10-06):指标集合、按代际分解、直接子代明细与响应体
// 逐字段对齐。`truncatedReason` 在 Rust 是 `Option<String>`(未截断为 `null`),TS 侧必须可空。
['GameDistributionContributionTotals', 'GameDistributionContributionTotals'],
['GameDistributionNetworkStatsTotals', 'GameDistributionNetworkStatsTotals'],
[
'GameDistributionContributionGeneration',
'GameDistributionContributionGeneration',
'GameDistributionNetworkStatsGeneration',
'GameDistributionNetworkStatsGeneration',
],
['GameDistributionContributionChild', 'GameDistributionContributionChild'],
['GameDistributionNetworkStatsChild', 'GameDistributionNetworkStatsChild'],
[
'GameDistributionContributionResponse',
'GameDistributionContributionResponse',
'GameDistributionNetworkStatsResponse',
'GameDistributionNetworkStatsResponse',
],
// M2a 取件通道:元数据响应与内容形态同样逐字段对齐(不含对象键是契约的一部分)。
['GameDistributionForkSourceKind', 'GameDistributionForkSourceKind'],
@@ -51,13 +51,13 @@ use shared_contracts::game_distribution::{
GAME_DISTRIBUTION_THEME_INVALID_CURSOR, GAME_DISTRIBUTION_THEME_MEMBER_GAME_NOT_FOUND,
GAME_DISTRIBUTION_THEME_MEMBER_NOT_ROOT, GAME_DISTRIBUTION_THEME_NOT_FOUND,
GAME_DISTRIBUTION_VERSION_NUMBER_CONFLICT, GameDistributionAuthor,
GameDistributionCollectionState, GameDistributionContributionChild,
GameDistributionContributionGeneration, GameDistributionContributionResponse,
GameDistributionContributionTotals, GameDistributionCreateGameRequest,
GameDistributionCollectionState, GameDistributionCreateGameRequest,
GameDistributionCreateThemeRequest, GameDistributionCreateVersionRequest,
GameDistributionForkAuthorization, GameDistributionForkSource, GameDistributionForkSourceKind,
GameDistributionForkSourceResponse, GameDistributionForksResponse, GameDistributionInputMode,
GameDistributionMyReviewResponse, GameDistributionNetworkNode, GameDistributionNetworkResponse,
GameDistributionNetworkStatsChild, GameDistributionNetworkStatsGeneration,
GameDistributionNetworkStatsResponse, GameDistributionNetworkStatsTotals,
GameDistributionOrientation, GameDistributionPlaySessionResponse, GameDistributionPurchase,
GameDistributionPurchaseRequest, GameDistributionPurchaseResponse,
GameDistributionRatingSummary, GameDistributionReview, GameDistributionReviewsResponse,
@@ -606,7 +606,7 @@ pub fn router(state: AppState) -> Router<AppState> {
let contribution = Router::new()
.route(
"/api/game-distribution/games/{game_id}/contribution",
get(get_game_contribution),
get(get_game_network_stats),
)
.route_layer(middleware::from_fn_with_state(
state.clone(),
@@ -2737,9 +2737,9 @@ fn derived_games_payload(derived: GameDistributionForksRecord) -> GameDistributi
}
fn contribution_totals_payload(
totals: spacetime_client::GameDistributionContributionTotalsRecord,
) -> GameDistributionContributionTotals {
GameDistributionContributionTotals {
totals: spacetime_client::GameDistributionNetworkStatsTotalsRecord,
) -> GameDistributionNetworkStatsTotals {
GameDistributionNetworkStatsTotals {
play_count: totals.play_count,
}
}
@@ -2749,9 +2749,9 @@ fn contribution_totals_payload(
/// 逐字段搬运、不做任何重算:数字的唯一来源是模块侧纯函数
/// `module_game_distribution::build_contribution_breakdown`,这一层只负责把它折成对外 DTO。
fn contribution_payload(
record: spacetime_client::GameDistributionContributionRecord,
) -> GameDistributionContributionResponse {
GameDistributionContributionResponse {
record: spacetime_client::GameDistributionNetworkStatsRecord,
) -> GameDistributionNetworkStatsResponse {
GameDistributionNetworkStatsResponse {
game_id: record.game_id,
own: contribution_totals_payload(record.own),
inherited: contribution_totals_payload(record.inherited),
@@ -2759,7 +2759,7 @@ fn contribution_payload(
by_generation: record
.by_generation
.into_iter()
.map(|entry| GameDistributionContributionGeneration {
.map(|entry| GameDistributionNetworkStatsGeneration {
generation: entry.generation,
game_count: entry.game_count,
own: contribution_totals_payload(entry.own),
@@ -2770,7 +2770,7 @@ fn contribution_payload(
direct_children: record
.direct_children
.into_iter()
.map(|entry| GameDistributionContributionChild {
.map(|entry| GameDistributionNetworkStatsChild {
game_id: entry.game_id,
generation: entry.generation,
own: contribution_totals_payload(entry.own),
@@ -2800,7 +2800,7 @@ fn contribution_payload(
/// ③ 代价明确且被接受:登录用户可用 403/404 之分辨别某个 gameId 是否存在——已公开作品本就能
/// 从公开目录枚举,未公开作品则多出一个存在性预言机;本接口只服务作者本人,
/// 这里显式记录该取舍,不假装它不存在。
async fn get_game_contribution(
async fn get_game_network_stats(
State(state): State<AppState>,
Extension(ctx): Extension<RequestContext>,
Extension(auth): Extension<AuthenticatedAccessToken>,
@@ -12192,21 +12192,21 @@ mod tests {
/// 「响应里的数字 == 纯函数的数字」打的是完整链路,而不是两份实现互相印证。
fn contribution_record(
breakdown: module_game_distribution::ContributionBreakdown,
) -> spacetime_client::GameDistributionContributionRecord {
) -> spacetime_client::GameDistributionNetworkStatsRecord {
use spacetime_client::{
GameDistributionContributionChildRecord, GameDistributionContributionGenerationRecord,
GameDistributionContributionRecord, GameDistributionContributionTotalsRecord,
GameDistributionNetworkStatsChildRecord, GameDistributionNetworkStatsGenerationRecord,
GameDistributionNetworkStatsRecord, GameDistributionNetworkStatsTotalsRecord,
};
fn totals(
value: module_game_distribution::ContributionTotals,
) -> GameDistributionContributionTotalsRecord {
GameDistributionContributionTotalsRecord {
) -> GameDistributionNetworkStatsTotalsRecord {
GameDistributionNetworkStatsTotalsRecord {
play_count: value.play_count,
}
}
GameDistributionContributionRecord {
GameDistributionNetworkStatsRecord {
game_id: breakdown.game_id,
own: totals(breakdown.own),
inherited: totals(breakdown.inherited),
@@ -12214,7 +12214,7 @@ mod tests {
by_generation: breakdown
.by_generation
.into_iter()
.map(|entry| GameDistributionContributionGenerationRecord {
.map(|entry| GameDistributionNetworkStatsGenerationRecord {
generation: entry.generation,
game_count: entry.game_count,
own: totals(entry.own),
@@ -12225,7 +12225,7 @@ mod tests {
direct_children: breakdown
.direct_children
.into_iter()
.map(|entry| GameDistributionContributionChildRecord {
.map(|entry| GameDistributionNetworkStatsChildRecord {
game_id: entry.game_id,
generation: entry.generation,
own: totals(entry.own),
@@ -380,7 +380,7 @@ pub struct GameDistributionForksResponse {
/// 递归定义(`inherited(W) = Σ 直接子代 total`、`total = own + inherited`)与投影形状都不动。
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GameDistributionContributionTotals {
pub struct GameDistributionNetworkStatsTotals {
pub play_count: u64,
}
@@ -391,24 +391,24 @@ pub struct GameDistributionContributionTotals {
/// 「各代之和」大于祖先的 `inherited`(同一个后代会在多层分解里被重复计数)。
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GameDistributionContributionGeneration {
pub struct GameDistributionNetworkStatsGeneration {
pub generation: u32,
/// 这一代计入分解的后代节点数。
pub game_count: u64,
pub own: GameDistributionContributionTotals,
pub inherited: GameDistributionContributionTotals,
pub total: GameDistributionContributionTotals,
pub own: GameDistributionNetworkStatsTotals,
pub inherited: GameDistributionNetworkStatsTotals,
pub total: GameDistributionNetworkStatsTotals,
}
/// 直接子代明细的一条:`total` 是该直接子代**含自己整棵子树**的值。
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GameDistributionContributionChild {
pub struct GameDistributionNetworkStatsChild {
pub game_id: String,
pub generation: u32,
pub own: GameDistributionContributionTotals,
pub inherited: GameDistributionContributionTotals,
pub total: GameDistributionContributionTotals,
pub own: GameDistributionNetworkStatsTotals,
pub inherited: GameDistributionNetworkStatsTotals,
pub total: GameDistributionNetworkStatsTotals,
}
/// 作者视角的贡献归集与归因响应(`GET /api/game-distribution/games/{gameId}/contribution`)。
@@ -423,13 +423,13 @@ pub struct GameDistributionContributionChild {
/// **本期只做计算**:响应里没有资金 / 分成 / 结算字段。
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GameDistributionContributionResponse {
pub struct GameDistributionNetworkStatsResponse {
pub game_id: String,
pub own: GameDistributionContributionTotals,
pub inherited: GameDistributionContributionTotals,
pub total: GameDistributionContributionTotals,
pub by_generation: Vec<GameDistributionContributionGeneration>,
pub direct_children: Vec<GameDistributionContributionChild>,
pub own: GameDistributionNetworkStatsTotals,
pub inherited: GameDistributionNetworkStatsTotals,
pub total: GameDistributionNetworkStatsTotals,
pub by_generation: Vec<GameDistributionNetworkStatsGeneration>,
pub direct_children: Vec<GameDistributionNetworkStatsChild>,
/// 参与计算的后代节点数(不含根)。
pub node_count: u64,
pub truncated: bool,
@@ -101,11 +101,11 @@ pub use self::game_distribution::{
GameDistributionAdminThemeMutationRecord, GameDistributionAdminThemeRecord,
GameDistributionAdminUserReviewDetailRecord, GameDistributionAdminUserReviewListRecord,
GameDistributionAdminUserReviewRecord, GameDistributionAdminVersionRecord,
GameDistributionCollectionMutationRecord, GameDistributionContributionChildRecord,
GameDistributionContributionGenerationRecord, GameDistributionContributionRecord,
GameDistributionContributionTotalsRecord, GameDistributionForkSourceRecord,
GameDistributionCollectionMutationRecord, GameDistributionForkSourceRecord,
GameDistributionForksRecord, GameDistributionGameRecord, GameDistributionNetworkNodeRecord,
GameDistributionNetworkRecord, GameDistributionNetworkTreeRecord,
GameDistributionNetworkRecord, GameDistributionNetworkStatsChildRecord,
GameDistributionNetworkStatsGenerationRecord, GameDistributionNetworkStatsRecord,
GameDistributionNetworkStatsTotalsRecord, GameDistributionNetworkTreeRecord,
GameDistributionOwnerGameRecord, GameDistributionPublicGameRecord,
GameDistributionPurchaseRecord, GameDistributionRatingSummaryRecord,
GameDistributionReviewGameListRecord, GameDistributionReviewGameRecord,
@@ -342,7 +342,7 @@ pub struct GameDistributionForksRecord {
///
/// 单独成结构体:将来加点赞 / 收藏 / 收入只需加字段,递归与归因形状都不动。
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct GameDistributionContributionTotalsRecord {
pub struct GameDistributionNetworkStatsTotalsRecord {
pub play_count: u64,
}
@@ -351,22 +351,22 @@ pub struct GameDistributionContributionTotalsRecord {
/// `total` 是这一代对祖先 `inherited` 的贡献(即该代节点自身的量),`inherited` 恒为 0:
/// 更深代际的量归它们自己那一条,重复计入会让各代之和大于祖先的 `inherited`。
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct GameDistributionContributionGenerationRecord {
pub struct GameDistributionNetworkStatsGenerationRecord {
pub generation: u32,
pub game_count: u64,
pub own: GameDistributionContributionTotalsRecord,
pub inherited: GameDistributionContributionTotalsRecord,
pub total: GameDistributionContributionTotalsRecord,
pub own: GameDistributionNetworkStatsTotalsRecord,
pub inherited: GameDistributionNetworkStatsTotalsRecord,
pub total: GameDistributionNetworkStatsTotalsRecord,
}
/// 直接子代明细的一条:`total` 含该子代整棵子树。
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct GameDistributionContributionChildRecord {
pub struct GameDistributionNetworkStatsChildRecord {
pub game_id: String,
pub generation: u32,
pub own: GameDistributionContributionTotalsRecord,
pub inherited: GameDistributionContributionTotalsRecord,
pub total: GameDistributionContributionTotalsRecord,
pub own: GameDistributionNetworkStatsTotalsRecord,
pub inherited: GameDistributionNetworkStatsTotalsRecord,
pub total: GameDistributionNetworkStatsTotalsRecord,
}
/// 作者视角的贡献归集与归因结果。
@@ -375,13 +375,13 @@ pub struct GameDistributionContributionChildRecord {
/// `total == own + inherited`、`node_count == Σ by_generation.game_count`(截断后同样成立)。
/// **本期只做计算**:没有任何资金 / 分成字段。
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct GameDistributionContributionRecord {
pub struct GameDistributionNetworkStatsRecord {
pub game_id: String,
pub own: GameDistributionContributionTotalsRecord,
pub inherited: GameDistributionContributionTotalsRecord,
pub total: GameDistributionContributionTotalsRecord,
pub by_generation: Vec<GameDistributionContributionGenerationRecord>,
pub direct_children: Vec<GameDistributionContributionChildRecord>,
pub own: GameDistributionNetworkStatsTotalsRecord,
pub inherited: GameDistributionNetworkStatsTotalsRecord,
pub total: GameDistributionNetworkStatsTotalsRecord,
pub by_generation: Vec<GameDistributionNetworkStatsGenerationRecord>,
pub direct_children: Vec<GameDistributionNetworkStatsChildRecord>,
pub node_count: u64,
pub truncated: bool,
/// `node_limit` / `depth_limit`;未截断为 `None`。
@@ -691,8 +691,8 @@ pub(crate) fn map_game_distribution_derived_games_result(
fn map_contribution_totals(
value: crate::module_bindings::GameDistributionContributionTotalsSnapshot,
) -> GameDistributionContributionTotalsRecord {
GameDistributionContributionTotalsRecord {
) -> GameDistributionNetworkStatsTotalsRecord {
GameDistributionNetworkStatsTotalsRecord {
play_count: value.play_count,
}
}
@@ -701,7 +701,7 @@ fn map_contribution_totals(
/// `found == false` 表示作品不存在 / 已软删除,折成 `None` 由 api-server 映射 404。
pub(crate) fn map_game_distribution_contribution_result(
result: crate::module_bindings::GameDistributionContributionResult,
) -> Result<Option<GameDistributionContributionRecord>, SpacetimeClientError> {
) -> Result<Option<GameDistributionNetworkStatsRecord>, SpacetimeClientError> {
if !result.ok {
return Err(SpacetimeClientError::procedure_failed(result.error_message));
}
@@ -714,7 +714,7 @@ pub(crate) fn map_game_distribution_contribution_result(
"贡献归集缺失:found 为真但没有结果负载".to_string(),
)));
};
Ok(Some(GameDistributionContributionRecord {
Ok(Some(GameDistributionNetworkStatsRecord {
game_id: contribution.game_id,
own: map_contribution_totals(contribution.own),
inherited: map_contribution_totals(contribution.inherited),
@@ -722,7 +722,7 @@ pub(crate) fn map_game_distribution_contribution_result(
by_generation: contribution
.by_generation
.into_iter()
.map(|entry| GameDistributionContributionGenerationRecord {
.map(|entry| GameDistributionNetworkStatsGenerationRecord {
generation: entry.generation,
game_count: entry.game_count,
own: map_contribution_totals(entry.own),
@@ -733,7 +733,7 @@ pub(crate) fn map_game_distribution_contribution_result(
direct_children: contribution
.direct_children
.into_iter()
.map(|entry| GameDistributionContributionChildRecord {
.map(|entry| GameDistributionNetworkStatsChildRecord {
game_id: entry.game_id,
generation: entry.generation,
own: map_contribution_totals(entry.own),
@@ -833,7 +833,7 @@ impl SpacetimeClient {
&self,
game_id: String,
owner_user_id: String,
) -> Result<Option<GameDistributionContributionRecord>, SpacetimeClientError> {
) -> Result<Option<GameDistributionNetworkStatsRecord>, SpacetimeClientError> {
let input = crate::module_bindings::GameDistributionContributionInput {
game_id,
owner_user_id,