Merge remote-tracking branch 'origin/master' into hermes/hermes-1e775b03

# Conflicts:
#	server-rs/crates/api-server/src/app.rs
#	server-rs/crates/api-server/src/creation_entry_config.rs
#	server-rs/crates/api-server/src/puzzle.rs
#	server-rs/crates/spacetime-client/src/lib.rs
#	src/components/platform-entry/PlatformEntryFlowShellImpl.tsx
This commit is contained in:
2026-05-14 19:17:17 +08:00
495 changed files with 40663 additions and 5654 deletions

View File

@@ -114,8 +114,9 @@ export type AuthWechatStartResponse = {
};
export type AuthWechatBindPhoneRequest = {
phone: string;
code: string;
phone?: string;
code?: string;
wechatPhoneCode?: string;
};
export type AuthWechatBindPhoneResponse = {
@@ -123,6 +124,16 @@ export type AuthWechatBindPhoneResponse = {
user: AuthUser;
};
export type AuthWechatMiniProgramLoginRequest = {
code: string;
};
export type AuthWechatMiniProgramLoginResponse = {
token: string;
bindingStatus: AuthBindingStatus;
user: AuthUser;
};
export type AuthPhoneChangeRequest = {
phone: string;
code: string;
@@ -139,6 +150,8 @@ export type AuthRefreshResponse = {
export type AuthSessionSummary = {
sessionId: string;
sessionIds: string[];
sessionCount: number;
clientType: string;
clientRuntime: string;
clientPlatform: string;

View File

@@ -0,0 +1,85 @@
export const BABY_LOVE_DRAWING_TEMPLATE_ID = 'baby-love-drawing';
export const BABY_LOVE_DRAWING_TEMPLATE_NAME = '宝贝爱画';
export const BABY_LOVE_DRAWING_EDUTAINMENT_TAG = '寓教于乐';
export type BabyLoveDrawingTemplateId =
typeof BABY_LOVE_DRAWING_TEMPLATE_ID;
export type BabyLoveDrawingTool = 'brush' | 'eraser';
export type BabyLoveDrawingSaveMode =
| 'original-only'
| 'original-and-magic';
export type BabyLoveDrawingGenerationProvider =
| 'vector-engine-gpt-image-2'
| 'local-demo';
export type BabyLoveDrawingPoint = {
x: number;
y: number;
t: number;
};
export type BabyLoveDrawingStroke = {
strokeId: string;
tool: BabyLoveDrawingTool;
color: string;
points: BabyLoveDrawingPoint[];
};
export type BabyLoveDrawingRecord = {
drawingId: string;
templateId: BabyLoveDrawingTemplateId;
templateName: typeof BABY_LOVE_DRAWING_TEMPLATE_NAME;
originalImageSrc: string;
magicImageSrc: string | null;
strokeTrace: BabyLoveDrawingStroke[];
saveMode: BabyLoveDrawingSaveMode;
themeTags: string[];
createdAt: string;
updatedAt: string;
};
export type CreateBabyLoveDrawingMagicRequest = {
originalImageSrc: string;
strokeTrace: BabyLoveDrawingStroke[];
};
export type CreateBabyLoveDrawingMagicResponse = {
magicImageSrc: string;
generationProvider: BabyLoveDrawingGenerationProvider;
prompt: string;
};
export type SaveBabyLoveDrawingRequest = {
originalImageSrc: string;
magicImageSrc?: string | null;
strokeTrace: BabyLoveDrawingStroke[];
};
export type SaveBabyLoveDrawingResponse = {
record: BabyLoveDrawingRecord;
};
export const BABY_LOVE_DRAWING_RAINBOW_COLORS = [
{ id: 'red', label: '红', value: '#ef4444' },
{ id: 'orange', label: '橙', value: '#f97316' },
{ id: 'yellow', label: '黄', value: '#facc15' },
{ id: 'green', label: '绿', value: '#22c55e' },
{ id: 'cyan', label: '青', value: '#06b6d4' },
{ id: 'blue', label: '蓝', value: '#3b82f6' },
{ id: 'purple', label: '紫', value: '#a855f7' },
] as const;
export type BabyLoveDrawingRainbowColorId =
(typeof BABY_LOVE_DRAWING_RAINBOW_COLORS)[number]['id'];
export function normalizeBabyLoveDrawingTags(tags: string[]) {
return [
...new Set([
BABY_LOVE_DRAWING_EDUTAINMENT_TAG,
...tags.map((tag) => tag.trim()).filter(Boolean),
]),
];
}

View File

@@ -0,0 +1,119 @@
export const BABY_OBJECT_MATCH_TEMPLATE_ID = 'baby-object-match';
export const BABY_OBJECT_MATCH_TEMPLATE_NAME = '宝贝识物';
export const BABY_OBJECT_MATCH_EDUTAINMENT_TAG = '寓教于乐';
export type BabyObjectMatchTemplateId = typeof BABY_OBJECT_MATCH_TEMPLATE_ID;
export type BabyObjectMatchAssetProvider =
| 'vector-engine-gpt-image-2'
| 'placeholder';
export type BabyObjectMatchPublicationStatus = 'draft' | 'published';
export type BabyObjectMatchItemAsset = {
itemId: string;
itemName: string;
imageSrc: string;
assetObjectId: string | null;
generationProvider: BabyObjectMatchAssetProvider;
prompt: string;
};
export type BabyObjectMatchVisualAssetKind =
| 'background'
| 'ui-frame'
| 'gift-box'
| 'basket'
| 'smoke-puff';
export type BabyObjectMatchVisualAsset = {
assetId: string;
assetKind: BabyObjectMatchVisualAssetKind;
imageSrc: string;
assetObjectId: string | null;
generationProvider: BabyObjectMatchAssetProvider;
prompt: string;
};
export type BabyObjectMatchVisualPackage = {
themePrompt: string;
assets: BabyObjectMatchVisualAsset[];
};
export type BabyObjectMatchDraft = {
draftId: string;
profileId: string;
templateId: BabyObjectMatchTemplateId;
templateName: typeof BABY_OBJECT_MATCH_TEMPLATE_NAME;
workTitle: string;
workDescription: string;
itemNames: [string, string];
itemAssets: [BabyObjectMatchItemAsset, BabyObjectMatchItemAsset];
visualPackage?: BabyObjectMatchVisualPackage | null;
themeTags: string[];
publicationStatus: BabyObjectMatchPublicationStatus;
createdAt: string;
updatedAt: string;
publishedAt: string | null;
};
export type CreateBabyObjectMatchDraftRequest = {
itemAName: string;
itemBName: string;
};
export type GenerateBabyObjectMatchAssetsRequest = {
itemNames: [string, string];
};
export type GenerateBabyObjectMatchAssetsResponse = {
assets: [BabyObjectMatchItemAsset, BabyObjectMatchItemAsset];
visualPackage?: BabyObjectMatchVisualPackage | null;
};
export type BabyObjectMatchDraftResponse = {
draft: BabyObjectMatchDraft;
};
export type SaveBabyObjectMatchDraftRequest = {
draft: BabyObjectMatchDraft;
};
export type BabyObjectMatchPublishRequest = {
draft: BabyObjectMatchDraft;
};
export type BabyObjectMatchPublishResponse = {
draft: BabyObjectMatchDraft;
publicWorkCode: string;
};
export function normalizeBabyObjectMatchItemName(value: string) {
return value.trim();
}
export function normalizeBabyObjectMatchTags(tags: string[]) {
return [
...new Set([
BABY_OBJECT_MATCH_EDUTAINMENT_TAG,
...tags.map((tag) => tag.trim()).filter(Boolean),
]),
];
}
export function hasBabyObjectMatchRequiredTag(tags: string[]) {
return tags.some((tag) => tag === BABY_OBJECT_MATCH_EDUTAINMENT_TAG);
}
export function validateBabyObjectMatchItemNames(
payload: CreateBabyObjectMatchDraftRequest,
) {
const itemAName = normalizeBabyObjectMatchItemName(payload.itemAName);
const itemBName = normalizeBabyObjectMatchItemName(payload.itemBName);
return {
itemAName,
itemBName,
valid: Boolean(itemAName && itemBName),
};
}

View File

@@ -2,7 +2,10 @@
* 抓大鹅 Match3D 创作 Agent 共享契约。
* 字段按 HTTP facade 的 camelCase DTO 命名,后端领域层 snake_case 字段由 facade 映射。
*/
import type { Match3DGeneratedItemAsset } from './match3dWorks';
import type {
Match3DGeneratedBackgroundAsset,
Match3DGeneratedItemAsset,
} from './match3dWorks';
export type Match3DCreationStage =
| 'collecting'
@@ -34,6 +37,7 @@ export interface CreateMatch3DAgentSessionRequest {
assetStyleId?: string | null;
assetStyleLabel?: string | null;
assetStylePrompt?: string | null;
generateClickSound?: boolean;
}
export type CreateMatch3DSessionRequest = CreateMatch3DAgentSessionRequest;
@@ -55,6 +59,7 @@ export interface ExecuteMatch3DAgentActionRequest {
coverImageSrc?: string | null;
clearCount?: number;
difficulty?: number;
generateClickSound?: boolean;
}
export type ExecuteMatch3DActionRequest = ExecuteMatch3DAgentActionRequest;
@@ -80,6 +85,7 @@ export interface Match3DCreatorConfig {
assetStyleId?: string | null;
assetStyleLabel?: string | null;
assetStylePrompt?: string | null;
generateClickSound?: boolean;
}
export interface Match3DResultDraft {
@@ -96,6 +102,10 @@ export interface Match3DResultDraft {
totalItemCount?: number;
publishReady?: boolean;
blockers?: string[];
backgroundPrompt?: string | null;
backgroundImageSrc?: string | null;
backgroundImageObjectKey?: string | null;
generatedBackgroundAsset?: Match3DGeneratedBackgroundAsset | null;
generatedItemAssets?: Match3DGeneratedItemAsset[];
}

View File

@@ -46,6 +46,7 @@ export type Match3DClickConfirmStatus =
export interface StartMatch3DRunRequest {
profileId: string;
itemTypeCountOverride?: number | null;
}
export interface Match3DClickItemRequest {

View File

@@ -14,18 +14,42 @@ export type Match3DGeneratedItemAssetStatus =
| 'failed'
| string;
export interface Match3DGeneratedBackgroundAsset {
prompt: string;
imageSrc?: string | null;
imageObjectKey?: string | null;
containerPrompt?: string | null;
containerImageSrc?: string | null;
containerImageObjectKey?: string | null;
status: string;
error?: string | null;
}
export interface Match3DGeneratedItemImageView {
viewId: string;
viewIndex: number;
imageSrc?: string | null;
imageObjectKey?: string | null;
}
export interface Match3DGeneratedItemAsset {
itemId: string;
itemName: string;
imageSrc?: string | null;
imageObjectKey?: string | null;
imageViews?: Match3DGeneratedItemImageView[];
modelSrc?: string | null;
modelObjectKey?: string | null;
modelFileName?: string | null;
taskUuid?: string | null;
subscriptionKey?: string | null;
soundPrompt?: string | null;
backgroundMusicTitle?: string | null;
backgroundMusicStyle?: string | null;
backgroundMusicPrompt?: string | null;
backgroundMusic?: CreationAudioAsset | null;
clickSound?: CreationAudioAsset | null;
backgroundAsset?: Match3DGeneratedBackgroundAsset | null;
status: Match3DGeneratedItemAssetStatus;
error?: string | null;
}
@@ -34,6 +58,52 @@ export interface PutMatch3DAudioAssetsRequest {
generatedItemAssets: Match3DGeneratedItemAsset[];
}
export interface PersistMatch3DGeneratedModelRequest {
itemId: string;
itemName: string;
sourceUrl: string;
fileName?: string | null;
taskUuid?: string | null;
subscriptionKey?: string | null;
}
export interface PersistMatch3DGeneratedModelResponse {
asset: Match3DGeneratedItemAsset;
}
export interface GenerateMatch3DCoverImageRequest {
prompt: string;
referenceImageSrc?: string | null;
}
export interface GenerateMatch3DCoverImageResponse {
item: Match3DWorkProfile;
coverImageSrc: string;
coverImageObjectKey: string;
prompt: string;
}
export interface GenerateMatch3DBackgroundImageRequest {
prompt: string;
}
export interface GenerateMatch3DBackgroundImageResponse {
item: Match3DWorkProfile;
backgroundImageSrc: string;
backgroundImageObjectKey: string;
generatedBackgroundAsset: Match3DGeneratedBackgroundAsset;
prompt: string;
}
export interface GenerateMatch3DItemAssetsRequest {
itemNames: string[];
}
export interface GenerateMatch3DItemAssetsResponse {
item: Match3DWorkProfile;
generatedItemAssets: Match3DGeneratedItemAsset[];
}
export interface PutMatch3DWorkRequest {
gameName: string;
themeText?: string;
@@ -48,6 +118,7 @@ export interface PutMatch3DWorkRequest {
export interface GenerateMatch3DWorkTagsRequest {
gameName: string;
themeText: string;
summary?: string | null;
}
export interface GenerateMatch3DWorkTagsResponse {
@@ -72,6 +143,10 @@ export interface Match3DWorkSummary {
updatedAt: string;
publishedAt?: string | null;
publishReady: boolean;
backgroundPrompt?: string | null;
backgroundImageSrc?: string | null;
backgroundImageObjectKey?: string | null;
generatedBackgroundAsset?: Match3DGeneratedBackgroundAsset | null;
generatedItemAssets?: Match3DGeneratedItemAsset[];
}

View File

@@ -4,6 +4,7 @@ export type PuzzleAgentSuggestedActionType =
| 'request_summary'
| 'compile_puzzle_draft'
| 'generate_puzzle_images'
| 'generate_puzzle_ui_background'
| 'generate_puzzle_tags'
| 'publish_puzzle_work';
@@ -17,6 +18,7 @@ export type PuzzleAgentActionType =
| 'save_puzzle_form_draft'
| 'compile_puzzle_draft'
| 'generate_puzzle_images'
| 'generate_puzzle_ui_background'
| 'generate_puzzle_tags'
| 'select_puzzle_image'
| 'publish_puzzle_work';
@@ -77,6 +79,16 @@ export type PuzzleAgentActionRequest =
themeTags?: string[];
levelsJson?: string;
}
| {
action: 'generate_puzzle_ui_background';
levelId?: string | null;
promptText: string;
workTitle?: string;
workDescription?: string;
summary?: string;
themeTags?: string[];
levelsJson?: string;
}
| {
action: 'generate_puzzle_tags';
workTitle: string;

View File

@@ -48,6 +48,9 @@ export interface PuzzleDraftLevel {
levelName: string;
pictureDescription: string;
pictureReference?: string | null;
uiBackgroundPrompt?: string | null;
uiBackgroundImageSrc?: string | null;
uiBackgroundImageObjectKey?: string | null;
backgroundMusic?: CreationAudioAsset | null;
candidates: PuzzleGeneratedImageCandidate[];
selectedCandidateId: string | null;

View File

@@ -1,3 +1,5 @@
import type { CreationAudioAsset } from './creationAudio';
export type PuzzleGridSize = 3 | 4 | 5 | 6 | 7;
export interface PuzzleCellPosition {
@@ -55,6 +57,8 @@ export interface PuzzleRuntimeLevelSnapshot {
authorDisplayName: string;
themeTags: string[];
coverImageSrc: string | null;
uiBackgroundImageSrc?: string | null;
backgroundMusic?: CreationAudioAsset | null;
board: PuzzleBoardSnapshot;
status: PuzzleRuntimeLevelStatus;
startedAtMs: number;

View File

@@ -78,7 +78,12 @@ export type ProfileWalletLedgerResponse = {
export type ProfileRechargeProductKind = 'points' | 'membership';
export type ProfileMembershipStatus = 'normal' | 'active';
export type ProfileMembershipTier = 'normal' | 'month' | 'season' | 'year';
export type ProfileRechargeOrderStatus = 'paid';
export type ProfileRechargeOrderStatus =
| 'pending'
| 'paid'
| 'failed'
| 'closed'
| 'refunded';
export type ProfileRechargeProduct = {
productId: string;
@@ -117,7 +122,8 @@ export type ProfileRechargeOrder = {
amountCents: number;
status: ProfileRechargeOrderStatus;
paymentChannel: string;
paidAt: string;
paidAt: string | null;
providerTransactionId: string | null;
createdAt: string;
pointsDelta: number;
membershipExpiresAt: string | null;
@@ -133,6 +139,14 @@ export type ProfileRechargeCenterResponse = {
hasPointsRecharged: boolean;
};
export type WechatMiniProgramPayParams = {
timeStamp: string;
nonceStr: string;
package: string;
signType: 'RSA';
paySign: string;
};
export type CreateProfileRechargeOrderRequest = {
productId: string;
paymentChannel?: string;
@@ -141,6 +155,7 @@ export type CreateProfileRechargeOrderRequest = {
export type CreateProfileRechargeOrderResponse = {
order: ProfileRechargeOrder;
center: ProfileRechargeCenterResponse;
wechatMiniProgramPayParams?: WechatMiniProgramPayParams | null;
};
export type ProfileFeedbackStatus = 'open';

View File

@@ -150,11 +150,21 @@ function readTrimmedMessage(value: unknown) {
return typeof value === 'string' && value.trim() ? value.trim() : '';
}
function readApiErrorDetailMessage(details: unknown) {
if (!isRecord(details)) {
return '';
}
// 后端通用 message 常用于错误分类details.message / details.reason
// 才是给用户定位问题的业务原因,配置缺失类错误通常只带 reason。
return (
readTrimmedMessage(details.message) ||
readTrimmedMessage(details.reason)
);
}
export function getApiErrorDisplayMessage(error: ApiErrorPayload) {
// 后端通用 message 常用于错误分类details.message 才是给用户定位问题的业务原因。
const detailMessage = isRecord(error.details)
? readTrimmedMessage(error.details.message)
: '';
const detailMessage = readApiErrorDetailMessage(error.details);
return detailMessage || readTrimmedMessage(error.message);
}
@@ -177,9 +187,7 @@ export function parseApiErrorMessage(rawText: string, fallbackMessage: string) {
code?: string;
};
const detailMessage = isRecord(parsed.error?.details)
? readTrimmedMessage(parsed.error.details.message)
: '';
const detailMessage = readApiErrorDetailMessage(parsed.error?.details);
if (detailMessage) {
return detailMessage;

View File

@@ -6,6 +6,8 @@ export type * from './contracts/creationAgentDocumentInput';
export type * from './contracts/creationAudio';
export type * from './contracts/creativeAgent';
export type * from './contracts/customWorldAgent';
export * from './contracts/edutainmentBabyDrawing';
export * from './contracts/edutainmentBabyObject';
export type * from './contracts/hyper3d';
export * from './contracts/match3dAgent';
export * from './contracts/match3dRuntime';
@@ -13,8 +15,8 @@ export * from './contracts/match3dWorks';
export * from './contracts/puzzleAgentActions';
export * from './contracts/puzzleAgentDraft';
export * from './contracts/puzzleAgentSession';
export * from './contracts/puzzleOnboarding';
export type * from './contracts/puzzleCreativeTemplate';
export * from './contracts/puzzleOnboarding';
export * from './contracts/puzzleResultPreview';
export * from './contracts/puzzleRuntimeSession';
export * from './contracts/puzzleWorkSummary';