feat(whale-monitor): 保存策略时落库缓存市场并回显标题
- 创建/更新策略时复用 MarketService.getMarkets 写入 markets 表缓存 - 策略 DTO 增加 markets 字段,编辑页直接用其显示市场标题 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -44,6 +44,18 @@ data class WhaleMonitorStrategyListRequest(
|
|||||||
val enabled: Boolean? = null
|
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(列表与详情)
|
* 大单监听策略 DTO(列表与详情)
|
||||||
*/
|
*/
|
||||||
@@ -52,6 +64,8 @@ data class WhaleMonitorStrategyDto(
|
|||||||
val accountId: Long = 0L,
|
val accountId: Long = 0L,
|
||||||
val name: String? = null,
|
val name: String? = null,
|
||||||
val conditionIds: List<String> = emptyList(),
|
val conditionIds: List<String> = emptyList(),
|
||||||
|
/** 监听市场展示信息(与 conditionIds 顺序一致,保存时写入 markets 缓存) */
|
||||||
|
val markets: List<WhaleMonitorMarketDto> = emptyList(),
|
||||||
val windowSeconds: Int = 10,
|
val windowSeconds: Int = 10,
|
||||||
val thresholdAmount: String = "0",
|
val thresholdAmount: String = "0",
|
||||||
val orderAmount: String = "0",
|
val orderAmount: String = "0",
|
||||||
|
|||||||
+36
-3
@@ -7,6 +7,7 @@ import com.wrbug.polymarketbot.enums.ErrorCode
|
|||||||
import com.wrbug.polymarketbot.event.WhaleMonitorStrategyChangedEvent
|
import com.wrbug.polymarketbot.event.WhaleMonitorStrategyChangedEvent
|
||||||
import com.wrbug.polymarketbot.repository.WhaleMonitorStrategyRepository
|
import com.wrbug.polymarketbot.repository.WhaleMonitorStrategyRepository
|
||||||
import com.wrbug.polymarketbot.repository.WhaleMonitorTriggerRepository
|
import com.wrbug.polymarketbot.repository.WhaleMonitorTriggerRepository
|
||||||
|
import com.wrbug.polymarketbot.service.common.MarketService
|
||||||
import com.wrbug.polymarketbot.util.fromJson
|
import com.wrbug.polymarketbot.util.fromJson
|
||||||
import com.wrbug.polymarketbot.util.gt
|
import com.wrbug.polymarketbot.util.gt
|
||||||
import com.wrbug.polymarketbot.util.toSafeBigDecimal
|
import com.wrbug.polymarketbot.util.toSafeBigDecimal
|
||||||
@@ -26,6 +27,7 @@ import java.time.format.DateTimeFormatter
|
|||||||
class WhaleMonitorStrategyService(
|
class WhaleMonitorStrategyService(
|
||||||
private val strategyRepository: WhaleMonitorStrategyRepository,
|
private val strategyRepository: WhaleMonitorStrategyRepository,
|
||||||
private val triggerRepository: WhaleMonitorTriggerRepository,
|
private val triggerRepository: WhaleMonitorTriggerRepository,
|
||||||
|
private val marketService: MarketService,
|
||||||
private val eventPublisher: ApplicationEventPublisher
|
private val eventPublisher: ApplicationEventPublisher
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -63,6 +65,7 @@ class WhaleMonitorStrategyService(
|
|||||||
validatePriceDecimalPlaces(maxPrice, "maxPrice")
|
validatePriceDecimalPlaces(maxPrice, "maxPrice")
|
||||||
|
|
||||||
val conditionIdsJson = request.conditionIds.toJson()
|
val conditionIdsJson = request.conditionIds.toJson()
|
||||||
|
cacheMarkets(request.conditionIds)
|
||||||
val nameToSave = request.name?.takeIf { it.isNotBlank() }
|
val nameToSave = request.name?.takeIf { it.isNotBlank() }
|
||||||
?: generateStrategyName()
|
?: generateStrategyName()
|
||||||
|
|
||||||
@@ -95,10 +98,13 @@ class WhaleMonitorStrategyService(
|
|||||||
val existing = strategyRepository.findById(request.strategyId).orElse(null)
|
val existing = strategyRepository.findById(request.strategyId).orElse(null)
|
||||||
?: return Result.failure(IllegalArgumentException(ErrorCode.WHALE_MONITOR_STRATEGY_NOT_FOUND.messageKey))
|
?: 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))
|
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
|
val windowSeconds = request.windowSeconds ?: existing.windowSeconds
|
||||||
if (windowSeconds <= 0) {
|
if (windowSeconds <= 0) {
|
||||||
@@ -251,6 +257,32 @@ class WhaleMonitorStrategyService(
|
|||||||
return "大单监听策略-$suffix"
|
return "大单监听策略-$suffix"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 将 conditionId 对应市场写入 markets 表(复用 MarketService 缓存与 Gamma 拉取) */
|
||||||
|
private fun cacheMarkets(conditionIds: List<String>) {
|
||||||
|
if (conditionIds.isEmpty()) return
|
||||||
|
marketService.getMarkets(conditionIds)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildMarketDtos(conditionIds: List<String>): List<WhaleMonitorMarketDto> {
|
||||||
|
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 {
|
private fun entityToDto(e: WhaleMonitorStrategy, lastTriggerAt: Long?, triggerCount: Long): WhaleMonitorStrategyDto {
|
||||||
val conditionIdList: List<String> = try {
|
val conditionIdList: List<String> = try {
|
||||||
e.conditionIds.fromJson<List<String>>() ?: emptyList()
|
e.conditionIds.fromJson<List<String>>() ?: emptyList()
|
||||||
@@ -262,6 +294,7 @@ class WhaleMonitorStrategyService(
|
|||||||
accountId = e.accountId,
|
accountId = e.accountId,
|
||||||
name = e.name,
|
name = e.name,
|
||||||
conditionIds = conditionIdList,
|
conditionIds = conditionIdList,
|
||||||
|
markets = buildMarketDtos(conditionIdList),
|
||||||
windowSeconds = e.windowSeconds,
|
windowSeconds = e.windowSeconds,
|
||||||
thresholdAmount = e.thresholdAmount.toPlainString(),
|
thresholdAmount = e.thresholdAmount.toPlainString(),
|
||||||
orderAmount = e.orderAmount.toPlainString(),
|
orderAmount = e.orderAmount.toPlainString(),
|
||||||
|
|||||||
@@ -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 字符串 */
|
/** 解析 Gamma 返回的 outcomes JSON 字符串 */
|
||||||
export function parseMarketOutcomes(outcomes?: string): string[] {
|
export function parseMarketOutcomes(outcomes?: string): string[] {
|
||||||
if (!outcomes?.trim()) return []
|
if (!outcomes?.trim()) return []
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import {
|
|||||||
clearWhaleMonitorFormDraft,
|
clearWhaleMonitorFormDraft,
|
||||||
conditionIdsToMarkets,
|
conditionIdsToMarkets,
|
||||||
loadWhaleMonitorFormDraft,
|
loadWhaleMonitorFormDraft,
|
||||||
saveWhaleMonitorFormDraft
|
saveWhaleMonitorFormDraft,
|
||||||
|
strategyMarketsToItems
|
||||||
} from '../constants/whaleMonitor'
|
} from '../constants/whaleMonitor'
|
||||||
import type {
|
import type {
|
||||||
WhaleMonitorStrategyDto,
|
WhaleMonitorStrategyDto,
|
||||||
@@ -90,7 +91,19 @@ const WhaleMonitorStrategyList: React.FC = () => {
|
|||||||
} else {
|
} else {
|
||||||
setEditingStrategy(null)
|
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)
|
form.setFieldsValue(pendingFormDraft.formValues)
|
||||||
setFormVisible(true)
|
setFormVisible(true)
|
||||||
setPendingFormDraft(null)
|
setPendingFormDraft(null)
|
||||||
@@ -173,7 +186,11 @@ const WhaleMonitorStrategyList: React.FC = () => {
|
|||||||
|
|
||||||
const showEditModal = (strategy: WhaleMonitorStrategyDto) => {
|
const showEditModal = (strategy: WhaleMonitorStrategyDto) => {
|
||||||
setEditingStrategy(strategy)
|
setEditingStrategy(strategy)
|
||||||
setSelectedMarkets(conditionIdsToMarkets(strategy.conditionIds))
|
const known =
|
||||||
|
strategy.markets && strategy.markets.length > 0
|
||||||
|
? strategyMarketsToItems(strategy.markets)
|
||||||
|
: []
|
||||||
|
setSelectedMarkets(conditionIdsToMarkets(strategy.conditionIds, known))
|
||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
accountId: strategy.accountId,
|
accountId: strategy.accountId,
|
||||||
name: strategy.name,
|
name: strategy.name,
|
||||||
|
|||||||
@@ -1344,11 +1344,21 @@ export interface WhaleMonitorStrategyListLocationState {
|
|||||||
selectedMarkets?: WhaleMonitorMarketItem[]
|
selectedMarkets?: WhaleMonitorMarketItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface WhaleMonitorStrategyMarketDto {
|
||||||
|
conditionId: string
|
||||||
|
title: string
|
||||||
|
slug?: string
|
||||||
|
category?: string
|
||||||
|
image?: string
|
||||||
|
icon?: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface WhaleMonitorStrategyDto {
|
export interface WhaleMonitorStrategyDto {
|
||||||
id: number
|
id: number
|
||||||
accountId: number
|
accountId: number
|
||||||
name?: string
|
name?: string
|
||||||
conditionIds: string[]
|
conditionIds: string[]
|
||||||
|
markets?: WhaleMonitorStrategyMarketDto[]
|
||||||
windowSeconds: number
|
windowSeconds: number
|
||||||
thresholdAmount: string
|
thresholdAmount: string
|
||||||
orderAmount: string
|
orderAmount: string
|
||||||
|
|||||||
Reference in New Issue
Block a user