2026-06-26 16:10:59 +08:00

256 lines
8.7 KiB
TypeScript

import { API_ENDPOINTS, API_OPERATIONS, apiEndpointPath } from "@/shared/api/generated/endpoints";
import { apiRequest } from "@/shared/api/request";
import type { EntityId, PageQuery } from "@/shared/api/types";
export type LevelTrackCode = "wealth" | "game" | "charm";
export type LevelConfigStatus = "active" | "disabled";
export interface LevelRuleDto {
createdAtMs?: number;
displayConfigJson?: string;
level: number;
avatarFrameResourceId?: number;
longBadgeResourceId?: number;
name?: string;
requiredValue: number;
rewardResourceGroupId?: number;
shortBadgeResourceId?: number;
sortOrder?: number;
status?: LevelConfigStatus | string;
track: LevelTrackCode;
updatedAtMs?: number;
}
export interface LevelTierDto {
createdAtMs?: number;
displayAvatarFrameResourceId?: number;
displayBadgeResourceId?: number;
displayConfigJson?: string;
maxLevel: number;
minLevel: number;
name?: string;
rewardResourceGroupId?: number;
status?: LevelConfigStatus | string;
tierId: number;
track: LevelTrackCode;
updatedAtMs?: number;
}
export interface LevelTrackDto {
createdAtMs?: number;
displayConfigJson?: string;
name?: string;
rules: LevelRuleDto[];
sortOrder?: number;
status?: LevelConfigStatus | string;
tiers: LevelTierDto[];
track: LevelTrackCode;
updatedAtMs?: number;
}
export interface LevelConfigDto {
serverTimeMs?: number;
tracks: LevelTrackDto[];
}
export interface LevelTrackPayload {
display_config_json?: string;
name: string;
sort_order: number;
status: LevelConfigStatus;
}
export interface LevelRulePayload {
avatar_frame_resource_id?: number;
long_badge_resource_id?: number;
name: string;
required_value: number;
reward_resource_group_id?: number;
short_badge_resource_id?: number;
sort_order: number;
status: LevelConfigStatus;
}
export interface LevelTierPayload {
display_avatar_frame_resource_id?: number;
display_badge_resource_id?: number;
display_config_json?: string;
max_level: number;
min_level: number;
name: string;
reward_resource_group_id?: number;
status: LevelConfigStatus;
track: LevelTrackCode;
}
type AnyRecord = Record<string, unknown>;
export function listLevelConfig(query: PageQuery = {}): Promise<LevelConfigDto> {
const endpoint = API_ENDPOINTS.listLevelConfig;
return apiRequest<unknown>(apiEndpointPath(API_OPERATIONS.listLevelConfig), {
method: endpoint.method,
query,
}).then(normalizeLevelConfig);
}
export function updateTrack(track: LevelTrackCode, payload: LevelTrackPayload): Promise<LevelTrackDto> {
const endpoint = API_ENDPOINTS.updateTrack;
return apiRequest<unknown, LevelTrackPayload>(apiEndpointPath(API_OPERATIONS.updateTrack, { track }), {
body: payload,
method: endpoint.method,
}).then(normalizeTrack);
}
export function upsertRule(
track: LevelTrackCode,
level: EntityId,
payload: LevelRulePayload,
): Promise<LevelRuleDto> {
const endpoint = API_ENDPOINTS.upsertRule;
return apiRequest<unknown, LevelRulePayload>(
apiEndpointPath(API_OPERATIONS.upsertRule, { level, track }),
{
body: payload,
method: endpoint.method,
},
).then(normalizeRule);
}
export function createTier(payload: LevelTierPayload): Promise<LevelTierDto> {
const endpoint = API_ENDPOINTS.createTier;
return apiRequest<unknown, LevelTierPayload>(apiEndpointPath(API_OPERATIONS.createTier), {
body: payload,
method: endpoint.method,
}).then(normalizeTier);
}
export function updateTier(tierId: EntityId, payload: LevelTierPayload): Promise<LevelTierDto> {
const endpoint = API_ENDPOINTS.updateTier;
return apiRequest<unknown, LevelTierPayload>(apiEndpointPath(API_OPERATIONS.updateTier, { tier_id: tierId }), {
body: payload,
method: endpoint.method,
}).then(normalizeTier);
}
function normalizeLevelConfig(raw: unknown): LevelConfigDto {
const source = asRecord(raw);
return {
serverTimeMs: numberValue(source.serverTimeMs ?? source.server_time_ms),
tracks: arrayValue(source.tracks).map(normalizeTrack),
};
}
function normalizeTrack(raw: unknown): LevelTrackDto {
const source = asRecord(raw);
return {
createdAtMs: numberValue(source.createdAtMs ?? source.created_at_ms),
displayConfigJson: jsonString(source.displayConfigJson ?? source.display_config_json),
name: stringValue(source.name),
rules: arrayValue(source.rules).map(normalizeRule),
sortOrder: numberValue(source.sortOrder ?? source.sort_order),
status: stringValue(source.status),
tiers: arrayValue(source.tiers).map(normalizeTier),
track: stringValue(source.track) as LevelTrackCode,
updatedAtMs: numberValue(source.updatedAtMs ?? source.updated_at_ms),
};
}
function normalizeRule(raw: unknown): LevelRuleDto {
const source = asRecord(raw);
const displayConfig = source.displayConfigJson ?? source.display_config_json;
const displayConfigObject = parseDisplayConfig(displayConfig);
return {
createdAtMs: numberValue(source.createdAtMs ?? source.created_at_ms),
displayConfigJson: jsonString(displayConfig),
level: numberValue(source.level),
avatarFrameResourceId: numberValue(
source.avatarFrameResourceId ??
source.avatar_frame_resource_id ??
displayConfigObject.avatarFrameResourceId ??
displayConfigObject.avatar_frame_resource_id,
),
longBadgeResourceId: numberValue(
source.longBadgeResourceId ??
source.long_badge_resource_id ??
displayConfigObject.longBadgeResourceId ??
displayConfigObject.long_badge_resource_id,
),
name: stringValue(source.name),
requiredValue: numberValue(source.requiredValue ?? source.required_value),
rewardResourceGroupId: numberValue(source.rewardResourceGroupId ?? source.reward_resource_group_id),
shortBadgeResourceId: numberValue(
source.shortBadgeResourceId ??
source.short_badge_resource_id ??
displayConfigObject.shortBadgeResourceId ??
displayConfigObject.short_badge_resource_id,
),
sortOrder: numberValue(source.sortOrder ?? source.sort_order),
status: stringValue(source.status),
track: stringValue(source.track) as LevelTrackCode,
updatedAtMs: numberValue(source.updatedAtMs ?? source.updated_at_ms),
};
}
function normalizeTier(raw: unknown): LevelTierDto {
const source = asRecord(raw);
return {
createdAtMs: numberValue(source.createdAtMs ?? source.created_at_ms),
displayAvatarFrameResourceId: numberValue(
source.displayAvatarFrameResourceId ?? source.display_avatar_frame_resource_id,
),
displayBadgeResourceId: numberValue(source.displayBadgeResourceId ?? source.display_badge_resource_id),
displayConfigJson: jsonString(source.displayConfigJson ?? source.display_config_json),
maxLevel: numberValue(source.maxLevel ?? source.max_level),
minLevel: numberValue(source.minLevel ?? source.min_level),
name: stringValue(source.name),
rewardResourceGroupId: numberValue(source.rewardResourceGroupId ?? source.reward_resource_group_id),
status: stringValue(source.status),
tierId: numberValue(source.tierId ?? source.tier_id),
track: stringValue(source.track) as LevelTrackCode,
updatedAtMs: numberValue(source.updatedAtMs ?? source.updated_at_ms),
};
}
function asRecord(value: unknown): AnyRecord {
return value && typeof value === "object" && !Array.isArray(value) ? (value as AnyRecord) : {};
}
function arrayValue(value: unknown): unknown[] {
return Array.isArray(value) ? value : [];
}
function numberValue(value: unknown): number {
if (value === undefined || value === null || value === "") {
return 0;
}
const numeric = Number(value);
return Number.isFinite(numeric) ? numeric : 0;
}
function stringValue(value: unknown): string {
return value === undefined || value === null ? "" : String(value);
}
function jsonString(value: unknown): string {
if (value === undefined || value === null || value === "") {
return "";
}
if (typeof value === "string") {
return value;
}
return JSON.stringify(value);
}
function parseDisplayConfig(value: unknown): AnyRecord {
const raw = jsonString(value).trim();
if (!raw) {
return {};
}
try {
return asRecord(JSON.parse(raw));
} catch {
return {};
}
}