feat: 实现订单推送服务和 WebSocket 重连机制
- 实现订单推送服务,支持多账户订单实时推送 - 添加 WebSocket 自动重连机制,支持指数退避策略 - 修复订单详情接口 L2 认证问题,通过 PolymarketClobService 获取 - 配置 Gson lenient 模式,支持解析格式不严格的 JSON - 添加响应日志拦截器,便于调试 API 响应问题 - 使用 Gamma API 获取市场信息,支持通过 condition_ids 查询 - 修复字段映射问题,使用 @SerializedName 替代 @JsonProperty - 订单推送消息包含订单详情和市场信息
This commit is contained in:
+88
-2
@@ -1,6 +1,6 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useEffect, useCallback } from 'react'
|
||||
import { BrowserRouter, Routes, Route } from 'react-router-dom'
|
||||
import { ConfigProvider } from 'antd'
|
||||
import { ConfigProvider, notification } from 'antd'
|
||||
import zhCN from 'antd/locale/zh_CN'
|
||||
import Layout from './components/Layout'
|
||||
import AccountList from './pages/AccountList'
|
||||
@@ -13,8 +13,83 @@ import ConfigPage from './pages/ConfigPage'
|
||||
import PositionList from './pages/PositionList'
|
||||
import Statistics from './pages/Statistics'
|
||||
import { wsManager } from './services/websocket'
|
||||
import type { OrderPushMessage } from './types'
|
||||
|
||||
function App() {
|
||||
/**
|
||||
* 获取订单类型文本
|
||||
*/
|
||||
const getOrderTypeText = useCallback((type: string): string => {
|
||||
switch (type) {
|
||||
case 'PLACEMENT':
|
||||
return '订单创建'
|
||||
case 'UPDATE':
|
||||
return '订单更新'
|
||||
case 'CANCELLATION':
|
||||
return '订单取消'
|
||||
default:
|
||||
return '订单事件'
|
||||
}
|
||||
}, [])
|
||||
|
||||
/**
|
||||
* 处理订单推送消息,显示全局通知
|
||||
*/
|
||||
const handleOrderPush = useCallback((message: OrderPushMessage) => {
|
||||
const { accountName, order, orderDetail } = message
|
||||
|
||||
// 根据订单类型和操作类型确定通知内容
|
||||
const orderTypeText = getOrderTypeText(order.type)
|
||||
const sideText = order.side === 'BUY' ? '买入' : '卖出'
|
||||
|
||||
// 如果有市场名称,在标题中显示
|
||||
const marketName = orderDetail?.marketName || order.market.substring(0, 8) + '...'
|
||||
const title = `${accountName} - ${orderTypeText}`
|
||||
|
||||
// 优先使用订单详情中的数据,如果没有则使用 WebSocket 消息中的数据
|
||||
const price = orderDetail ? parseFloat(orderDetail.price).toFixed(4) : parseFloat(order.price).toFixed(4)
|
||||
const size = orderDetail ? parseFloat(orderDetail.size).toFixed(2) : parseFloat(order.original_size).toFixed(2)
|
||||
const filled = orderDetail ? parseFloat(orderDetail.filled).toFixed(2) : parseFloat(order.size_matched).toFixed(2)
|
||||
const status = orderDetail?.status || 'UNKNOWN'
|
||||
|
||||
// 构建描述信息
|
||||
let description = `市场: ${marketName}\n${sideText} ${size} @ ${price}`
|
||||
|
||||
// 如果有订单详情,显示更详细的信息
|
||||
if (orderDetail) {
|
||||
description += `\n状态: ${status}`
|
||||
if (parseFloat(filled) > 0) {
|
||||
description += ` | 已成交: ${filled}`
|
||||
}
|
||||
const remaining = (parseFloat(size) - parseFloat(filled)).toFixed(2)
|
||||
if (parseFloat(remaining) > 0) {
|
||||
description += ` | 剩余: ${remaining}`
|
||||
}
|
||||
} else if (order.type === 'UPDATE' && parseFloat(order.size_matched) > 0) {
|
||||
// 如果没有订单详情,使用 WebSocket 消息中的已成交数量
|
||||
description += `\n已成交: ${filled}`
|
||||
}
|
||||
|
||||
// 根据订单类型选择通知类型
|
||||
let notificationType: 'info' | 'success' | 'warning' | 'error' = 'info'
|
||||
if (order.type === 'PLACEMENT') {
|
||||
notificationType = 'info'
|
||||
} else if (order.type === 'UPDATE') {
|
||||
notificationType = 'success'
|
||||
} else if (order.type === 'CANCELLATION') {
|
||||
notificationType = 'warning'
|
||||
}
|
||||
|
||||
// 显示通知
|
||||
notification[notificationType]({
|
||||
message: title,
|
||||
description: description,
|
||||
placement: 'topRight',
|
||||
duration: order.type === 'CANCELLATION' ? 3 : 5, // 取消订单通知显示时间短一些
|
||||
key: `order-${order.id}`, // 使用订单 ID 作为 key,避免重复通知
|
||||
})
|
||||
}, [getOrderTypeText])
|
||||
|
||||
// 应用启动时立即建立全局 WebSocket 连接
|
||||
useEffect(() => {
|
||||
// 立即建立连接(如果还未连接)
|
||||
@@ -26,6 +101,17 @@ function App() {
|
||||
// WebSocket 连接会在整个应用生命周期中保持,并自动重连
|
||||
}, [])
|
||||
|
||||
// 订阅订单推送并显示全局通知
|
||||
useEffect(() => {
|
||||
const unsubscribe = wsManager.subscribe('order', (data: OrderPushMessage) => {
|
||||
handleOrderPush(data)
|
||||
})
|
||||
|
||||
return () => {
|
||||
unsubscribe()
|
||||
}
|
||||
}, [handleOrderPush])
|
||||
|
||||
return (
|
||||
<ConfigProvider locale={zhCN}>
|
||||
<BrowserRouter>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useEffect, useState, useRef } from 'react'
|
||||
import { wsManager, SubscriptionCallback } from '../services/websocket'
|
||||
import { wsManager } from '../services/websocket'
|
||||
|
||||
/**
|
||||
* 使用 WebSocket 订阅
|
||||
*/
|
||||
export function useWebSocketSubscription<T = any>(
|
||||
channel: string,
|
||||
callback: SubscriptionCallback,
|
||||
callback: (data: T) => void,
|
||||
payload?: any
|
||||
): { connected: boolean } {
|
||||
const [connected, setConnected] = useState(wsManager.isConnected())
|
||||
|
||||
@@ -210,3 +210,51 @@ export function getPositionKey(position: AccountPosition): string {
|
||||
return `${position.accountId}-${position.marketId}-${position.side}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Polymarket 订单消息(来自 WebSocket User Channel)
|
||||
*/
|
||||
export interface OrderMessage {
|
||||
asset_id: string
|
||||
associate_trades?: string[]
|
||||
event_type: string // "order"
|
||||
id: string // order id
|
||||
market: string // condition ID of market
|
||||
order_owner: string // owner of order
|
||||
original_size: string // original order size
|
||||
outcome: string // outcome
|
||||
owner: string // owner of orders
|
||||
price: string // price of order
|
||||
side: string // BUY/SELL
|
||||
size_matched: string // size of order that has been matched
|
||||
timestamp: string // time of event
|
||||
type: string // PLACEMENT/UPDATE/CANCELLATION
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单详情(通过 API 获取)
|
||||
*/
|
||||
export interface OrderDetail {
|
||||
id: string // 订单 ID
|
||||
market: string // 市场 ID (condition ID)
|
||||
side: string // BUY/SELL
|
||||
price: string // 价格
|
||||
size: string // 订单大小
|
||||
filled: string // 已成交数量
|
||||
status: string // 订单状态
|
||||
createdAt: string // 创建时间(ISO 8601 格式)
|
||||
marketName?: string // 市场名称
|
||||
marketSlug?: string // 市场 slug
|
||||
marketIcon?: string // 市场图标
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单推送消息
|
||||
*/
|
||||
export interface OrderPushMessage {
|
||||
accountId: number
|
||||
accountName: string
|
||||
order: OrderMessage // 订单信息(来自 WebSocket)
|
||||
orderDetail?: OrderDetail // 订单详情(通过 API 获取)
|
||||
timestamp?: number // 推送时间戳
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user