From fa5e0dccfe0ea32d09898a714ff5d09215217205 Mon Sep 17 00:00:00 2001 From: ivan Date: Sun, 15 Mar 2026 21:05:56 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=EF=BC=9A=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/api/event.md | 2 +- docs/api/historyRecord.md | 87 ++++++++++++++ docs/api/jwt.md | 27 +++++ docs/api/position.md | 8 +- docs/api/priceHistory.md | 69 +++++++++++ docs/components/OrderBook.md | 2 +- docs/stores/user.md | 2 +- docs/views/TradeDetail.md | 4 +- docs/views/Wallet.md | 5 +- src/api/event.ts | 4 + src/api/jwt.ts | 18 +++ src/components/TradeComponent.vue | 183 +++++++++++++++++++----------- src/stores/user.ts | 21 ++-- 13 files changed, 346 insertions(+), 86 deletions(-) create mode 100644 docs/api/historyRecord.md create mode 100644 docs/api/jwt.md create mode 100644 docs/api/priceHistory.md create mode 100644 src/api/jwt.ts diff --git a/docs/api/event.md b/docs/api/event.md index de0e52c..71ed8f1 100644 --- a/docs/api/event.md +++ b/docs/api/event.md @@ -8,7 +8,7 @@ Event(预测市场事件)相关接口与类型定义,对接 XTrader API ## 核心能力 -- `getPmEventPublic`:分页获取公开事件列表(无需鉴权) +- `getPmEventPublic`:分页获取公开事件列表(无需鉴权);请求时固定传入 **startDateMax**、**endDateMin** 为当前时间戳(Unix 秒),**startDateMin**、**endDateMax** 不传 - `findPmEvent`:按 id/slug 查询事件详情(需鉴权) - `mapEventItemToCard`:将 `PmEventListItem` 转为 `EventCardItem`(供 MarketCard 使用) - 内存缓存:`getEventListCache`、`setEventListCache`、`clearEventListCache`,用于列表切换页面时复用 diff --git a/docs/api/historyRecord.md b/docs/api/historyRecord.md new file mode 100644 index 0000000..22fd93b --- /dev/null +++ b/docs/api/historyRecord.md @@ -0,0 +1,87 @@ +# historyRecord.ts + +**路径**:`src/api/historyRecord.ts` + +## 功能用途 + +历史记录接口,用于 Wallet 页「History」Tab。**Wallet 使用 GET /hr/getHistoryRecordListClient**(需鉴权、按当前用户分页);另提供 GET /hr/getHistoryRecordPublic(不需要鉴权)备用。 + +## 核心能力 + +- `getHistoryRecordListClient`:客户端分页获取历史记录(需鉴权,传 userId、page、pageSize) +- `getHistoryRecordPublic`:分页获取历史记录(无需鉴权) +- `getHistoryRecordList`:从响应 data 中解析 list 并映射为 `HistoryDisplayItem[]`,同时返回 total +- `mapHistoryRecordToDisplayItem`:单条 `HistoryRecordItem` → 钱包展示项(与 Wallet.vue HistoryItem 一致) + +## GET /hr/getHistoryRecordListClient(Wallet 使用) + +需鉴权:x-token、x-user-id。 + +### 请求参数(Query) + +| 参数 | 类型 | 说明 | +|------|------|------| +| page | number | 页码 | +| pageSize | number | 每页条数 | +| userId | number | 用户 ID | +| keyword | string | 关键字 | +| slug | string | 标识 | +| title | string | 标题 | +| name | string | 名称 | +| bio | string | 简介 | +| createdAtRange | string[] | 创建时间范围 | + +### 响应 + +`{ code, data, msg }`,`data` 为 `PageResult`(list、page、pageSize、total)。 + +## GET /hr/getHistoryRecordPublic + +### 请求参数(Query,可选) + +| 参数 | 类型 | 说明 | +|------|------|------| +| page | number | 页码 | +| pageSize | number | 每页条数 | +| keyword | string | 关键字 | +| slug | string | 标识 | +| title | string | 标题 | +| name | string | 名称 | +| bio | string | 简介 | +| createdAtRange | string[] | 创建时间范围 | + +### 响应 + +`{ code, data, msg }`。`data` 可能为 `PageResult`(list、page、pageSize、total)或 `HistoryRecordItem[]`。 + +### HistoryRecordItem(与 doc.json polymarket.HistoryRecord 对齐) + +| 字段 | 类型 | 说明 | +|------|------|------| +| ID | number | 主键 | +| title | string | 标题 | +| name | string | 名称 | +| eventSlug | string | 事件标识 | +| outcome | string | 结果(Yes/No 等) | +| side | string | 方向 | +| type | string | 类型 | +| price | number | 价格 | +| size | number | 大小 | +| createdAt | string | 创建时间 | +| timestamp | number | 时间戳(秒) | +| 其他 | - | asset, bio, conditionId, icon, slug, transactionHash 等 | + +## 使用方式 + +```typescript +import { getHistoryRecordPublic, getHistoryRecordList } from '@/api/historyRecord' + +const res = await getHistoryRecordPublic({ page: 1, pageSize: 10 }) +const { list, total } = getHistoryRecordList(res.data) +// list 为 HistoryDisplayItem[],可直接用于 Wallet 历史列表 +``` + +## 扩展方式 + +- 若后端固定返回分页结构,可收紧 `HistoryRecordPublicResponse['data']` 类型为 `PageResult` +- 展示字段(market、activity、value、timeAgo 等)可在 `mapHistoryRecordToDisplayItem` 中按实际接口字段再调整 diff --git a/docs/api/jwt.md b/docs/api/jwt.md new file mode 100644 index 0000000..bf03c3d --- /dev/null +++ b/docs/api/jwt.md @@ -0,0 +1,27 @@ +# jwt.ts + +**路径**:`src/api/jwt.ts` + +## 功能用途 + +JWT 相关接口,用于退出登录时将当前 token 加入黑名单,使 token 失效。 + +## 核心能力 + +- `jsonInBlacklist`:POST /jwt/jsonInBlacklist,需鉴权(x-token、x-user-id) + +## POST /jwt/jsonInBlacklist + +### 请求 + +- 方法:POST +- 鉴权:需在 headers 中传 x-token、x-user-id +- Body:无 + +### 响应 + +`{ code, msg }`,标准 ApiResponse。 + +### 使用场景 + +退出登录时由 `useUserStore().logout()` 调用,在清空本地 token 前先请求该接口,使服务端将当前 JWT 加入黑名单。若请求失败(如网络错误、401),仍会执行本地登出。 diff --git a/docs/api/position.md b/docs/api/position.md index 7238776..fdb5850 100644 --- a/docs/api/position.md +++ b/docs/api/position.md @@ -8,8 +8,8 @@ ## 核心能力 -- `getPositionList`:分页获取持仓列表(需鉴权 x-token、x-user-id);返回项含 `needClaim`、`market`(内嵌市场 question、outcomes、outcomePrices) -- `mapPositionToDisplayItem`:将接口项转为展示结构;`market` 优先用 `market.question`,否则用 marketID;`avgNow` 有 `market.outcomePrices` 时展示「AVG → NOW」格式;`iconChar`/`iconClass`/`imageUrl` 用于展示图标(market.image 优先) +- `getPositionList`:分页获取持仓列表(需鉴权 x-token、x-user-id);返回项含 `market`(内嵌市场 question、outcomes、outcomePrices、**closed**) +- `mapPositionToDisplayItem`:将接口项转为展示结构;`market` 优先用 `market.question`,否则用 marketID;`avgNow` 有 `market.outcomePrices` 时展示「AVG → NOW」格式;`iconChar`/`iconClass`/`imageUrl` 用于展示图标(market.image 优先);**marketClosed** 取自 `market.closed`,用于钱包侧判断可结算/可领取 ## GET /clob/position/getPositionList @@ -43,8 +43,7 @@ | cost | string | 成本 | | outcome | string | 方向 | | version | number | 版本号 | -| needClaim | boolean | 是否待领取结算 | -| market | ClobPositionMarket | 内嵌市场详情(question、outcomes、outcomePrices 等) | +| market | ClobPositionMarket | 内嵌市场详情(question、outcomes、outcomePrices、closed 等) | | createdAt | string | 创建时间 | | updatedAt | string | 更新时间 | @@ -60,6 +59,7 @@ | outcomes | string[] | 选项(如 ["Up", "Down"]) | | outcomePrices | string[] \| number[] | 各选项当前价格 | | clobTokenIds | string[] | CLOB Token ID 列表 | +| closed | boolean | 市场是否已关闭,true 表示可结算/可领取 | ## 使用方式 diff --git a/docs/api/priceHistory.md b/docs/api/priceHistory.md new file mode 100644 index 0000000..b8a5070 --- /dev/null +++ b/docs/api/priceHistory.md @@ -0,0 +1,69 @@ +# priceHistory.ts + +**路径**:`src/api/priceHistory.ts` + +## 功能用途 + +价格历史公开接口,用于 TradeDetail 页面的 Yes/No 折线图。对接 **GET /pmPriceHistory/getPmPriceHistoryPublic**(无需鉴权)。 + +## 核心能力 + +- `getPmPriceHistoryPublic`:按市场 ID 分页获取价格历史 +- `priceHistoryToChartData`:将接口返回的 `list` 转为 ECharts 使用的 `[timestamp_ms, value_0_100][]` + +## GET /pmPriceHistory/getPmPriceHistoryPublic + +### 请求参数(Query) + +| 参数 | 类型 | 必填 | 说明 | +|------|------|------|------| +| market | string | 是 | 传 YES 对应的 clobTokenId(即当前市场 clobTokenIds[0]) | +| page | number | 否 | 页码,默认 1 | +| pageSize | number | 否 | 每页条数,默认 500 | +| interval | string | 否 | 数据间隔 | +| time | number | 否 | 时间筛选 | +| createdAtRange | string[] | 否 | 创建时间范围 | +| fidelity | number | 否 | 数据精度 | +| keyword | string | 否 | 关键字 | +| order | string | 否 | 排序 | +| sort | string | 否 | 排序字段 | +| price | number | 否 | 价格筛选 | + +### 响应 + +`{ code, data, msg }`,`data` 为 `PageResult`(`list`、`page`、`pageSize`、`total`)。 + +### PmPriceHistoryItem(list 单项) + +| 字段 | 类型 | 说明 | +|------|------|------| +| ID | number | 主键 | +| market | string | 市场标识 | +| price | number | 价格(0–1 小数或 0–100,由 priceHistoryToChartData 统一为 0–100) | +| time | number | Unix 秒时间戳 | +| interval | string | 数据间隔 | +| fidelity | number | 数据精度 | +| createdAt | string | 创建时间 | +| updatedAt | string | 更新时间 | + +## 使用方式 + +```typescript +import { + getPmPriceHistoryPublic, + priceHistoryToChartData, + type PmPriceHistoryItem, +} from '@/api/priceHistory' + +const res = await getPmPriceHistoryPublic({ + market: marketId, + page: 1, + pageSize: 500, +}) +const chartData = priceHistoryToChartData(res.data?.list ?? []) +``` + +## 扩展方式 + +- 按时间范围(1H/6H/1D 等)传 `interval` 或 `createdAtRange` 需与后端约定取值 +- 若后端返回的 `price` 固定为 0–100,`priceHistoryToChartData` 已兼容(≤1 时乘 100) diff --git a/docs/components/OrderBook.md b/docs/components/OrderBook.md index c40d380..2e05b0f 100644 --- a/docs/components/OrderBook.md +++ b/docs/components/OrderBook.md @@ -9,7 +9,7 @@ ## 核心能力 - Trade Up / Trade Down Tab -- Asks、Bids 列表,带 `HorizontalProgressBar` 深度条 +- Asks、Bids 列表,带 `HorizontalProgressBar` 深度条(买卖两边共用同一最大值 `maxOrderBookTotal`,取两边累计总量中的最大值,便于对比深度) - Last price、Spread 展示 - Live / 连接中 状态展示(均通过 i18n 国际化) diff --git a/docs/stores/user.md b/docs/stores/user.md index 376fbc0..0b78d6d 100644 --- a/docs/stores/user.md +++ b/docs/stores/user.md @@ -12,7 +12,7 @@ - `isLoggedIn`、`avatarUrl`:派生状态 - `balance`:USDC 余额显示(如 "0.00"),支持 **UserSocket** 实时推送更新 - `setUser`:设置登录数据并持久化,登录成功后自动连接 UserSocket -- `logout`:清空并断开 UserSocket +- `logout`:退出登录;先调用 **POST /jwt/jsonInBlacklist** 将当前 JWT 加入黑名单,再清空本地 token/user 并断开 UserSocket;接口失败时仍执行本地登出 - `getAuthHeaders`:返回 `{ 'x-token', 'x-user-id' }`,未登录时返回 `undefined` - `fetchUserInfo`、`fetchUsdcBalance`:拉取并更新用户信息与余额;`fetchUserInfo` 兼容多种 API 字段名(id/ID、userName/username 等) - 内部 `parseUserId`:从 API 返回的 user 对象解析 id/ID,兼容 number 与 string,供 `setUser` 与 `fetchUserInfo` 复用 diff --git a/docs/views/TradeDetail.md b/docs/views/TradeDetail.md index 73ec996..94b6cb0 100644 --- a/docs/views/TradeDetail.md +++ b/docs/views/TradeDetail.md @@ -9,7 +9,7 @@ ## 核心能力 -- 分时图:ECharts 渲染,支持 Past、时间粒度切换;**加密货币事件**可切换 YES/NO 分时图与加密货币价格走势图(CoinGecko 实时数据) +- 分时图:ECharts 渲染,支持 Past、时间粒度切换(1H/6H/1D/1W/1M/ALL);**Yes/No 模式**数据来自 **GET /pmPriceHistory/getPmPriceHistoryPublic**(market 传 clobTokenIds[0]),接口返回 `time`(Unix 秒)、`price`(0–1)转成 `[timestamp_ms, value_0_100][]` 后缓存在 `rawChartData`,**分时**为前端按当前选中范围过滤:1H=最近 1 小时、6H=6 小时、1D=1 天、1W=7 天、1M=30 天、ALL=全部,切换时间范围不重复请求;**加密货币事件**可切换 YES/NO 分时图与加密货币价格走势图(CoinGecko 实时数据) - 订单簿:`OrderBook` 组件,通过 **ClobSdk** 对接 CLOB WebSocket 实时数据(全量快照、增量更新、成交推送);份额接口按 6 位小数传(1_000_000 = 1 share),`priceSizeToRows` 与 `mergeDelta` 会将 raw 值除以 `ORDER_BOOK_SIZE_SCALE` 转为展示值 - 交易:`TradeComponent`,传入 `market`、`initialOption`、`positions`(持仓数据) - 持仓列表:通过 `getPositionList` 获取当前市场持仓,传递给 `TradeComponent` 用于计算可合并份额 @@ -36,7 +36,7 @@ ## 扩展方式 1. **订单簿**:已通过 `sdk/clobSocket.ts` 的 ClobSdk 对接 CLOB WebSocket,使用 **Yes/No token ID** 订阅 `price_size_all`、`price_size_delta`、`trade` 消息 -2. **分时图**:可接入 WebSocket 推送的图表数据;加密货币事件已支持 YES/NO 分时与加密货币价格图切换(`src/api/cryptoChart.ts`) +2. **分时图**:Yes/NO 折线图仅使用真实接口 `getPmPriceHistoryPublic`,无模拟数据与定时器;事件详情加载完成后自动请求并展示,无 marketId 或接口无数据时展示空图;加密货币事件已支持 YES/NO 分时与加密货币价格图切换(`src/api/cryptoChart.ts`) 3. **Comments**:对接评论接口,替换 placeholder 4. **Top Holders**:对接持仓接口 5. **Activity**:已对接 CLOB `trade` 消息,实时追加成交记录 diff --git a/docs/views/Wallet.md b/docs/views/Wallet.md index 0739618..d300b5d 100644 --- a/docs/views/Wallet.md +++ b/docs/views/Wallet.md @@ -10,8 +10,9 @@ ## 核心能力 - Portfolio 卡片:余额、Deposit/Withdraw 按钮 -- Profit/Loss 卡片:时间范围切换、ECharts 图表 -- Tab:Positions、Open orders、History、Withdrawals(提现记录) +- Profit/Loss 卡片:时间范围切换(1D/1W/1M/ALL)、ECharts 资产变化折线图;数据格式为 `[timestamp_ms, pnl][]`,**暂无接口时全部显示为 0**(真实时间轴 + 数值 0),有接口后在此处对接 +- Tab:Positions、Open orders、**History**(历史记录来自 **GET /hr/getHistoryRecordListClient**,`src/api/historyRecord.ts`,需鉴权、按当前用户分页)、Withdrawals(提现记录) +- **可结算/领取**:未结算项(unsettledItems)由持仓中有 `marketID`、`tokenID` 且 **所属 market.closed=true** 的项组成,用于「领取结算」按钮;不再使用 needClaim 判断 - Withdrawals:分页列表,状态筛选(全部/审核中/提现成功/审核不通过/提现失败),对接 GET /pmset/getPmSettlementRequestsListClient - DepositDialog、WithdrawDialog 组件 - **401 权限错误**:取消订单等接口失败时,通过 `useAuthError().formatAuthError` 统一提示「请先登录」或「权限不足」 diff --git a/src/api/event.ts b/src/api/event.ts index 1adb46a..9da7f86 100644 --- a/src/api/event.ts +++ b/src/api/event.ts @@ -110,6 +110,7 @@ export interface PmEventListResponse { /** * GET /PmEvent/getPmEventPublic 请求参数(与 doc.json 对齐) + * startDateMin、endDateMax 不传;startDateMax、endDateMin 传当前时间戳(秒) */ export interface GetPmEventListParams { page?: number @@ -147,6 +148,7 @@ export async function getPmEventPublic( archived = false, closed = false, } = params + const nowTs = Math.floor(Date.now() / 1000) const query = buildQuery({ page, pageSize, @@ -156,6 +158,8 @@ export async function getPmEventPublic( active: active ? 'true' : 'false', archived: archived ? 'true' : 'false', closed: closed ? 'true' : 'false', + startDateMax: nowTs, + endDateMin: nowTs, }) return get('/PmEvent/getPmEventPublic', query) } diff --git a/src/api/jwt.ts b/src/api/jwt.ts new file mode 100644 index 0000000..d3fa69b --- /dev/null +++ b/src/api/jwt.ts @@ -0,0 +1,18 @@ +/** + * JWT 相关接口 + * POST /jwt/jsonInBlacklist:将当前 JWT 加入黑名单(退出登录时调用) + */ + +import { post } from './request' +import type { ApiResponse } from './types' + +/** + * 将 JWT 加入黑名单(需鉴权:x-token) + * POST /jwt/jsonInBlacklist + * 退出登录时调用,使当前 token 失效 + */ +export async function jsonInBlacklist( + config?: { headers?: Record }, +): Promise { + return post('/jwt/jsonInBlacklist', undefined, config) +} diff --git a/src/components/TradeComponent.vue b/src/components/TradeComponent.vue index 736b465..f1c005c 100644 --- a/src/components/TradeComponent.vue +++ b/src/components/TradeComponent.vue @@ -63,7 +63,9 @@
{{ t('trade.amount') }} - {{ t('trade.balanceLabel') }} ${{ balance.toFixed(2) }} + {{ t('trade.balanceLabel') }} ${{ balance.toFixed(2) }}
{{ t('trade.shares') }} - {{ t('trade.maxShares') }}: {{ maxAvailableShares }} + {{ t('trade.maxShares') }}: {{ maxAvailableShares }}
{{ orderError }}

- + {{ t('trade.deposit') }}
{{ t('trade.amount') }} - {{ t('trade.balanceLabel') }} ${{ balance.toFixed(2) }} + {{ t('trade.balanceLabel') }} ${{ balance.toFixed(2) }}
{{ t('trade.shares') }} - {{ t('trade.maxShares') }}: {{ maxAvailableShares }} + {{ t('trade.maxShares') }}: {{ maxAvailableShares }}
{{ t('trade.shares') }} - {{ t('trade.maxShares') }}: {{ maxAvailableShares }} + {{ t('trade.maxShares') }}: {{ maxAvailableShares }}
-

{{ t('trade.sharesExceedsMax', { max: maxAvailableShares }) }}

+

+ {{ t('trade.sharesExceedsMax', { max: maxAvailableShares }) }} +

mdi-information 20.00 matching @@ -506,7 +514,9 @@
{{ t('trade.amount') }} - {{ t('trade.balanceLabel') }} ${{ balance.toFixed(2) }} + {{ t('trade.balanceLabel') }} ${{ balance.toFixed(2) }}
{{ t('trade.shares') }} - {{ t('trade.maxShares') }}: {{ maxAvailableShares }} + {{ t('trade.maxShares') }}: {{ maxAvailableShares }}