AGC 发布面板接入买断制定价并抽取共享定价组件
Project CI / Backend tests (pull_request) Failing after 18s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 5m0s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 6m47s
Project CI / Repository checks (pull_request) Failing after 56s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m28s
Project CI / Frontend tests (pull_request) Successful in 4m18s
Project CI / AI game creator shell web tests (pull_request) Successful in 4m7s
Project CI / Native shell tests (pull_request) Successful in 9m27s
Project CI / Backend tests (pull_request) Failing after 18s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 5m0s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 6m47s
Project CI / Repository checks (pull_request) Failing after 56s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m28s
Project CI / Frontend tests (pull_request) Successful in 4m18s
Project CI / AI game creator shell web tests (pull_request) Successful in 4m7s
Project CI / Native shell tests (pull_request) Successful in 9m27s
packages/shared/src/components:新增 PlatformGamePricingField(.tsx/.css/.test.tsx)与 platformGamePricingModel(上限常量与解析),并从 components/index.ts 导出;组件只表达付费方式单选、买断价输入、上限提示、禁用态与错误行,校验由使用方持有。 src/components/common/PlatformGamePricingField.tsx:按既有的 common 适配约定新增网页端再导出。 src/components/game-distribution/GamePublishPage.tsx:把内联的付费方式 fieldset 与买断价输入替换为共享组件,文案、校验、禁用逻辑与既有用例保持不变。 src/components/game-distribution/gamePublishMetadata.ts:买断价上限与解析改为复用共享常量与共享函数,不再各写一份。 apps/ai-game-creator-shell/src/components/game-distribution/GameDistributionPublishPanel.tsx:首次发布与更新两条路径都加入共享定价组件,默认免费;提交前本地校验并透传价格。 apps/ai-game-creator-shell/src/services/gameDistributionPublish.ts:发布命令载荷透传 priceMudPoints,并做 0..=1000000 兜底校验;发布草稿回读新增 priceMudPoints 字段。 apps/ai-game-creator-shell/src-tauri/src/game_distribution_publish.rs:Tauri 命令新增可选 price_mud_points(旧前端不传按免费,向后兼容)并替换硬编码的 0;回读作者作品时解析 priceMudPoints,按「最近版本冻结价优先、回退游戏行当前价」预填,避免已上线付费作品被静默改成免费;发布摘要纳入价格,只改价格也会换幂等键,不会重放上一次收据。 apps/ai-game-creator-shell/tests:补充买断价透传、本地校验拦截、更新模式价格预填与免费兜底用例。 packages/shared/src/components/PlatformGamePricingField.test.tsx:覆盖免费/买断制切换、价格输入、上限提示、禁用态与错误行。
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
export type { PlatformGamePricingFieldProps } from '@genarrative/shared/components';
|
||||
export { PlatformGamePricingField } from '@genarrative/shared/components';
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
} from '../../services/gameDistributionClient';
|
||||
import { useAuthUi } from '../auth/AuthUiContext';
|
||||
import { PlatformActionButton } from '../common/PlatformActionButton';
|
||||
import { PlatformGamePricingField } from '../common/PlatformGamePricingField';
|
||||
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
|
||||
import {
|
||||
GAME_METADATA_CATEGORIES,
|
||||
@@ -40,7 +41,6 @@ import { applyVersionMediaDefaults } from './gamePublishMediaDefaults';
|
||||
import { GamePublishMediaFieldset } from './GamePublishMediaFieldset';
|
||||
import {
|
||||
type GamePublishPriceMode,
|
||||
MAX_GAME_PRICE_MUD_POINTS,
|
||||
resolveGamePublishPriceMudPoints,
|
||||
resolvePublishMetadataError,
|
||||
} from './gamePublishMetadata';
|
||||
@@ -804,45 +804,13 @@ export function GamePublishPage({
|
||||
</label>
|
||||
))}
|
||||
</fieldset>
|
||||
<fieldset className="game-publish-fieldset">
|
||||
<legend>付费方式</legend>
|
||||
<label className="game-publish-checkbox">
|
||||
<input
|
||||
type="radio"
|
||||
name="game-price-mode"
|
||||
checked={priceMode === 'free'}
|
||||
disabled={isResumingDraft}
|
||||
onChange={() => setPriceMode('free')}
|
||||
/>
|
||||
免费
|
||||
</label>
|
||||
<label className="game-publish-checkbox">
|
||||
<input
|
||||
type="radio"
|
||||
name="game-price-mode"
|
||||
checked={priceMode === 'paid'}
|
||||
disabled={isResumingDraft}
|
||||
onChange={() => setPriceMode('paid')}
|
||||
/>
|
||||
买断制
|
||||
</label>
|
||||
</fieldset>
|
||||
{priceMode === 'paid' ? (
|
||||
<label className="game-publish-field">
|
||||
买断价(泥点)
|
||||
<input
|
||||
type="number"
|
||||
inputMode="numeric"
|
||||
value={priceInput}
|
||||
disabled={isResumingDraft}
|
||||
onChange={(event) => setPriceInput(event.target.value)}
|
||||
/>
|
||||
<span className="game-publish-hint">
|
||||
买断一次永久拥有;价格随本次版本审核通过后生效,最多{' '}
|
||||
{MAX_GAME_PRICE_MUD_POINTS} 泥点
|
||||
</span>
|
||||
</label>
|
||||
) : null}
|
||||
<PlatformGamePricingField
|
||||
mode={priceMode}
|
||||
priceInput={priceInput}
|
||||
disabled={isResumingDraft}
|
||||
onModeChange={setPriceMode}
|
||||
onPriceInputChange={setPriceInput}
|
||||
/>
|
||||
<GamePublishMediaFieldset
|
||||
cover={coverAsset}
|
||||
screenshots={screenshotAssets}
|
||||
|
||||
@@ -1,39 +1,24 @@
|
||||
import {
|
||||
PLATFORM_GAME_MAX_PRICE_MUD_POINTS,
|
||||
type PlatformGamePricingMode,
|
||||
resolvePlatformGamePriceMudPoints,
|
||||
} from '../../../packages/shared/src/components/platformGamePricingModel';
|
||||
import type { GameDistributionDeviceSupport } from '../../../packages/shared/src/contracts/gameDistribution';
|
||||
import { MAX_GAME_SCREENSHOTS } from './gamePublishAssets';
|
||||
|
||||
/** 买断价上限,与服务端 `MAX_GAME_PRICE_MUD_POINTS` 保持一致。 */
|
||||
export const MAX_GAME_PRICE_MUD_POINTS = 1_000_000;
|
||||
/** 买断价上限,复用共享定价组件常量,网页与 AGC 及服务端保持同一口径。 */
|
||||
export const MAX_GAME_PRICE_MUD_POINTS = PLATFORM_GAME_MAX_PRICE_MUD_POINTS;
|
||||
|
||||
export type GamePublishPriceMode = 'free' | 'paid';
|
||||
export type GamePublishPriceMode = PlatformGamePricingMode;
|
||||
|
||||
/**
|
||||
* 解析发布表单的付费方式与买断价输入。
|
||||
*
|
||||
* 口径与服务端一致:免费等价于 `0`;买断制必须是 `1..=1_000_000` 的整数泥点。
|
||||
* 返回的 `error` 为空字符串表示可用,否则 `priceMudPoints` 无意义。
|
||||
* 实现落在共享 `platformGamePricingModel`,网页与 AGC 发布面板共用同一份校验。
|
||||
*/
|
||||
export function resolveGamePublishPriceMudPoints(
|
||||
mode: GamePublishPriceMode,
|
||||
rawValue: string,
|
||||
): { priceMudPoints: number; error: string } {
|
||||
if (mode === 'free') return { priceMudPoints: 0, error: '' };
|
||||
const value = rawValue.trim();
|
||||
if (!/^\d+$/u.test(value)) {
|
||||
return { priceMudPoints: 0, error: '买断价必须是整数泥点' };
|
||||
}
|
||||
const priceMudPoints = Number(value);
|
||||
if (
|
||||
!Number.isSafeInteger(priceMudPoints) ||
|
||||
priceMudPoints < 1 ||
|
||||
priceMudPoints > MAX_GAME_PRICE_MUD_POINTS
|
||||
) {
|
||||
return {
|
||||
priceMudPoints: 0,
|
||||
error: `买断价必须是 1 到 ${MAX_GAME_PRICE_MUD_POINTS} 之间的整数泥点`,
|
||||
};
|
||||
}
|
||||
return { priceMudPoints, error: '' };
|
||||
}
|
||||
export const resolveGamePublishPriceMudPoints =
|
||||
resolvePlatformGamePriceMudPoints;
|
||||
|
||||
/** 作者侧元数据校验;口径与服务端 `validate_game_metadata` 保持一致。 */
|
||||
export function resolvePublishMetadataError(input: {
|
||||
|
||||
Reference in New Issue
Block a user