diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/dto/WhaleMonitorStrategyDto.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/dto/WhaleMonitorStrategyDto.kt index 0aca4f6..cdfb219 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/dto/WhaleMonitorStrategyDto.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/dto/WhaleMonitorStrategyDto.kt @@ -44,6 +44,18 @@ data class WhaleMonitorStrategyListRequest( val enabled: Boolean? = null ) +/** + * 策略关联市场展示信息(来自 markets 表缓存) + */ +data class WhaleMonitorMarketDto( + val conditionId: String = "", + val title: String = "", + val slug: String? = null, + val category: String? = null, + val image: String? = null, + val icon: String? = null +) + /** * 大单监听策略 DTO(列表与详情) */ @@ -52,6 +64,8 @@ data class WhaleMonitorStrategyDto( val accountId: Long = 0L, val name: String? = null, val conditionIds: List = emptyList(), + /** 监听市场展示信息(与 conditionIds 顺序一致,保存时写入 markets 缓存) */ + val markets: List = emptyList(), val windowSeconds: Int = 10, val thresholdAmount: String = "0", val orderAmount: String = "0", diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/whalemonitor/WhaleMonitorStrategyService.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/whalemonitor/WhaleMonitorStrategyService.kt index 9a77f90..f12b31f 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/whalemonitor/WhaleMonitorStrategyService.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/whalemonitor/WhaleMonitorStrategyService.kt @@ -7,6 +7,7 @@ import com.wrbug.polymarketbot.enums.ErrorCode import com.wrbug.polymarketbot.event.WhaleMonitorStrategyChangedEvent import com.wrbug.polymarketbot.repository.WhaleMonitorStrategyRepository import com.wrbug.polymarketbot.repository.WhaleMonitorTriggerRepository +import com.wrbug.polymarketbot.service.common.MarketService import com.wrbug.polymarketbot.util.fromJson import com.wrbug.polymarketbot.util.gt import com.wrbug.polymarketbot.util.toSafeBigDecimal @@ -26,6 +27,7 @@ import java.time.format.DateTimeFormatter class WhaleMonitorStrategyService( private val strategyRepository: WhaleMonitorStrategyRepository, private val triggerRepository: WhaleMonitorTriggerRepository, + private val marketService: MarketService, private val eventPublisher: ApplicationEventPublisher ) { @@ -63,6 +65,7 @@ class WhaleMonitorStrategyService( validatePriceDecimalPlaces(maxPrice, "maxPrice") val conditionIdsJson = request.conditionIds.toJson() + cacheMarkets(request.conditionIds) val nameToSave = request.name?.takeIf { it.isNotBlank() } ?: generateStrategyName() @@ -95,10 +98,13 @@ class WhaleMonitorStrategyService( val existing = strategyRepository.findById(request.strategyId).orElse(null) ?: return Result.failure(IllegalArgumentException(ErrorCode.WHALE_MONITOR_STRATEGY_NOT_FOUND.messageKey)) - val conditionIdsJson = request.conditionIds?.let { + val updatedConditionIds = request.conditionIds?.also { if (it.isEmpty()) return Result.failure(IllegalArgumentException(ErrorCode.WHALE_MONITOR_STRATEGY_CONDITION_IDS_EMPTY.messageKey)) - it.toJson() - } ?: existing.conditionIds + } + val conditionIdsJson = updatedConditionIds?.toJson() ?: existing.conditionIds + if (updatedConditionIds != null) { + cacheMarkets(updatedConditionIds) + } val windowSeconds = request.windowSeconds ?: existing.windowSeconds if (windowSeconds <= 0) { @@ -251,6 +257,32 @@ class WhaleMonitorStrategyService( return "大单监听策略-$suffix" } + /** 将 conditionId 对应市场写入 markets 表(复用 MarketService 缓存与 Gamma 拉取) */ + private fun cacheMarkets(conditionIds: List) { + if (conditionIds.isEmpty()) return + marketService.getMarkets(conditionIds) + } + + private fun buildMarketDtos(conditionIds: List): List { + if (conditionIds.isEmpty()) return emptyList() + val cached = marketService.getMarkets(conditionIds) + return conditionIds.map { id -> + val market = cached[id] + if (market != null) { + WhaleMonitorMarketDto( + conditionId = id, + title = market.title, + slug = market.slug, + category = market.category, + image = market.image, + icon = market.icon + ) + } else { + WhaleMonitorMarketDto(conditionId = id, title = id) + } + } + } + private fun entityToDto(e: WhaleMonitorStrategy, lastTriggerAt: Long?, triggerCount: Long): WhaleMonitorStrategyDto { val conditionIdList: List = try { e.conditionIds.fromJson>() ?: emptyList() @@ -262,6 +294,7 @@ class WhaleMonitorStrategyService( accountId = e.accountId, name = e.name, conditionIds = conditionIdList, + markets = buildMarketDtos(conditionIdList), windowSeconds = e.windowSeconds, thresholdAmount = e.thresholdAmount.toPlainString(), orderAmount = e.orderAmount.toPlainString(), diff --git a/frontend/src/constants/whaleMonitor.ts b/frontend/src/constants/whaleMonitor.ts index ff9913a..f35533f 100644 --- a/frontend/src/constants/whaleMonitor.ts +++ b/frontend/src/constants/whaleMonitor.ts @@ -30,6 +30,27 @@ export function conditionIdsToMarkets( }) } +/** 策略列表/详情中的 markets 字段转为表单展示项 */ +export function strategyMarketsToItems( + markets: Array<{ + conditionId: string + title: string + slug?: string + category?: string + image?: string + icon?: string + }> +): WhaleMonitorMarketItem[] { + return markets.map(m => ({ + conditionId: m.conditionId, + title: m.title, + slug: m.slug, + category: m.category, + image: m.image, + icon: m.icon + })) +} + /** 解析 Gamma 返回的 outcomes JSON 字符串 */ export function parseMarketOutcomes(outcomes?: string): string[] { if (!outcomes?.trim()) return [] diff --git a/frontend/src/pages/WhaleMonitorStrategyList.tsx b/frontend/src/pages/WhaleMonitorStrategyList.tsx index bddc680..4f70fb8 100644 --- a/frontend/src/pages/WhaleMonitorStrategyList.tsx +++ b/frontend/src/pages/WhaleMonitorStrategyList.tsx @@ -10,7 +10,8 @@ import { clearWhaleMonitorFormDraft, conditionIdsToMarkets, loadWhaleMonitorFormDraft, - saveWhaleMonitorFormDraft + saveWhaleMonitorFormDraft, + strategyMarketsToItems } from '../constants/whaleMonitor' import type { WhaleMonitorStrategyDto, @@ -90,7 +91,19 @@ const WhaleMonitorStrategyList: React.FC = () => { } else { setEditingStrategy(null) } - setSelectedMarkets(pendingFormDraft.selectedMarkets) + const draftStrategy = pendingFormDraft.editingStrategyId + ? strategies.find(s => s.id === pendingFormDraft.editingStrategyId) + : undefined + const knownMarkets = + draftStrategy?.markets && draftStrategy.markets.length > 0 + ? strategyMarketsToItems(draftStrategy.markets) + : pendingFormDraft.selectedMarkets + setSelectedMarkets( + conditionIdsToMarkets( + draftStrategy?.conditionIds ?? knownMarkets.map(m => m.conditionId), + knownMarkets + ) + ) form.setFieldsValue(pendingFormDraft.formValues) setFormVisible(true) setPendingFormDraft(null) @@ -173,7 +186,11 @@ const WhaleMonitorStrategyList: React.FC = () => { const showEditModal = (strategy: WhaleMonitorStrategyDto) => { setEditingStrategy(strategy) - setSelectedMarkets(conditionIdsToMarkets(strategy.conditionIds)) + const known = + strategy.markets && strategy.markets.length > 0 + ? strategyMarketsToItems(strategy.markets) + : [] + setSelectedMarkets(conditionIdsToMarkets(strategy.conditionIds, known)) form.setFieldsValue({ accountId: strategy.accountId, name: strategy.name, diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 0f649cb..1875c61 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -1344,11 +1344,21 @@ export interface WhaleMonitorStrategyListLocationState { selectedMarkets?: WhaleMonitorMarketItem[] } +export interface WhaleMonitorStrategyMarketDto { + conditionId: string + title: string + slug?: string + category?: string + image?: string + icon?: string +} + export interface WhaleMonitorStrategyDto { id: number accountId: number name?: string conditionIds: string[] + markets?: WhaleMonitorStrategyMarketDto[] windowSeconds: number thresholdAmount: string orderAmount: string