import { apiRequest } from "@/shared/api/request"; import { API_ENDPOINTS, API_OPERATIONS, apiEndpointPath } from "@/shared/api/generated/endpoints"; import type { AdminJobDto, ApiPage, AppUserBanPayload, AppUserBanRecordDto, AppUserExportJobDto, AppUserLevelAdjustmentPayload, AppUserLoginLogDto, AppUserDto, AppUserPasswordPayload, AppUserUnbanPayload, AppUserUpdatePayload, AdminGrantPrettyDisplayIDResultDto, EntityId, GeneratePrettyDisplayIDsPayload, GrantPrettyDisplayIDPayload, PageQuery, PrettyDisplayIDDto, PrettyDisplayIDGenerationBatchDto, PrettyDisplayIDPoolDto, PrettyDisplayIDPoolPayload, RecyclePrettyDisplayIDPayload, SetPrettyDisplayIDStatusPayload, } from "@/shared/api/types"; export async function listAppUsers(query: PageQuery = {}): Promise> { const endpoint = API_ENDPOINTS.appListUsers; const data = await apiRequest & { page_size?: number }>( apiEndpointPath(API_OPERATIONS.appListUsers), { method: endpoint.method, query, }, ); return normalizePage(data, normalizeAppUser); } export async function getAppUser(userId: EntityId): Promise { const endpoint = API_ENDPOINTS.appGetUser; const data = await apiRequest(apiEndpointPath(API_OPERATIONS.appGetUser, { id: userId }), { method: endpoint.method, }); return normalizeAppUser(data); } export async function listAppUserLoginLogs(query: PageQuery = {}): Promise> { const endpoint = API_ENDPOINTS.appListLoginLogs; const data = await apiRequest & { page_size?: number }>( apiEndpointPath(API_OPERATIONS.appListLoginLogs), { method: endpoint.method, query, }, ); return normalizePage(data, normalizeAppUserLoginLog); } export async function listAppUserBans(query: PageQuery = {}): Promise> { const endpoint = API_ENDPOINTS.appListBannedUsers; const data = await apiRequest & { page_size?: number }>( apiEndpointPath(API_OPERATIONS.appListBannedUsers), { method: endpoint.method, query, }, ); return normalizePage(data, normalizeAppUserBanRecord); } export async function updateAppUser(userId: EntityId, payload: AppUserUpdatePayload): Promise { const endpoint = API_ENDPOINTS.appUpdateUser; const data = await apiRequest( apiEndpointPath(API_OPERATIONS.appUpdateUser, { id: userId }), { body: payload, method: endpoint.method, }, ); return normalizeAppUser(data); } export async function adjustAppUserLevels( userId: EntityId, payload: AppUserLevelAdjustmentPayload, ): Promise { const endpoint = API_ENDPOINTS.appAdjustUserLevels; const data = await apiRequest( apiEndpointPath(API_OPERATIONS.appAdjustUserLevels, { id: userId }), { body: payload, method: endpoint.method }, ); return normalizeAppUser(data); } export async function banAppUser(userId: EntityId, payload: AppUserBanPayload): Promise { const endpoint = API_ENDPOINTS.appBanUser; const data = await apiRequest( apiEndpointPath(API_OPERATIONS.appBanUser, { id: userId }), { body: payload, method: endpoint.method }, ); // 新接口返回 SetUserStatusResult;兼容旧环境直接返回 AppUser,避免滚动发布期间列表补丁丢失 userId。 return normalizeAppUser(data.user ?? data); } export async function unbanAppUser(userId: EntityId, payload: AppUserUnbanPayload = {}): Promise { const endpoint = API_ENDPOINTS.appUnbanUser; const data = await apiRequest( apiEndpointPath(API_OPERATIONS.appUnbanUser, { id: userId }), { body: payload, method: endpoint.method }, ); return normalizeAppUser(data.user ?? data); } export function createAppUserExport(query: PageQuery = {}): Promise { const endpoint = API_ENDPOINTS.appExportUsers; return apiRequest(apiEndpointPath(API_OPERATIONS.appExportUsers), { method: endpoint.method, query, }); } export function getAppUserExportJob(jobId: EntityId): Promise { const endpoint = API_ENDPOINTS.getJob; return apiRequest(apiEndpointPath(API_OPERATIONS.getJob, { id: jobId }), { method: endpoint.method, }); } export function downloadAppUserExport(jobId: EntityId): Promise { return apiRequest(apiEndpointPath(API_OPERATIONS.downloadJobArtifact, { id: jobId }), { raw: true }); } export function setAppUserPassword( userId: EntityId, payload: AppUserPasswordPayload, ): Promise<{ passwordSet: boolean }> { const endpoint = API_ENDPOINTS.appSetPassword; return apiRequest<{ passwordSet: boolean }, AppUserPasswordPayload>( apiEndpointPath(API_OPERATIONS.appSetPassword, { id: userId }), { body: payload, method: endpoint.method, }, ); } // 靓号管理接口路径全部来自生成的 OpenAPI 端点,页面层只依赖这些函数,避免手写 URL 和契约漂移。 export async function listPrettyDisplayIDPools(query: PageQuery = {}): Promise> { const endpoint = API_ENDPOINTS.listPrettyIdPools; const data = await apiRequest>(apiEndpointPath(API_OPERATIONS.listPrettyIdPools), { method: endpoint.method, query, }); return normalizePage(data, normalizePrettyDisplayIDPool); } export async function createPrettyDisplayIDPool(payload: PrettyDisplayIDPoolPayload): Promise { const endpoint = API_ENDPOINTS.createPrettyIdPool; const data = await apiRequest( apiEndpointPath(API_OPERATIONS.createPrettyIdPool), { body: payload, method: endpoint.method, }, ); return normalizePrettyDisplayIDPool(data); } export async function updatePrettyDisplayIDPool( poolId: EntityId, payload: PrettyDisplayIDPoolPayload, ): Promise { const endpoint = API_ENDPOINTS.updatePrettyIdPool; const data = await apiRequest( apiEndpointPath(API_OPERATIONS.updatePrettyIdPool, { pool_id: poolId }), { body: payload, method: endpoint.method, }, ); return normalizePrettyDisplayIDPool(data); } export async function generatePrettyDisplayIDs( poolId: EntityId, payload: GeneratePrettyDisplayIDsPayload, ): Promise { const endpoint = API_ENDPOINTS.generatePrettyIds; const data = await apiRequest( apiEndpointPath(API_OPERATIONS.generatePrettyIds, { pool_id: poolId }), { body: payload, method: endpoint.method, }, ); return normalizePrettyDisplayIDGenerationBatch(data); } export async function listPrettyDisplayIDs(query: PageQuery = {}): Promise> { const endpoint = API_ENDPOINTS.listPrettyIds; const data = await apiRequest>(apiEndpointPath(API_OPERATIONS.listPrettyIds), { method: endpoint.method, query, }); return normalizePage(data, normalizePrettyDisplayID); } export async function grantPrettyDisplayID( payload: GrantPrettyDisplayIDPayload, ): Promise { const endpoint = API_ENDPOINTS.grantPrettyId; const data = await apiRequest( apiEndpointPath(API_OPERATIONS.grantPrettyId), { body: payload, method: endpoint.method, }, ); return normalizeAdminGrantPrettyDisplayIDResult(data); } export async function recyclePrettyDisplayID( prettyId: EntityId, payload: RecyclePrettyDisplayIDPayload = {}, ): Promise { const endpoint = API_ENDPOINTS.recyclePrettyId; const data = await apiRequest( apiEndpointPath(API_OPERATIONS.recyclePrettyId, { pretty_id: prettyId }), { body: payload, method: endpoint.method, }, ); return normalizePrettyDisplayID(data); } export async function setPrettyDisplayIDStatus( prettyId: EntityId, payload: SetPrettyDisplayIDStatusPayload, ): Promise { const endpoint = API_ENDPOINTS.setPrettyIdStatus; const data = await apiRequest( apiEndpointPath(API_OPERATIONS.setPrettyIdStatus, { pretty_id: prettyId }), { body: payload, method: endpoint.method, }, ); return normalizePrettyDisplayID(data); } // 用户相关接口历史上既返回过 snake_case,也返回过 camelCase;在 API 边界归一化后,共享用户组件只消费稳定字段。 interface RawAppUser { age?: number | null; avatar?: string; balances?: RawAppUserAssetBalance[]; ban?: RawAppUserBan; birth?: string; coin?: number; country?: string; country_display_name?: string; countryDisplayName?: string; country_name?: string; countryName?: string; created_at_ms?: number; createdAtMs?: number; default_display_user_id?: string; defaultDisplayUserId?: string; diamond?: number; display_user_id?: string; displayUserId?: string; equipped_resources?: RawAppUserResource[]; equippedResources?: RawAppUserResource[]; gender?: string; last_active_at_ms?: number; lastActiveAtMs?: number; last_login_at_ms?: number; lastLoginAtMs?: number; last_operated_at_ms?: number; lastOperatedAtMs?: number; last_operator?: RawAppUserOperator; lastOperator?: RawAppUserOperator; levels?: RawAppUserLevels; pretty_display_user_id?: string; prettyDisplayUserId?: string; pretty_id?: string; prettyId?: string; region_id?: number; regionId?: number; region_name?: string; regionName?: string; register_device?: string; registerDevice?: string; resources?: RawAppUserResource[]; roles?: string[]; status?: string; updated_at_ms?: number; updatedAtMs?: number; user_id?: EntityId; userId?: EntityId; username?: string; vip?: RawAppUserVIP; cumulative_recharge_usd_minor?: number; cumulativeRechargeUsdMinor?: number; } interface RawAppUserStatusMutation extends RawAppUser { user?: RawAppUser; } interface RawAppUserLevel { display_level?: number; displayLevel?: number; display_value?: number; displayValue?: number; expires_at_ms?: number; expiresAtMs?: number; real_level?: number; realLevel?: number; real_total_value?: number; realTotalValue?: number; started_at_ms?: number; startedAtMs?: number; temporary_level_id?: string; temporaryLevelId?: string; temporary_target_level?: number; temporaryTargetLevel?: number; track?: string; } interface RawAppUserLevels { charm?: RawAppUserLevel; game?: RawAppUserLevel; wealth?: RawAppUserLevel; } interface RawAppUserBan { active?: boolean; expires_at_ms?: number; expiresAtMs?: number; id?: string; permanent?: boolean; reason?: string; source?: string; } interface RawAppUserOperator { action?: string; admin_id?: EntityId; adminId?: EntityId; name?: string; } type RawAppUserBrief = RawAppUser; interface RawAppUserAssetBalance { asset_type?: string; assetType?: string; available_amount?: number; availableAmount?: number; frozen_amount?: number; frozenAmount?: number; total_amount?: number; totalAmount?: number; updated_at_ms?: number; updatedAtMs?: number; version?: number; } interface RawAppUserVIP { active?: boolean; expires_at_ms?: number; expiresAtMs?: number; level?: number; name?: string; started_at_ms?: number; startedAtMs?: number; updated_at_ms?: number; updatedAtMs?: number; } interface RawAppUserResource { animation_url?: string; animationUrl?: string; asset_url?: string; assetUrl?: string; created_at_ms?: number; createdAtMs?: number; effective_at_ms?: number; effectiveAtMs?: number; entitlement_id?: string; entitlementId?: string; equipped?: boolean; expires_at_ms?: number; expiresAtMs?: number; name?: string; preview_url?: string; previewUrl?: string; quantity?: number; remaining_quantity?: number; remainingQuantity?: number; resource_code?: string; resourceCode?: string; resource_id?: number; resourceId?: number; resource_type?: string; resourceType?: string; source_grant_id?: string; sourceGrantId?: string; status?: string; updated_at_ms?: number; updatedAtMs?: number; } interface RawAppUserBanOperator extends RawAppUserBrief { account?: string; admin_id?: EntityId; adminId?: EntityId; name?: string; type?: string; } interface RawAppUserBanRecord { created_at_ms?: number; createdAtMs?: number; id?: number; new_status?: string; newStatus?: string; old_status?: string; oldStatus?: string; operator?: RawAppUserBanOperator | null; reason?: string; request_id?: string; requestId?: string; target?: RawAppUserBrief; } interface RawAppUserLoginLog extends RawAppUserBrief { block_reason?: string; blockReason?: string; blocked?: boolean; channel?: string; country?: string; created_at_ms?: number; createdAtMs?: number; failure_code?: string; failureCode?: string; id?: number; ip_country_code?: string; ipCountryCode?: string; login_ip?: string; loginIp?: string; login_type?: string; loginType?: string; platform?: string; provider?: string; region_id?: number; regionId?: number; region_name?: string; regionName?: string; request_id?: string; requestId?: string; result?: string; risk_highlighted?: boolean; riskHighlighted?: boolean; } // Go 后台当前按 snake_case JSON 返回,生成类型和页面代码使用 camelCase;Raw 类型同时接收两种形态,便于后续切换生成客户端。 interface RawPrettyDisplayIDPool { app_code?: string; appCode?: string; created_at_ms?: number; createdAtMs?: number; created_by_admin_id?: EntityId; createdByAdminId?: EntityId; level_track?: string; levelTrack?: string; max_level?: number; maxLevel?: number; min_level?: number; minLevel?: number; name?: string; pool_id?: string; poolId?: string; rule_config_json?: string; ruleConfigJson?: string; rule_type?: string; ruleType?: string; sort_order?: number; sortOrder?: number; status?: string; updated_at_ms?: number; updatedAtMs?: number; updated_by_admin_id?: EntityId; updatedByAdminId?: EntityId; } interface RawPrettyDisplayID { app_code?: string; appCode?: string; assigned_at_ms?: number; assignedAtMs?: number; assigned_lease_id?: string; assignedLeaseId?: string; assigned_user?: RawAppUserBrief; assignedUser?: RawAppUserBrief; assigned_user_id?: EntityId; assignedUserId?: EntityId; created_at_ms?: number; createdAtMs?: number; created_by_admin_id?: EntityId; createdByAdminId?: EntityId; display_user_id?: string; displayUserId?: string; generated_batch_id?: string; generatedBatchId?: string; operator?: RawPrettyDisplayIDOperator | null; pool?: RawPrettyDisplayIDPool | null; pool_id?: string; poolId?: string; pretty_id?: string; prettyId?: string; release_reason?: string; releaseReason?: string; released_at_ms?: number; releasedAtMs?: number; source?: string; status?: string; updated_at_ms?: number; updatedAtMs?: number; updated_by_admin_id?: EntityId; updatedByAdminId?: EntityId; } interface RawPrettyDisplayIDOperator extends RawAppUserBrief { account?: string; admin_id?: EntityId; adminId?: EntityId; name?: string; source?: string; type?: string; } interface RawPrettyDisplayIDGenerationBatch { app_code?: string; appCode?: string; batch_id?: string; batchId?: string; created_at_ms?: number; createdAtMs?: number; generated_count?: number; generatedCount?: number; operator_admin_id?: EntityId; operatorAdminId?: EntityId; pool_id?: string; poolId?: string; request_id?: string; requestId?: string; requested_count?: number; requestedCount?: number; rule_config_json?: string; ruleConfigJson?: string; rule_type?: string; ruleType?: string; skipped_conflict_count?: number; skippedConflictCount?: number; status?: string; updated_at_ms?: number; updatedAtMs?: number; } interface RawAdminGrantPrettyDisplayIDResult { identity?: { default_display_user_id?: string; defaultDisplayUserId?: string; display_user_id?: string; displayUserId?: string; pretty_display_user_id?: string; prettyDisplayUserId?: string; pretty_id?: string; prettyId?: string; user_id?: EntityId; userId?: EntityId; }; lease_id?: string; leaseId?: string; pretty_id?: string; prettyId?: string; } // 分页结构也做一次兼容,page_size 来自 Go 响应,pageSize 来自前端 ApiPage 约定。 function normalizePage( data: ApiPage & { page_size?: number }, normalize: (item: TRaw) => TItem, ): ApiPage { return { items: (data.items || []).map(normalize), page: Number(data.page || 1), pageSize: Number(data.pageSize || data.page_size || 20), total: Number(data.total || 0), }; } function normalizeAppUser(item: RawAppUser = {}): AppUserDto { return { age: optionalNumberValue(item.age), avatar: stringValue(item.avatar), balances: (item.balances || []).map(normalizeAppUserAssetBalance), ban: normalizeAppUserBan(item.ban), birth: stringValue(item.birth), coin: numberValue(item.coin), country: stringValue(item.country), countryDisplayName: stringValue(item.countryDisplayName ?? item.country_display_name), countryName: stringValue(item.countryName ?? item.country_name), createdAtMs: numberValue(item.createdAtMs ?? item.created_at_ms), defaultDisplayUserId: stringValue(item.defaultDisplayUserId ?? item.default_display_user_id), diamond: numberValue(item.diamond), displayUserId: stringValue(item.displayUserId ?? item.display_user_id), equippedResources: (item.equippedResources ?? item.equipped_resources ?? []).map(normalizeAppUserResource), gender: stringValue(item.gender), lastActiveAtMs: numberValue(item.lastActiveAtMs ?? item.last_active_at_ms), lastLoginAtMs: numberValue(item.lastLoginAtMs ?? item.last_login_at_ms), lastOperatedAtMs: numberValue(item.lastOperatedAtMs ?? item.last_operated_at_ms), lastOperator: normalizeAppUserOperator(item.lastOperator ?? item.last_operator), levels: normalizeAppUserLevels(item.levels), prettyDisplayUserId: stringValue(item.prettyDisplayUserId ?? item.pretty_display_user_id), prettyId: stringValue(item.prettyId ?? item.pretty_id), regionId: numberValue(item.regionId ?? item.region_id), regionName: stringValue(item.regionName ?? item.region_name), registerDevice: stringValue(item.registerDevice ?? item.register_device), resources: (item.resources || []).map(normalizeAppUserResource), roles: Array.isArray(item.roles) ? item.roles.map(stringValue).filter(Boolean) : [], status: stringValue(item.status), updatedAtMs: numberValue(item.updatedAtMs ?? item.updated_at_ms), userId: stringValue(item.userId ?? item.user_id), username: stringValue(item.username), vip: normalizeAppUserVIP(item.vip), cumulativeRechargeUsdMinor: numberValue( item.cumulativeRechargeUsdMinor ?? item.cumulative_recharge_usd_minor, ), }; } function normalizeAppUserLevels(item: RawAppUserLevels = {}) { return { charm: normalizeAppUserLevel(item.charm, "charm"), game: normalizeAppUserLevel(item.game, "game"), wealth: normalizeAppUserLevel(item.wealth, "wealth"), }; } function normalizeAppUserLevel(item: RawAppUserLevel = {}, fallbackTrack: string) { return { displayLevel: numberValue(item.displayLevel ?? item.display_level), displayValue: numberValue(item.displayValue ?? item.display_value), expiresAtMs: numberValue(item.expiresAtMs ?? item.expires_at_ms), realLevel: numberValue(item.realLevel ?? item.real_level), realTotalValue: numberValue(item.realTotalValue ?? item.real_total_value), startedAtMs: numberValue(item.startedAtMs ?? item.started_at_ms), temporaryLevelId: stringValue(item.temporaryLevelId ?? item.temporary_level_id), temporaryTargetLevel: numberValue(item.temporaryTargetLevel ?? item.temporary_target_level), track: stringValue(item.track || fallbackTrack), }; } function normalizeAppUserBan(item: RawAppUserBan = {}) { return { active: Boolean(item.active), expiresAtMs: numberValue(item.expiresAtMs ?? item.expires_at_ms), id: stringValue(item.id), permanent: Boolean(item.permanent), reason: stringValue(item.reason), source: stringValue(item.source), }; } function normalizeAppUserOperator(item?: RawAppUserOperator) { if (!item) { return undefined; } return { action: stringValue(item.action), adminId: stringValue(item.adminId ?? item.admin_id), name: stringValue(item.name), }; } function normalizeAppUserAssetBalance(item: RawAppUserAssetBalance = {}) { const availableAmount = numberValue(item.availableAmount ?? item.available_amount); const frozenAmount = numberValue(item.frozenAmount ?? item.frozen_amount); return { assetType: stringValue(item.assetType ?? item.asset_type).toUpperCase(), availableAmount, frozenAmount, totalAmount: numberValue(item.totalAmount ?? item.total_amount ?? availableAmount + frozenAmount), updatedAtMs: numberValue(item.updatedAtMs ?? item.updated_at_ms), version: numberValue(item.version), }; } function normalizeAppUserVIP(item: RawAppUserVIP = {}) { return { active: Boolean(item.active), expiresAtMs: numberValue(item.expiresAtMs ?? item.expires_at_ms), level: numberValue(item.level), name: stringValue(item.name), startedAtMs: numberValue(item.startedAtMs ?? item.started_at_ms), updatedAtMs: numberValue(item.updatedAtMs ?? item.updated_at_ms), }; } function normalizeAppUserResource(item: RawAppUserResource = {}) { return { animationUrl: stringValue(item.animationUrl ?? item.animation_url), assetUrl: stringValue(item.assetUrl ?? item.asset_url), createdAtMs: numberValue(item.createdAtMs ?? item.created_at_ms), effectiveAtMs: numberValue(item.effectiveAtMs ?? item.effective_at_ms), entitlementId: stringValue(item.entitlementId ?? item.entitlement_id), equipped: Boolean(item.equipped), expiresAtMs: numberValue(item.expiresAtMs ?? item.expires_at_ms), name: stringValue(item.name), previewUrl: stringValue(item.previewUrl ?? item.preview_url), quantity: numberValue(item.quantity), remainingQuantity: numberValue(item.remainingQuantity ?? item.remaining_quantity), resourceCode: stringValue(item.resourceCode ?? item.resource_code), resourceId: numberValue(item.resourceId ?? item.resource_id), resourceType: stringValue(item.resourceType ?? item.resource_type), sourceGrantId: stringValue(item.sourceGrantId ?? item.source_grant_id), status: stringValue(item.status), updatedAtMs: numberValue(item.updatedAtMs ?? item.updated_at_ms), }; } function normalizeAppUserBrief(item: RawAppUserBrief = {}) { const user = normalizeAppUser(item); return { avatar: user.avatar, defaultDisplayUserId: user.defaultDisplayUserId, displayUserId: user.displayUserId, prettyDisplayUserId: user.prettyDisplayUserId, prettyId: user.prettyId, userId: user.userId, username: user.username, }; } function normalizeAppUserBanOperator(item?: RawAppUserBanOperator | null) { if (!item) { return undefined; } const user = normalizeAppUserBrief(item); return { ...user, account: stringValue(item.account), adminId: stringValue(item.adminId ?? item.admin_id), name: stringValue(item.name), type: stringValue(item.type || "system"), }; } function normalizePrettyDisplayIDOperator(item?: RawPrettyDisplayIDOperator | null, fallbackAdminId?: EntityId) { if (!item) { const adminId = stringValue(fallbackAdminId); return adminId ? { adminId, name: "后台管理员", type: "admin", } : undefined; } const user = normalizeAppUserBrief(item); const adminId = stringValue(item.adminId ?? item.admin_id ?? fallbackAdminId); return { ...user, account: stringValue(item.account), adminId, name: stringValue(item.name), source: stringValue(item.source), type: stringValue(item.type || (adminId ? "admin" : "app_user")), }; } function normalizeOptionalAppUserBrief(item?: RawAppUserBrief | null) { if (!item) { return undefined; } const user = normalizeAppUserBrief(item); return nonZeroStringValue(user.userId) || nonZeroStringValue(user.displayUserId) || user.username ? user : undefined; } function normalizeAppUserBanRecord(item: RawAppUserBanRecord = {}): AppUserBanRecordDto { return { createdAtMs: numberValue(item.createdAtMs ?? item.created_at_ms), id: numberValue(item.id), newStatus: stringValue(item.newStatus ?? item.new_status), oldStatus: stringValue(item.oldStatus ?? item.old_status), operator: normalizeAppUserBanOperator(item.operator), reason: stringValue(item.reason), requestId: stringValue(item.requestId ?? item.request_id), target: normalizeAppUserBrief(item.target || {}), }; } function normalizeAppUserLoginLog(item: RawAppUserLoginLog = {}): AppUserLoginLogDto { const user = normalizeAppUserBrief(item); return { ...user, blockReason: stringValue(item.blockReason ?? item.block_reason), blocked: Boolean(item.blocked), channel: stringValue(item.channel), country: stringValue(item.country), createdAtMs: numberValue(item.createdAtMs ?? item.created_at_ms), failureCode: stringValue(item.failureCode ?? item.failure_code), id: numberValue(item.id), ipCountryCode: stringValue(item.ipCountryCode ?? item.ip_country_code), loginIp: stringValue(item.loginIp ?? item.login_ip), loginType: stringValue(item.loginType ?? item.login_type), platform: stringValue(item.platform), provider: stringValue(item.provider), regionId: numberValue(item.regionId ?? item.region_id), regionName: stringValue(item.regionName ?? item.region_name), requestId: stringValue(item.requestId ?? item.request_id), result: stringValue(item.result), riskHighlighted: Boolean(item.riskHighlighted ?? item.risk_highlighted), }; } // 池 DTO 在这里完成字段名和数值兜底,页面表格不再关心后端是否省略可选字段。 function normalizePrettyDisplayIDPool(item: RawPrettyDisplayIDPool): PrettyDisplayIDPoolDto { return { appCode: item.appCode ?? item.app_code ?? "", createdAtMs: item.createdAtMs ?? item.created_at_ms ?? 0, createdByAdminId: item.createdByAdminId ?? item.created_by_admin_id ?? "", levelTrack: item.levelTrack ?? item.level_track ?? "", maxLevel: Number(item.maxLevel ?? item.max_level ?? 0), minLevel: Number(item.minLevel ?? item.min_level ?? 0), name: item.name ?? "", poolId: item.poolId ?? item.pool_id ?? "", ruleConfigJson: item.ruleConfigJson ?? item.rule_config_json ?? "", ruleType: item.ruleType ?? item.rule_type ?? "", sortOrder: Number(item.sortOrder ?? item.sort_order ?? 0), status: item.status ?? "", updatedAtMs: item.updatedAtMs ?? item.updated_at_ms ?? 0, updatedByAdminId: item.updatedByAdminId ?? item.updated_by_admin_id ?? "", }; } function stringValue(value: unknown): string { return value === undefined || value === null ? "" : String(value).trim(); } function numberValue(value: unknown): number { const parsed = Number(value || 0); return Number.isFinite(parsed) ? parsed : 0; } function optionalNumberValue(value: unknown): number | undefined { if (value === undefined || value === null || value === "") { return undefined; } const parsed = Number(value); return Number.isFinite(parsed) ? parsed : undefined; } // 靓号明细包含池号和后台发放两类来源,pool 可能为空,归一化后页面只判断 source/status 即可。 function normalizePrettyDisplayID(item: RawPrettyDisplayID): PrettyDisplayIDDto { const createdByAdminId = item.createdByAdminId ?? item.created_by_admin_id ?? ""; const updatedByAdminId = item.updatedByAdminId ?? item.updated_by_admin_id ?? ""; return { appCode: item.appCode ?? item.app_code ?? "", assignedAtMs: item.assignedAtMs ?? item.assigned_at_ms ?? 0, assignedLeaseId: item.assignedLeaseId ?? item.assigned_lease_id ?? "", assignedUser: normalizeOptionalAppUserBrief(item.assignedUser ?? item.assigned_user), assignedUserId: optionalEntityID(item.assignedUserId ?? item.assigned_user_id), createdAtMs: item.createdAtMs ?? item.created_at_ms ?? 0, createdByAdminId, displayUserId: item.displayUserId ?? item.display_user_id ?? "", generatedBatchId: item.generatedBatchId ?? item.generated_batch_id ?? "", operator: normalizePrettyDisplayIDOperator(item.operator, updatedByAdminId || createdByAdminId), pool: item.pool ? normalizePrettyDisplayIDPool(item.pool) : null, poolId: item.poolId ?? item.pool_id ?? "", prettyId: item.prettyId ?? item.pretty_id ?? "", releaseReason: item.releaseReason ?? item.release_reason ?? "", releasedAtMs: item.releasedAtMs ?? item.released_at_ms ?? 0, source: item.source ?? "", status: item.status ?? "", updatedAtMs: item.updatedAtMs ?? item.updated_at_ms ?? 0, updatedByAdminId, }; } // 生成结果用于展示本次批量生成的真实落库数量和冲突跳过数量,数值统一转换,避免字符串数字影响统计。 function normalizePrettyDisplayIDGenerationBatch( item: RawPrettyDisplayIDGenerationBatch, ): PrettyDisplayIDGenerationBatchDto { return { appCode: item.appCode ?? item.app_code ?? "", batchId: item.batchId ?? item.batch_id ?? "", createdAtMs: item.createdAtMs ?? item.created_at_ms ?? 0, generatedCount: Number(item.generatedCount ?? item.generated_count ?? 0), operatorAdminId: item.operatorAdminId ?? item.operator_admin_id ?? "", poolId: item.poolId ?? item.pool_id ?? "", requestId: item.requestId ?? item.request_id ?? "", requestedCount: Number(item.requestedCount ?? item.requested_count ?? 0), ruleConfigJson: item.ruleConfigJson ?? item.rule_config_json ?? "", ruleType: item.ruleType ?? item.rule_type ?? "", skippedConflictCount: Number(item.skippedConflictCount ?? item.skipped_conflict_count ?? 0), status: item.status ?? "", updatedAtMs: item.updatedAtMs ?? item.updated_at_ms ?? 0, }; } // 后台发放返回的是新 lease 和用户当前身份快照,identity 归一化后能直接用于测试和后续结果提示。 function normalizeAdminGrantPrettyDisplayIDResult( item: RawAdminGrantPrettyDisplayIDResult, ): AdminGrantPrettyDisplayIDResultDto { const identity = item.identity || {}; return { identity: { defaultDisplayUserId: identity.defaultDisplayUserId ?? identity.default_display_user_id ?? "", displayUserId: identity.displayUserId ?? identity.display_user_id ?? "", prettyDisplayUserId: identity.prettyDisplayUserId ?? identity.pretty_display_user_id ?? "", prettyId: identity.prettyId ?? identity.pretty_id ?? "", userId: String(identity.userId ?? identity.user_id ?? ""), }, leaseId: item.leaseId ?? item.lease_id ?? "", prettyId: item.prettyId ?? item.pretty_id ?? "", }; } function optionalEntityID(value: unknown): string { return nonZeroStringValue(value); } function nonZeroStringValue(value: unknown): string { const normalized = stringValue(value); return normalized === "0" ? "" : normalized; }