duoqi-api/src/types/app-api.ts

140 lines
3.0 KiB
TypeScript

export type SubscriptionTier = 'free' | 'pro' | 'proplus';
export type SubscriptionStatus = 'none' | 'active' | 'expired' | 'cancelled';
export type NodeStatus = 'done' | 'current' | 'locked' | 'chest';
export type LeaderboardScope = 'region' | 'topic';
export type RewardSource = 'ad' | 'check_in' | 'subscription' | 'admin_grant' | 'debug';
export type SubscriptionPlatform = 'huawei' | 'apple' | 'google';
export interface UserBriefDto {
id: string;
nickname: string;
avatarUrl: string | null;
tier: SubscriptionTier;
level: number;
}
export interface SubscriptionDto {
status: SubscriptionStatus;
tier: SubscriptionTier;
expiresAt: string | null;
autoRenew?: boolean;
}
export interface ProgressSummaryDto {
hearts: number;
maxHearts: number;
nextHeartRestoreAt: string | null;
dailyAttemptsLeft: number;
dailyAttemptsMax: number;
nextAttemptResetAt: string | null;
highRewardSessionsLeft: number;
highRewardSessionsMax: number;
xp: number;
level: number;
xpToNextLevel: number;
streakDays: number;
checkInDays: number;
streakProtectedUntil: string | null;
activeTrackId: string | null;
isSubscribed: boolean;
}
export interface ThemeNodeDto {
id: string;
title: string;
status: NodeStatus;
reward: string;
questionCount: number;
}
export interface ThemeTrackDto {
id: string;
name: string;
icon: string;
progress: number;
nodes: ThemeNodeDto[];
}
export interface ShopBenefitDto {
id: string;
type: 'hearts' | 'attempts' | 'streak' | 'subscription';
title: string;
description: string;
enabled: boolean;
requiresAd: boolean;
}
export interface BootstrapDto {
user: UserBriefDto;
progress: ProgressSummaryDto;
tracks: ThemeTrackDto[];
shopBenefits: readonly ShopBenefitDto[];
subscription: SubscriptionDto;
}
export interface ChallengeQuestionDto {
challengeId: string;
trackId: string;
nodeId: string;
question: {
id: string;
prompt: string;
options: ReadonlyArray<{ id: string; text: string }>;
};
}
export interface ChallengeSessionDto {
challengeId: string;
trackId: string;
nodeId: string;
totalQuestions: number;
highRewardEligible: boolean;
questions: readonly ChallengeQuestionDto[];
}
export interface AnswerRequestDto {
challengeId: string;
questionId: string;
selectedOptionId: string;
timeMs: number;
submitRequestId?: string;
}
export interface AnswerResultDto {
answerState: 'correct' | 'wrong';
correctOptionId: string;
xpDelta: number;
progress: {
hearts: number;
dailyAttemptsLeft: number;
highRewardSessionsLeft: number;
highRewardSessionsMax: number;
xp: number;
streakDays: number;
};
knowledgeCard: {
id: string;
title: string;
summary: string;
fact: string;
};
rewards: ReadonlyArray<{
type: string;
source?: string;
amount?: number;
itemId?: string;
quantity?: number;
title?: string;
}>;
}
export interface LeaderboardEntryDto {
rank: number;
userId: string;
displayName: string;
avatarUrl: string | null;
xp: number;
badge: string;
isMe: boolean;
}