2026-06-25 13:48:35 +08:00

358 lines
14 KiB
TypeScript

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<CPRelationshipDto> {
serverTimeMs?: number;
}
export interface CPApplicationPageDto extends ApiPage<CPApplicationDto> {
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<CPConfigDto> {
const endpoint = API_ENDPOINTS.getCPConfig;
return apiRequest<RawConfig>(apiEndpointPath(API_OPERATIONS.getCPConfig), { method: endpoint.method }).then(
normalizeConfig,
);
}
export function updateCPConfig(payload: CPConfigPayload): Promise<CPConfigDto> {
const endpoint = API_ENDPOINTS.updateCPConfig;
return apiRequest<RawConfig, CPConfigPayload>(apiEndpointPath(API_OPERATIONS.updateCPConfig), {
body: payload,
method: endpoint.method,
}).then(normalizeConfig);
}
export function listCPRelationships(query: PageQuery = {}): Promise<CPRelationshipPageDto> {
const endpoint = API_ENDPOINTS.listCPRelationships;
return apiRequest<RawRelationshipPage>(apiEndpointPath(API_OPERATIONS.listCPRelationships), {
method: endpoint.method,
query,
}).then(normalizeRelationshipPage);
}
export function listCPApplications(query: PageQuery = {}): Promise<CPApplicationPageDto> {
const endpoint = API_ENDPOINTS.listCPApplications;
return apiRequest<RawApplicationPage>(apiEndpointPath(API_OPERATIONS.listCPApplications), {
method: endpoint.method,
query,
}).then(normalizeApplicationPage);
}
type RawConfig = CPConfigDto & Record<string, unknown>;
type RawRelation = CPRelationConfigDto & Record<string, unknown>;
type RawLevel = CPLevelDto & Record<string, unknown>;
type RawWeeklyRank = CPWeeklyRankDto & Record<string, unknown>;
type RawRelationship = CPRelationshipDto & Record<string, unknown>;
type RawApplication = CPApplicationDto & Record<string, unknown>;
type RawUser = CPUserProfileDto & Record<string, unknown>;
type RawGift = CPGiftSnapshotDto & Record<string, unknown>;
type RawRelationshipPage = ApiPage<RawRelationship> & {
relationships?: RawRelationship[];
page_size?: number | string;
server_time_ms?: number | string;
} & Record<string, unknown>;
type RawApplicationPage = ApiPage<RawApplication> & {
applications?: RawApplication[];
page_size?: number | string;
server_time_ms?: number | string;
} & Record<string, unknown>;
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<string, unknown> {
return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : {};
}
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;
}