import { apiRequest } from "@/shared/api/request"; import { API_ENDPOINTS, API_OPERATIONS, apiEndpointPath } from "@/shared/api/generated/endpoints"; import type { ApiPage, PageQuery } from "@/shared/api/types"; export type CPRelationType = "cp" | "brother" | "sister"; export interface CPUserProfileDto { userId: string; displayUserId?: string; prettyDisplayUserId?: string; prettyId?: string; username?: string; avatar?: string; } export interface CPGiftSnapshotDto { giftId: string; giftName: string; giftIconUrl: string; giftAnimationUrl: string; giftCount: number; giftValue: number; billingReceiptId: string; } export interface CPRelationshipDto { relationshipId: string; relationType: CPRelationType; status: string; userA: CPUserProfileDto; userB: CPUserProfileDto; intimacyValue: number; level: number; durationDays: number; gift?: CPGiftSnapshotDto; formedAtMs: number; updatedAtMs: number; endedAtMs: number; } export interface CPApplicationDto { applicationId: string; relationType: CPRelationType; status: string; requester: CPUserProfileDto; target: CPUserProfileDto; gift: CPGiftSnapshotDto; roomId: string; roomRegionId: number; createdAtMs: number; updatedAtMs: number; expiresAtMs: number; decidedAtMs: number; } export interface CPRelationshipPageDto extends ApiPage { serverTimeMs?: number; } export interface CPApplicationPageDto extends ApiPage { serverTimeMs?: number; } export interface CPLevelDto { level: number; intimacyThreshold: number; rewardResourceGroupId: number; levelIconUrl: string; status: string; updatedAtMs?: number; } export interface CPRelationConfigDto { relationType: CPRelationType; displayName: string; maxCountPerUser: number; applicationExpireHours: number; breakupCostCoins: number; status: string; levels: CPLevelDto[]; updatedAtMs?: number; } export interface CPConfigDto { appCode: string; relations: CPRelationConfigDto[]; weeklyRank: CPWeeklyRankDto; serverTimeMs?: number; } export interface CPWeeklyRankRewardDto { rankNo: number; resourceGroupId: number; } export interface CPWeeklyRankDto { enabled: boolean; relationType: CPRelationType; topCount: number; rewards: CPWeeklyRankRewardDto[]; updatedByAdminId?: number; createdAtMs?: number; updatedAtMs?: number; } export interface CPConfigPayload { relations: Array<{ relationType: CPRelationType; maxCountPerUser: number; applicationExpireHours: number; breakupCostCoins: number; status: string; levels: Array<{ level: number; intimacyThreshold: number; rewardResourceGroupId: number; levelIconUrl: string; status: string; }>; }>; } export function getCPConfig(): Promise { const endpoint = API_ENDPOINTS.getCPConfig; return apiRequest(apiEndpointPath(API_OPERATIONS.getCPConfig), { method: endpoint.method }).then( normalizeConfig, ); } export function updateCPConfig(payload: CPConfigPayload): Promise { const endpoint = API_ENDPOINTS.updateCPConfig; return apiRequest(apiEndpointPath(API_OPERATIONS.updateCPConfig), { body: payload, method: endpoint.method, }).then(normalizeConfig); } export function listCPRelationships(query: PageQuery = {}): Promise { const endpoint = API_ENDPOINTS.listCPRelationships; return apiRequest(apiEndpointPath(API_OPERATIONS.listCPRelationships), { method: endpoint.method, query, }).then(normalizeRelationshipPage); } export function listCPApplications(query: PageQuery = {}): Promise { const endpoint = API_ENDPOINTS.listCPApplications; return apiRequest(apiEndpointPath(API_OPERATIONS.listCPApplications), { method: endpoint.method, query, }).then(normalizeApplicationPage); } type RawConfig = CPConfigDto & Record; type RawRelation = CPRelationConfigDto & Record; type RawLevel = CPLevelDto & Record; type RawWeeklyRank = CPWeeklyRankDto & Record; type RawRelationship = CPRelationshipDto & Record; type RawApplication = CPApplicationDto & Record; type RawUser = CPUserProfileDto & Record; type RawGift = CPGiftSnapshotDto & Record; type RawRelationshipPage = ApiPage & { relationships?: RawRelationship[]; page_size?: number | string; server_time_ms?: number | string; } & Record; type RawApplicationPage = ApiPage & { applications?: RawApplication[]; page_size?: number | string; server_time_ms?: number | string; } & Record; function normalizeConfig(raw: RawConfig): CPConfigDto { const item = asRecord(raw) as RawConfig; return { appCode: stringValue(item.appCode ?? item.app_code), relations: arrayValue(item.relations).map(normalizeRelation), weeklyRank: normalizeWeeklyRank(item.weeklyRank ?? item.weekly_rank), serverTimeMs: numberValue(item.serverTimeMs ?? item.server_time_ms), }; } function normalizeRelation(raw: unknown): CPRelationConfigDto { const item = asRecord(raw) as RawRelation; const relationType = normalizeRelationType(item.relationType ?? item.relation_type); return { relationType, displayName: stringValue(item.displayName ?? item.display_name) || relationLabel(relationType), maxCountPerUser: numberValue(item.maxCountPerUser ?? item.max_count_per_user), applicationExpireHours: numberValue(item.applicationExpireHours ?? item.application_expire_hours), breakupCostCoins: numberValue(item.breakupCostCoins ?? item.breakup_cost_coins), status: stringValue(item.status) || "active", levels: arrayValue(item.levels).map(normalizeLevel), updatedAtMs: numberValue(item.updatedAtMs ?? item.updated_at_ms), }; } function normalizeWeeklyRank(raw: unknown): CPWeeklyRankDto { const item = asRecord(raw) as RawWeeklyRank; return { enabled: item.enabled !== false, relationType: normalizeRelationType(item.relationType ?? item.relation_type), topCount: numberValue(item.topCount ?? item.top_count) || 3, rewards: arrayValue(item.rewards).map((reward) => { const rawReward = asRecord(reward); return { rankNo: numberValue(rawReward.rankNo ?? rawReward.rank_no), resourceGroupId: numberValue(rawReward.resourceGroupId ?? rawReward.resource_group_id), }; }), updatedByAdminId: numberValue(item.updatedByAdminId ?? item.updated_by_admin_id), createdAtMs: numberValue(item.createdAtMs ?? item.created_at_ms), updatedAtMs: numberValue(item.updatedAtMs ?? item.updated_at_ms), }; } function normalizeLevel(raw: unknown): CPLevelDto { const item = asRecord(raw) as RawLevel; return { level: numberValue(item.level), intimacyThreshold: numberValue(item.intimacyThreshold ?? item.intimacy_threshold), rewardResourceGroupId: numberValue(item.rewardResourceGroupId ?? item.reward_resource_group_id), levelIconUrl: stringValue(item.levelIconUrl ?? item.level_icon_url), status: stringValue(item.status) || "active", updatedAtMs: numberValue(item.updatedAtMs ?? item.updated_at_ms), }; } function normalizeRelationshipPage(page: RawRelationshipPage): CPRelationshipPageDto { const response = asRecord(page) as RawRelationshipPage; const items = arrayValue(response.items ?? response.relationships).map(normalizeRelationship); return { items, page: numberValue(response.page) || 1, pageSize: numberValue(response.pageSize ?? response.page_size) || items.length || 50, total: numberValue(response.total), serverTimeMs: numberValue(response.serverTimeMs ?? response.server_time_ms), }; } function normalizeApplicationPage(page: RawApplicationPage): CPApplicationPageDto { const response = asRecord(page) as RawApplicationPage; const items = arrayValue(response.items ?? response.applications).map(normalizeApplication); return { items, page: numberValue(response.page) || 1, pageSize: numberValue(response.pageSize ?? response.page_size) || items.length || 50, total: numberValue(response.total), serverTimeMs: numberValue(response.serverTimeMs ?? response.server_time_ms), }; } function normalizeRelationship(raw: unknown): CPRelationshipDto { const item = asRecord(raw) as RawRelationship; const userA = normalizeUser(item.userA ?? item.user_a ?? item.me ?? item.requester); const userB = normalizeUser(item.userB ?? item.user_b ?? item.partner ?? item.target); const gift = normalizeGift( item.gift ?? item.sourceGift ?? item.source_gift ?? item.applicationGift ?? item.application_gift, ); return { relationshipId: stringValue(item.relationshipId ?? item.relationship_id), relationType: normalizeRelationType(item.relationType ?? item.relation_type), status: stringValue(item.status) || "active", userA, userB, intimacyValue: numberValue( item.intimacyValue ?? item.intimacy_value ?? item.experienceValue ?? item.experience_value, ), level: numberValue(item.level ?? item.levelNo ?? item.level_no), durationDays: numberValue(item.durationDays ?? item.duration_days ?? item.days), gift: gift.giftId || gift.giftName || gift.giftValue ? gift : undefined, formedAtMs: numberValue(item.formedAtMs ?? item.formed_at_ms ?? item.createdAtMs ?? item.created_at_ms), updatedAtMs: numberValue(item.updatedAtMs ?? item.updated_at_ms), endedAtMs: numberValue(item.endedAtMs ?? item.ended_at_ms), }; } function normalizeApplication(raw: unknown): CPApplicationDto { const item = asRecord(raw) as RawApplication; return { applicationId: stringValue(item.applicationId ?? item.application_id), relationType: normalizeRelationType(item.relationType ?? item.relation_type), status: stringValue(item.status) || "pending", requester: normalizeUser( item.requester ?? item.requesterUser ?? item.requester_user ?? item.userA ?? item.user_a, ), target: normalizeUser(item.target ?? item.targetUser ?? item.target_user ?? item.userB ?? item.user_b), gift: normalizeGift(item.gift ?? item.sourceGift ?? item.source_gift), roomId: stringValue(item.roomId ?? item.room_id), roomRegionId: numberValue(item.roomRegionId ?? item.room_region_id), createdAtMs: numberValue(item.createdAtMs ?? item.created_at_ms), updatedAtMs: numberValue(item.updatedAtMs ?? item.updated_at_ms), expiresAtMs: numberValue(item.expiresAtMs ?? item.expires_at_ms), decidedAtMs: numberValue(item.decidedAtMs ?? item.decided_at_ms), }; } function normalizeUser(raw: unknown): CPUserProfileDto { const item = asRecord(raw) as RawUser; return { userId: stringValue(item.userId ?? item.user_id), displayUserId: optionalString(item.displayUserId ?? item.display_user_id), prettyDisplayUserId: optionalString(item.prettyDisplayUserId ?? item.pretty_display_user_id), prettyId: optionalString(item.prettyId ?? item.pretty_id), username: optionalString(item.username ?? item.name), avatar: optionalString(item.avatar), }; } function normalizeGift(raw: unknown): CPGiftSnapshotDto { const item = asRecord(raw) as RawGift; return { giftId: stringValue(item.giftId ?? item.gift_id), giftName: stringValue(item.giftName ?? item.gift_name ?? item.name), giftIconUrl: stringValue(item.giftIconUrl ?? item.gift_icon_url ?? item.iconUrl ?? item.icon_url), giftAnimationUrl: stringValue( item.giftAnimationUrl ?? item.gift_animation_url ?? item.animationUrl ?? item.animation_url, ), giftCount: numberValue(item.giftCount ?? item.gift_count ?? item.count), giftValue: numberValue(item.giftValue ?? item.gift_value ?? item.value), billingReceiptId: stringValue(item.billingReceiptId ?? item.billing_receipt_id), }; } function normalizeRelationType(value: unknown): CPRelationType { const relationType = stringValue(value).toLowerCase(); if (relationType === "brother" || relationType === "sister") { return relationType; } return "cp"; } function relationLabel(relationType: CPRelationType) { return relationType === "brother" ? "兄弟" : relationType === "sister" ? "姐妹" : "CP"; } function asRecord(value: unknown): Record { return value && typeof value === "object" && !Array.isArray(value) ? (value as Record) : {}; } function arrayValue(value: unknown): unknown[] { return Array.isArray(value) ? value : []; } function stringValue(value: unknown) { return typeof value === "string" ? value : value === undefined || value === null ? "" : String(value); } function optionalString(value: unknown) { const text = stringValue(value).trim(); return text || undefined; } function numberValue(value: unknown) { const number = Number(value || 0); return Number.isFinite(number) ? number : 0; }