941 lines
33 KiB
TypeScript
941 lines
33 KiB
TypeScript
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<ApiPage<AppUserDto>> {
|
||
const endpoint = API_ENDPOINTS.appListUsers;
|
||
const data = await apiRequest<ApiPage<RawAppUser> & { page_size?: number }>(
|
||
apiEndpointPath(API_OPERATIONS.appListUsers),
|
||
{
|
||
method: endpoint.method,
|
||
query,
|
||
},
|
||
);
|
||
return normalizePage(data, normalizeAppUser);
|
||
}
|
||
|
||
export async function getAppUser(userId: EntityId): Promise<AppUserDto> {
|
||
const endpoint = API_ENDPOINTS.appGetUser;
|
||
const data = await apiRequest<RawAppUser>(apiEndpointPath(API_OPERATIONS.appGetUser, { id: userId }), {
|
||
method: endpoint.method,
|
||
});
|
||
return normalizeAppUser(data);
|
||
}
|
||
|
||
export async function listAppUserLoginLogs(query: PageQuery = {}): Promise<ApiPage<AppUserLoginLogDto>> {
|
||
const endpoint = API_ENDPOINTS.appListLoginLogs;
|
||
const data = await apiRequest<ApiPage<RawAppUserLoginLog> & { page_size?: number }>(
|
||
apiEndpointPath(API_OPERATIONS.appListLoginLogs),
|
||
{
|
||
method: endpoint.method,
|
||
query,
|
||
},
|
||
);
|
||
return normalizePage(data, normalizeAppUserLoginLog);
|
||
}
|
||
|
||
export async function listAppUserBans(query: PageQuery = {}): Promise<ApiPage<AppUserBanRecordDto>> {
|
||
const endpoint = API_ENDPOINTS.appListBannedUsers;
|
||
const data = await apiRequest<ApiPage<RawAppUserBanRecord> & { page_size?: number }>(
|
||
apiEndpointPath(API_OPERATIONS.appListBannedUsers),
|
||
{
|
||
method: endpoint.method,
|
||
query,
|
||
},
|
||
);
|
||
return normalizePage(data, normalizeAppUserBanRecord);
|
||
}
|
||
|
||
export async function updateAppUser(userId: EntityId, payload: AppUserUpdatePayload): Promise<AppUserDto> {
|
||
const endpoint = API_ENDPOINTS.appUpdateUser;
|
||
const data = await apiRequest<RawAppUser, AppUserUpdatePayload>(
|
||
apiEndpointPath(API_OPERATIONS.appUpdateUser, { id: userId }),
|
||
{
|
||
body: payload,
|
||
method: endpoint.method,
|
||
},
|
||
);
|
||
return normalizeAppUser(data);
|
||
}
|
||
|
||
export async function adjustAppUserLevels(
|
||
userId: EntityId,
|
||
payload: AppUserLevelAdjustmentPayload,
|
||
): Promise<AppUserDto> {
|
||
const endpoint = API_ENDPOINTS.appAdjustUserLevels;
|
||
const data = await apiRequest<RawAppUser, AppUserLevelAdjustmentPayload>(
|
||
apiEndpointPath(API_OPERATIONS.appAdjustUserLevels, { id: userId }),
|
||
{ body: payload, method: endpoint.method },
|
||
);
|
||
return normalizeAppUser(data);
|
||
}
|
||
|
||
export async function banAppUser(userId: EntityId, payload: AppUserBanPayload): Promise<AppUserDto> {
|
||
const endpoint = API_ENDPOINTS.appBanUser;
|
||
const data = await apiRequest<RawAppUserStatusMutation, AppUserBanPayload>(
|
||
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<AppUserDto> {
|
||
const endpoint = API_ENDPOINTS.appUnbanUser;
|
||
const data = await apiRequest<RawAppUserStatusMutation, AppUserUnbanPayload>(
|
||
apiEndpointPath(API_OPERATIONS.appUnbanUser, { id: userId }),
|
||
{ body: payload, method: endpoint.method },
|
||
);
|
||
return normalizeAppUser(data.user ?? data);
|
||
}
|
||
|
||
export function createAppUserExport(query: PageQuery = {}): Promise<AppUserExportJobDto> {
|
||
const endpoint = API_ENDPOINTS.appExportUsers;
|
||
return apiRequest<AppUserExportJobDto>(apiEndpointPath(API_OPERATIONS.appExportUsers), {
|
||
method: endpoint.method,
|
||
query,
|
||
});
|
||
}
|
||
|
||
export function getAppUserExportJob(jobId: EntityId): Promise<AdminJobDto> {
|
||
const endpoint = API_ENDPOINTS.getJob;
|
||
return apiRequest<AdminJobDto>(apiEndpointPath(API_OPERATIONS.getJob, { id: jobId }), {
|
||
method: endpoint.method,
|
||
});
|
||
}
|
||
|
||
export function downloadAppUserExport(jobId: EntityId): Promise<Response> {
|
||
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<ApiPage<PrettyDisplayIDPoolDto>> {
|
||
const endpoint = API_ENDPOINTS.listPrettyIdPools;
|
||
const data = await apiRequest<ApiPage<RawPrettyDisplayIDPool>>(apiEndpointPath(API_OPERATIONS.listPrettyIdPools), {
|
||
method: endpoint.method,
|
||
query,
|
||
});
|
||
return normalizePage(data, normalizePrettyDisplayIDPool);
|
||
}
|
||
|
||
export async function createPrettyDisplayIDPool(payload: PrettyDisplayIDPoolPayload): Promise<PrettyDisplayIDPoolDto> {
|
||
const endpoint = API_ENDPOINTS.createPrettyIdPool;
|
||
const data = await apiRequest<RawPrettyDisplayIDPool, PrettyDisplayIDPoolPayload>(
|
||
apiEndpointPath(API_OPERATIONS.createPrettyIdPool),
|
||
{
|
||
body: payload,
|
||
method: endpoint.method,
|
||
},
|
||
);
|
||
return normalizePrettyDisplayIDPool(data);
|
||
}
|
||
|
||
export async function updatePrettyDisplayIDPool(
|
||
poolId: EntityId,
|
||
payload: PrettyDisplayIDPoolPayload,
|
||
): Promise<PrettyDisplayIDPoolDto> {
|
||
const endpoint = API_ENDPOINTS.updatePrettyIdPool;
|
||
const data = await apiRequest<RawPrettyDisplayIDPool, PrettyDisplayIDPoolPayload>(
|
||
apiEndpointPath(API_OPERATIONS.updatePrettyIdPool, { pool_id: poolId }),
|
||
{
|
||
body: payload,
|
||
method: endpoint.method,
|
||
},
|
||
);
|
||
return normalizePrettyDisplayIDPool(data);
|
||
}
|
||
|
||
export async function generatePrettyDisplayIDs(
|
||
poolId: EntityId,
|
||
payload: GeneratePrettyDisplayIDsPayload,
|
||
): Promise<PrettyDisplayIDGenerationBatchDto> {
|
||
const endpoint = API_ENDPOINTS.generatePrettyIds;
|
||
const data = await apiRequest<RawPrettyDisplayIDGenerationBatch, GeneratePrettyDisplayIDsPayload>(
|
||
apiEndpointPath(API_OPERATIONS.generatePrettyIds, { pool_id: poolId }),
|
||
{
|
||
body: payload,
|
||
method: endpoint.method,
|
||
},
|
||
);
|
||
return normalizePrettyDisplayIDGenerationBatch(data);
|
||
}
|
||
|
||
export async function listPrettyDisplayIDs(query: PageQuery = {}): Promise<ApiPage<PrettyDisplayIDDto>> {
|
||
const endpoint = API_ENDPOINTS.listPrettyIds;
|
||
const data = await apiRequest<ApiPage<RawPrettyDisplayID>>(apiEndpointPath(API_OPERATIONS.listPrettyIds), {
|
||
method: endpoint.method,
|
||
query,
|
||
});
|
||
return normalizePage(data, normalizePrettyDisplayID);
|
||
}
|
||
|
||
export async function grantPrettyDisplayID(
|
||
payload: GrantPrettyDisplayIDPayload,
|
||
): Promise<AdminGrantPrettyDisplayIDResultDto> {
|
||
const endpoint = API_ENDPOINTS.grantPrettyId;
|
||
const data = await apiRequest<RawAdminGrantPrettyDisplayIDResult, GrantPrettyDisplayIDPayload>(
|
||
apiEndpointPath(API_OPERATIONS.grantPrettyId),
|
||
{
|
||
body: payload,
|
||
method: endpoint.method,
|
||
},
|
||
);
|
||
return normalizeAdminGrantPrettyDisplayIDResult(data);
|
||
}
|
||
|
||
export async function recyclePrettyDisplayID(
|
||
prettyId: EntityId,
|
||
payload: RecyclePrettyDisplayIDPayload = {},
|
||
): Promise<PrettyDisplayIDDto> {
|
||
const endpoint = API_ENDPOINTS.recyclePrettyId;
|
||
const data = await apiRequest<RawPrettyDisplayID, RecyclePrettyDisplayIDPayload>(
|
||
apiEndpointPath(API_OPERATIONS.recyclePrettyId, { pretty_id: prettyId }),
|
||
{
|
||
body: payload,
|
||
method: endpoint.method,
|
||
},
|
||
);
|
||
return normalizePrettyDisplayID(data);
|
||
}
|
||
|
||
export async function setPrettyDisplayIDStatus(
|
||
prettyId: EntityId,
|
||
payload: SetPrettyDisplayIDStatusPayload,
|
||
): Promise<PrettyDisplayIDDto> {
|
||
const endpoint = API_ENDPOINTS.setPrettyIdStatus;
|
||
const data = await apiRequest<RawPrettyDisplayID, SetPrettyDisplayIDStatusPayload>(
|
||
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<TRaw, TItem>(
|
||
data: ApiPage<TRaw> & { page_size?: number },
|
||
normalize: (item: TRaw) => TItem,
|
||
): ApiPage<TItem> {
|
||
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;
|
||
}
|