feat(游戏共创): 贡献归集与归因(接口 + 契约 + 文档;反洗稿改判搁置)
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
产品口径:「子代所有的都算父代的」,并要能溯源「多少是子代给的、多少是自己的」。本期**只做计算**,
不含资金/分成结算。
- 递归定义:`inherited(W) = Σ_{c∈直接子代} total(c)`,`total(W) = own(W) + inherited(W)`;每个后代在它的每个
祖先里只计一次。
- 接口:`GET /api/game-distribution/games/{game_id}/contribution`(Bearer + `no-store`)——**仅该作品作者**:
未登录/失效 401(中间件)、非作者 403(复用既有 owner-mismatch → FORBIDDEN,不新增第二套鉴权)、
作品不存在或根自身已软删 404。响应:`{ gameId, own, inherited, total, byGeneration[], directChildren[],
nodeCount, truncated, truncatedReason }`;指标结构 `ContributionTotals{playCount}` 可扩展(将来加点赞/收藏/收入)。
- 不变量(测试钉住):`inherited == Σ byGeneration.total == Σ directChildren.total`、`total == own + inherited`、
`nodeCount == Σ byGeneration.gameCount`;`byGeneration` 只含后代(绝对代际,升序)。
- 上限与截断:节点 500 / 深度 32;超限如实标 `truncated: true` + `truncatedReason ∈ {node_limit, depth_limit}`,
已计入部分仍自洽(与族谱 `truncated` 同约定,不静默给半个数);遍历只用血缘表既有索引,**不新增表/索引**。
- 契约:shared-contracts 4 个 DTO + TS 镜像 + parity(+4 组 → 66 组);7 个生成绑定(含 procedure)。
- 文档:技术方案新增 `§3.11 贡献归集与权益归因`(定义/归因分解/接口/上限与截断/鉴权口径与理由)+ §3.4 路由行 +
§5.4 实施证据;**任务 B**:反洗稿(相似度校验)从「待拍板」改为「**产品已决定搁置(2026-10-06)**」并从待拍板
清单移除,结算口径统一为「本期只做贡献归集与归因计算」。
- 测试:module-game-distribution **131 passed**(contribution 12 条,含 A→B→C 三层链路不变量);spacetime-module
**304 passed / 1 ignored**(+3 结构断言);spacetime-client 43 passed;api-server game_distribution **107 passed**(+3)。
门禁:wasm build 0;`cargo check --all-targets` 0;`SPACETIME_SCHEMA_BASE_REF=9f4c7d76 npm run check:spacetime-schema`
0(98 表);DTO parity 0(66 组 / 17 构建器 / 15 手拼类型);`check:project-bundle-policy-parity` OK;
`check:encoding` 0(5543 files);`cargo fmt --all --manifest-path server-rs/Cargo.toml -- --check` 0;`git diff --check` 0。
注:本块未提交 `scripts/*.mjs`(`check-game-distribution-lineage-e2e.mjs` / `-theme-e2e.mjs` /
`capture-game-lineage-visual.mjs`)——属其它 owner,工作区保留未提交。
This commit is contained in:
@@ -240,6 +240,66 @@ export type GameDistributionDerivedResponse = {
|
||||
truncated: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* 归集的指标集合(当前只有游玩数)。
|
||||
*
|
||||
* 单独成结构体而不是把字段摊进响应里:将来加点赞 / 收藏 / 收入只需加字段,
|
||||
* 递归定义(`inherited(W) = Σ 直接子代 total`、`total = own + inherited`)与投影形状都不动。
|
||||
*/
|
||||
export type GameDistributionContributionTotals = {
|
||||
playCount: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* 按代际分解的一条:**只含后代**(不含根自身),`generation` 为绝对代际(与族谱一致)。
|
||||
*
|
||||
* 语义:这一条是祖先 `inherited` 里**由这一代自己产生**的那一份,`total` 即该代贡献;
|
||||
* `inherited` 恒为 `0`——该代从更深代际继承到的量已计入更深代际那一条,重复计入会让
|
||||
* 「各代之和」大于祖先的 `inherited`(同一个后代会在多层分解里被重复计数)。
|
||||
*/
|
||||
export type GameDistributionContributionGeneration = {
|
||||
generation: number;
|
||||
/** 这一代计入分解的后代节点数。 */
|
||||
gameCount: number;
|
||||
own: GameDistributionContributionTotals;
|
||||
inherited: GameDistributionContributionTotals;
|
||||
total: GameDistributionContributionTotals;
|
||||
};
|
||||
|
||||
/** 直接子代明细的一条:`total` 是该直接子代**含自己整棵子树**的值。 */
|
||||
export type GameDistributionContributionChild = {
|
||||
gameId: string;
|
||||
generation: number;
|
||||
own: GameDistributionContributionTotals;
|
||||
inherited: GameDistributionContributionTotals;
|
||||
total: GameDistributionContributionTotals;
|
||||
};
|
||||
|
||||
/**
|
||||
* 作者视角的贡献归集与归因响应(`GET /api/game-distribution/games/{gameId}/contribution`)。
|
||||
*
|
||||
* 不变量(在**截断后的子树**上依然成立):
|
||||
* - `inherited == Σ byGeneration.total == Σ directChildren.total`;
|
||||
* - `total == own + inherited`;
|
||||
* - `nodeCount == Σ byGeneration.gameCount`(= 参与计算的**后代**节点数,不含根)。
|
||||
*
|
||||
* `truncated` 为真时 `truncatedReason` 取 `node_limit` / `depth_limit`,未截断为 `null`
|
||||
* (与族谱接口 `truncated` 同约定:超限如实标注,不静默给半个数)。
|
||||
* **本期只做计算**:响应里没有资金 / 分成 / 结算字段。
|
||||
*/
|
||||
export type GameDistributionContributionResponse = {
|
||||
gameId: string;
|
||||
own: GameDistributionContributionTotals;
|
||||
inherited: GameDistributionContributionTotals;
|
||||
total: GameDistributionContributionTotals;
|
||||
byGeneration: GameDistributionContributionGeneration[];
|
||||
directChildren: GameDistributionContributionChild[];
|
||||
/** 参与计算的后代节点数(不含根)。 */
|
||||
nodeCount: number;
|
||||
truncated: boolean;
|
||||
truncatedReason: string | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fork 取件内容的形态。M2a 只有已构建的发行成品包;M2b 引入工程源包后,同一版本同时存在
|
||||
* 两者时优先 `project`。
|
||||
|
||||
Reference in New Issue
Block a user