优化:历史数据接口对接

This commit is contained in:
ivan 2026-03-15 21:03:33 +08:00
parent d44e7157e8
commit 9dd61be92f
3 changed files with 288 additions and 3 deletions

185
src/api/historyRecord.ts Normal file
View File

@ -0,0 +1,185 @@
/**
* getHistoryRecordListClientgetHistoryRecordPublic
* Wallet.vue Tab
*/
import { buildQuery, get } from './request'
import type { PageResult } from './types'
/** 单条历史记录(与 doc.json definitions["polymarket.HistoryRecord"] 对齐) */
export interface HistoryRecordItem {
ID?: number
asset?: string
bio?: string
conditionId?: string
createdAt?: string
eventSlug?: string
icon?: string
name?: string
outcome?: string
outcomeIndex?: number
price?: number
profileImage?: string
profileImageOptimized?: string
proxyWallet?: string
pseudonym?: string
side?: string
size?: number
slug?: string
timestamp?: number
title?: string
transactionHash?: string
type?: string
updatedAt?: string
}
/** GET /hr/getHistoryRecordPublic 请求参数 */
export interface GetHistoryRecordPublicParams {
page?: number
pageSize?: number
keyword?: string
slug?: string
title?: string
name?: string
bio?: string
createdAtRange?: string[]
}
/** GET /hr/getHistoryRecordListClient 请求参数(客户端分页,需鉴权) */
export interface GetHistoryRecordListClientParams {
page?: number
pageSize?: number
userId?: number
keyword?: string
slug?: string
title?: string
name?: string
bio?: string
createdAtRange?: string[]
}
/** 响应 data 可能为分页或数组 */
export interface HistoryRecordPublicResponse {
code: number
data?: PageResult<HistoryRecordItem> | HistoryRecordItem[]
msg: string
}
/** getHistoryRecordListClient 响应 data 为 PageResult */
export interface HistoryRecordListClientResponse {
code: number
data?: PageResult<HistoryRecordItem>
msg: string
}
/**
* x-tokenx-user-id
* GET /hr/getHistoryRecordListClient
*/
export async function getHistoryRecordListClient(
params: GetHistoryRecordListClientParams = {},
config?: { headers?: Record<string, string> },
): Promise<HistoryRecordListClientResponse> {
const query = buildQuery({
page: params.page,
pageSize: params.pageSize,
userId: params.userId,
keyword: params.keyword,
slug: params.slug,
title: params.title,
name: params.name,
bio: params.bio,
createdAtRange: params.createdAtRange,
})
return get<HistoryRecordListClientResponse>('/hr/getHistoryRecordListClient', query, config)
}
/**
*
* GET /hr/getHistoryRecordPublic
*/
export async function getHistoryRecordPublic(
params: GetHistoryRecordPublicParams = {},
): Promise<HistoryRecordPublicResponse> {
const query = buildQuery({
page: params.page,
pageSize: params.pageSize,
keyword: params.keyword,
slug: params.slug,
title: params.title,
name: params.name,
bio: params.bio,
createdAtRange: params.createdAtRange,
})
return get<HistoryRecordPublicResponse>('/hr/getHistoryRecordPublic', query)
}
/** 钱包 History 展示项(与 Wallet.vue HistoryItem / order.HistoryDisplayItem 一致) */
export interface HistoryDisplayItem {
id: string
market: string
side: 'Yes' | 'No'
activity: string
value: string
activityDetail?: string
profitLoss?: string
profitLossNegative?: boolean
timeAgo?: string
avgPrice?: string
shares?: string
iconChar?: string
iconClass?: string
}
function formatTimeAgo(createdAt: string | undefined, timestamp?: number): string {
const ms = createdAt ? new Date(createdAt).getTime() : (timestamp != null ? timestamp * 1000 : 0)
if (!ms) return ''
const diff = Date.now() - ms
if (diff < 60000) return 'Just now'
if (diff < 3600000) return `${Math.floor(diff / 60000)} minutes ago`
if (diff < 86400000) return `${Math.floor(diff / 3600000)} hours ago`
if (diff < 604800000) return `${Math.floor(diff / 86400000)} days ago`
return new Date(ms).toLocaleDateString()
}
/**
* HistoryRecordItem History
*/
export function mapHistoryRecordToDisplayItem(record: HistoryRecordItem): HistoryDisplayItem {
const id = String(record.ID ?? '')
const market = record.title ?? record.name ?? record.eventSlug ?? ''
const outcome = (record.outcome ?? record.side ?? 'Yes').toString()
const side = outcome === 'No' || outcome === 'Down' ? 'No' : 'Yes'
const typeLabel = record.type ?? 'Trade'
const activity = `${typeLabel} ${outcome}`.trim()
const price = record.price ?? 0
const size = record.size ?? 0
const valueUsd = price * size
const value = `$${Math.abs(valueUsd).toFixed(2)}`
const priceCents = Math.round(price * 100)
const activityDetail = size > 0 ? `Sold ${Math.floor(size)} ${outcome} at ${priceCents}¢` : value
const timeAgo = formatTimeAgo(record.createdAt, record.timestamp)
return {
id,
market,
side,
activity,
value,
activityDetail,
profitLoss: value,
profitLossNegative: valueUsd < 0,
timeAgo,
avgPrice: priceCents ? `${priceCents}¢` : undefined,
shares: size > 0 ? String(Math.floor(size)) : undefined,
}
}
/** 从响应中取出 list 并映射为展示项,同时返回 total数组时为 length */
export function getHistoryRecordList(
data: HistoryRecordPublicResponse['data'],
): { list: HistoryDisplayItem[]; total: number } {
if (!data) return { list: [], total: 0 }
const list = Array.isArray(data) ? data : (data as PageResult<HistoryRecordItem>).list ?? []
const total = Array.isArray(data) ? data.length : (data as PageResult<HistoryRecordItem>).total ?? list.length
return { list: list.map(mapHistoryRecordToDisplayItem), total }
}

View File

@ -16,6 +16,8 @@ export interface ClobPositionMarket {
outcomes?: string[] outcomes?: string[]
outcomePrices?: string[] | number[] outcomePrices?: string[] | number[]
clobTokenIds?: string[] clobTokenIds?: string[]
/** 市场是否已关闭closed=true 表示可结算/可领取 */
closed?: boolean
[key: string]: unknown [key: string]: unknown
} }
@ -162,8 +164,8 @@ export interface PositionDisplayItem {
marketID?: string marketID?: string
/** Token ID用于 claimPositionAPI 返回 tokenId */ /** Token ID用于 claimPositionAPI 返回 tokenId */
tokenID?: string tokenID?: string
/** 是否待领取/未结算needClaim 为 true 时显示领取按钮) */ /** 所属市场是否已关闭market.closed=true 表示可结算/可领取 */
needClaim?: boolean marketClosed?: boolean
} }
/** /**
@ -230,6 +232,7 @@ export function mapPositionToDisplayItem(pos: ClobPositionItem): PositionDisplay
const marketID = String(pos.marketID ?? pos.market?.ID ?? '') const marketID = String(pos.marketID ?? pos.market?.ID ?? '')
const tokenID = pos.tokenId ?? (pos as { tokenID?: string }).tokenID ?? '' const tokenID = pos.tokenId ?? (pos as { tokenID?: string }).tokenID ?? ''
const imageUrl = (pos.market?.image ?? pos.market?.icon) as string | undefined const imageUrl = (pos.market?.image ?? pos.market?.icon) as string | undefined
const marketClosed = pos.market?.closed === true
return { return {
id, id,
@ -252,6 +255,6 @@ export function mapPositionToDisplayItem(pos: ClobPositionItem): PositionDisplay
availableSharesNum: availableNum >= 0 ? availableNum : undefined, availableSharesNum: availableNum >= 0 ? availableNum : undefined,
marketID: marketID || undefined, marketID: marketID || undefined,
tokenID: tokenID || undefined, tokenID: tokenID || undefined,
needClaim: pos.needClaim, marketClosed,
} }
} }

97
src/api/priceHistory.ts Normal file
View File

@ -0,0 +1,97 @@
/**
* GET /pmPriceHistory/getPmPriceHistoryPublic
* TradeDetail.vue 线
*/
import { buildQuery, get } from './request'
import type { ApiResponse } from './types'
import type { PageResult } from './types'
/** 单条价格历史(与 doc.json definitions["polymarket.PmPriceHistory"] 对齐) */
export interface PmPriceHistoryItem {
ID?: number
createdAt?: string
fidelity?: number
interval?: string
/** 市场标识 */
market?: string
/** 价格(通常 01 小数,前端展示可乘 100 为百分比) */
price?: number
/** 时间戳Unix 秒) */
time?: number
updatedAt?: string
}
/** GET /pmPriceHistory/getPmPriceHistoryPublic 请求参数 */
export interface GetPmPriceHistoryPublicParams {
/** 市场标识(必填,用于筛选) */
market: string
page?: number
pageSize?: number
/** 数据间隔 */
interval?: string
/** 时间筛选 */
time?: number
/** 创建时间范围 */
createdAtRange?: string[]
fidelity?: number
keyword?: string
order?: string
sort?: string
price?: number
}
/** 响应 data 为 PageResult<PmPriceHistoryItem> */
export interface PmPriceHistoryPublicResponse {
code: number
data?: PageResult<PmPriceHistoryItem>
msg: string
}
/**
*
* GET /pmPriceHistory/getPmPriceHistoryPublic
*/
export async function getPmPriceHistoryPublic(
params: GetPmPriceHistoryPublicParams,
config?: { headers?: Record<string, string> },
): Promise<PmPriceHistoryPublicResponse> {
const { market, page = 1, pageSize = 500, interval, time, createdAtRange, fidelity, keyword, order, sort, price } = params
const query = buildQuery({
market,
page,
pageSize,
interval,
time,
createdAtRange,
fidelity,
keyword,
order,
sort,
price,
})
return get<PmPriceHistoryPublicResponse>('/pmPriceHistory/getPmPriceHistoryPublic', query, config)
}
/** 图表单点格式 [timestamp_ms, value_0_100] */
export type ChartDataPoint = [number, number]
/**
* list ECharts 线
* - time
* - price 1 100 0100
*/
export function priceHistoryToChartData(list: PmPriceHistoryItem[]): ChartDataPoint[] {
if (!list?.length) return []
const out: ChartDataPoint[] = []
for (const item of list) {
const t = item.time
const p = item.price
if (t == null || p == null || !Number.isFinite(Number(t))) continue
const tsMs = Number(t) < 1e12 ? Number(t) * 1000 : Number(t)
const value = Number(p) <= 1 ? Number(p) * 100 : Number(p)
out.push([tsMs, value])
}
out.sort((a, b) => a[0] - b[0])
return out
}