fix: 修复跟单配置更新时清空可选字段无法保存的问题
- 修复价格区间(minPrice/maxPrice)清空后无法保存的问题 - 修复最大仓位数量(maxPositionCount)清空后无法保存的问题 - 修复市场截止时间(maxMarketEndDate)清空后无法保存的问题 - 修复其他可选字段(minOrderDepth/maxSpread/maxPositionValue)清空后无法保存的问题 - 修复截止时间输入框删除后失去焦点自动填充1的问题 前端:清空字段时传空字符串或-1标记,让后端识别为清空操作 后端:处理空字符串和-1标记,正确设置为null以清空字段
This commit is contained in:
+72
-7
@@ -10,6 +10,7 @@ import com.wrbug.polymarketbot.repository.CopyTradingTemplateRepository
|
|||||||
import com.wrbug.polymarketbot.repository.LeaderRepository
|
import com.wrbug.polymarketbot.repository.LeaderRepository
|
||||||
import com.wrbug.polymarketbot.service.copytrading.monitor.CopyTradingMonitorService
|
import com.wrbug.polymarketbot.service.copytrading.monitor.CopyTradingMonitorService
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
|
import com.wrbug.polymarketbot.util.IllegalBigDecimal
|
||||||
import com.wrbug.polymarketbot.util.JsonUtils
|
import com.wrbug.polymarketbot.util.JsonUtils
|
||||||
import com.wrbug.polymarketbot.util.toSafeBigDecimal
|
import com.wrbug.polymarketbot.util.toSafeBigDecimal
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
@@ -211,12 +212,67 @@ class CopyTradingService(
|
|||||||
websocketReconnectInterval = request.websocketReconnectInterval ?: copyTrading.websocketReconnectInterval,
|
websocketReconnectInterval = request.websocketReconnectInterval ?: copyTrading.websocketReconnectInterval,
|
||||||
websocketMaxRetries = request.websocketMaxRetries ?: copyTrading.websocketMaxRetries,
|
websocketMaxRetries = request.websocketMaxRetries ?: copyTrading.websocketMaxRetries,
|
||||||
supportSell = request.supportSell ?: copyTrading.supportSell,
|
supportSell = request.supportSell ?: copyTrading.supportSell,
|
||||||
minOrderDepth = request.minOrderDepth?.toSafeBigDecimal() ?: copyTrading.minOrderDepth,
|
// 处理可选字段:空字符串表示要清空(设置为 null),null 表示不更新,转换失败保留旧值
|
||||||
maxSpread = request.maxSpread?.toSafeBigDecimal() ?: copyTrading.maxSpread,
|
minOrderDepth = if (request.minOrderDepth != null) {
|
||||||
minPrice = request.minPrice?.toSafeBigDecimal() ?: copyTrading.minPrice,
|
if (request.minOrderDepth.isBlank()) {
|
||||||
maxPrice = request.maxPrice?.toSafeBigDecimal() ?: copyTrading.maxPrice,
|
null
|
||||||
maxPositionValue = request.maxPositionValue?.toSafeBigDecimal() ?: copyTrading.maxPositionValue,
|
} else {
|
||||||
maxPositionCount = request.maxPositionCount ?: copyTrading.maxPositionCount,
|
val converted = request.minOrderDepth.toSafeBigDecimal()
|
||||||
|
if (converted == IllegalBigDecimal) copyTrading.minOrderDepth else converted
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copyTrading.minOrderDepth
|
||||||
|
},
|
||||||
|
maxSpread = if (request.maxSpread != null) {
|
||||||
|
if (request.maxSpread.isBlank()) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
val converted = request.maxSpread.toSafeBigDecimal()
|
||||||
|
if (converted == IllegalBigDecimal) copyTrading.maxSpread else converted
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copyTrading.maxSpread
|
||||||
|
},
|
||||||
|
minPrice = if (request.minPrice != null) {
|
||||||
|
if (request.minPrice.isBlank()) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
val converted = request.minPrice.toSafeBigDecimal()
|
||||||
|
if (converted == IllegalBigDecimal) copyTrading.minPrice else converted
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copyTrading.minPrice
|
||||||
|
},
|
||||||
|
maxPrice = if (request.maxPrice != null) {
|
||||||
|
if (request.maxPrice.isBlank()) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
val converted = request.maxPrice.toSafeBigDecimal()
|
||||||
|
if (converted == IllegalBigDecimal) copyTrading.maxPrice else converted
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copyTrading.maxPrice
|
||||||
|
},
|
||||||
|
maxPositionValue = if (request.maxPositionValue != null) {
|
||||||
|
if (request.maxPositionValue.isBlank()) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
val converted = request.maxPositionValue.toSafeBigDecimal()
|
||||||
|
if (converted == IllegalBigDecimal) copyTrading.maxPositionValue else converted
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copyTrading.maxPositionValue
|
||||||
|
},
|
||||||
|
// 处理 maxPositionCount:-1 表示要清空(设置为 null),null 表示不更新
|
||||||
|
maxPositionCount = if (request.maxPositionCount != null) {
|
||||||
|
if (request.maxPositionCount == -1) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
request.maxPositionCount
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copyTrading.maxPositionCount
|
||||||
|
},
|
||||||
keywordFilterMode = request.keywordFilterMode ?: copyTrading.keywordFilterMode,
|
keywordFilterMode = request.keywordFilterMode ?: copyTrading.keywordFilterMode,
|
||||||
keywords = if (request.keywords != null) {
|
keywords = if (request.keywords != null) {
|
||||||
convertKeywordsToJson(request.keywords)
|
convertKeywordsToJson(request.keywords)
|
||||||
@@ -227,7 +283,16 @@ class CopyTradingService(
|
|||||||
},
|
},
|
||||||
configName = configName,
|
configName = configName,
|
||||||
pushFailedOrders = request.pushFailedOrders ?: copyTrading.pushFailedOrders,
|
pushFailedOrders = request.pushFailedOrders ?: copyTrading.pushFailedOrders,
|
||||||
maxMarketEndDate = request.maxMarketEndDate ?: copyTrading.maxMarketEndDate,
|
// 处理 maxMarketEndDate:-1 表示要清空(设置为 null),null 表示不更新
|
||||||
|
maxMarketEndDate = if (request.maxMarketEndDate != null) {
|
||||||
|
if (request.maxMarketEndDate == -1L) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
request.maxMarketEndDate
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
copyTrading.maxMarketEndDate
|
||||||
|
},
|
||||||
updatedAt = System.currentTimeMillis()
|
updatedAt = System.currentTimeMillis()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -168,12 +168,17 @@ const EditModal: React.FC<EditModalProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 计算市场截止时间(毫秒)
|
// 计算市场截止时间(毫秒)
|
||||||
|
// 如果用户清空了,传 -1 表示要清空(后端会识别并设置为 null)
|
||||||
let maxMarketEndDate: number | undefined
|
let maxMarketEndDate: number | undefined
|
||||||
if (maxMarketEndDateValue !== undefined && maxMarketEndDateValue > 0) {
|
if (maxMarketEndDateValue !== undefined && maxMarketEndDateValue !== null && maxMarketEndDateValue > 0) {
|
||||||
const multiplier = maxMarketEndDateUnit === 'HOUR'
|
const multiplier = maxMarketEndDateUnit === 'HOUR'
|
||||||
? 60 * 60 * 1000 // 小时转毫秒
|
? 60 * 60 * 1000 // 小时转毫秒
|
||||||
: 24 * 60 * 60 * 1000 // 天转毫秒
|
: 24 * 60 * 60 * 1000 // 天转毫秒
|
||||||
maxMarketEndDate = maxMarketEndDateValue * multiplier
|
maxMarketEndDate = maxMarketEndDateValue * multiplier
|
||||||
|
} else {
|
||||||
|
// 如果值为 null/undefined/0/负数,传 -1 表示要清空
|
||||||
|
// 这样无论之前是否有值,清空后都会设置为 null
|
||||||
|
maxMarketEndDate = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
@@ -195,12 +200,14 @@ const EditModal: React.FC<EditModalProps> = ({
|
|||||||
websocketReconnectInterval: values.websocketReconnectInterval,
|
websocketReconnectInterval: values.websocketReconnectInterval,
|
||||||
websocketMaxRetries: values.websocketMaxRetries,
|
websocketMaxRetries: values.websocketMaxRetries,
|
||||||
supportSell: values.supportSell,
|
supportSell: values.supportSell,
|
||||||
minOrderDepth: values.minOrderDepth?.toString(),
|
// 对于可选字段,始终发送(即使为空也发送空字符串,让后端知道要清空)
|
||||||
maxSpread: values.maxSpread?.toString(),
|
minOrderDepth: values.minOrderDepth != null ? values.minOrderDepth.toString() : '',
|
||||||
minPrice: values.minPrice?.toString(),
|
maxSpread: values.maxSpread != null ? values.maxSpread.toString() : '',
|
||||||
maxPrice: values.maxPrice?.toString(),
|
minPrice: values.minPrice != null ? values.minPrice.toString() : '',
|
||||||
maxPositionValue: values.maxPositionValue?.toString(),
|
maxPrice: values.maxPrice != null ? values.maxPrice.toString() : '',
|
||||||
maxPositionCount: values.maxPositionCount,
|
maxPositionValue: values.maxPositionValue != null ? values.maxPositionValue.toString() : '',
|
||||||
|
// 对于 maxPositionCount,如果值为 null/undefined,传 -1 表示要清空(后端会识别并设置为 null)
|
||||||
|
maxPositionCount: values.maxPositionCount != null ? values.maxPositionCount : -1,
|
||||||
keywordFilterMode: values.keywordFilterMode || 'DISABLED',
|
keywordFilterMode: values.keywordFilterMode || 'DISABLED',
|
||||||
keywords: (values.keywordFilterMode === 'WHITELIST' || values.keywordFilterMode === 'BLACKLIST')
|
keywords: (values.keywordFilterMode === 'WHITELIST' || values.keywordFilterMode === 'BLACKLIST')
|
||||||
? keywords
|
? keywords
|
||||||
@@ -722,18 +729,35 @@ const EditModal: React.FC<EditModalProps> = ({
|
|||||||
>
|
>
|
||||||
<Input.Group compact style={{ display: 'flex' }}>
|
<Input.Group compact style={{ display: 'flex' }}>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
min={1}
|
min={0}
|
||||||
max={9999}
|
max={9999}
|
||||||
step={1}
|
step={1}
|
||||||
precision={0}
|
precision={0}
|
||||||
value={maxMarketEndDateValue}
|
value={maxMarketEndDateValue}
|
||||||
onChange={(value) => setMaxMarketEndDateValue(value !== null && value !== undefined ? Math.floor(value) : undefined)}
|
onChange={(value) => {
|
||||||
|
// 允许设置为 null 或 undefined(清空)
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
setMaxMarketEndDateValue(undefined)
|
||||||
|
} else {
|
||||||
|
const num = Math.floor(value)
|
||||||
|
// 如果值为 0,也设置为 undefined(表示清空)
|
||||||
|
setMaxMarketEndDateValue(num > 0 ? num : undefined)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onBlur={(e) => {
|
||||||
|
// 失去焦点时,如果值为 0 或空,设置为 undefined
|
||||||
|
const input = e.target as HTMLInputElement
|
||||||
|
const value = input.value
|
||||||
|
if (!value || value === '0') {
|
||||||
|
setMaxMarketEndDateValue(undefined)
|
||||||
|
}
|
||||||
|
}}
|
||||||
style={{ width: '60%' }}
|
style={{ width: '60%' }}
|
||||||
placeholder={t('copyTradingEdit.maxMarketEndDatePlaceholder') || '输入时间值(可选)'}
|
placeholder={t('copyTradingEdit.maxMarketEndDatePlaceholder') || '输入时间值(可选)'}
|
||||||
parser={(value) => {
|
parser={(value) => {
|
||||||
if (!value) return 0
|
if (!value) return ''
|
||||||
const num = parseInt(value.replace(/\D/g, ''), 10)
|
const num = parseInt(value.replace(/\D/g, ''), 10)
|
||||||
return isNaN(num) ? 0 : num
|
return isNaN(num) ? '' : num.toString()
|
||||||
}}
|
}}
|
||||||
formatter={(value) => {
|
formatter={(value) => {
|
||||||
if (!value && value !== 0) return ''
|
if (!value && value !== 0) return ''
|
||||||
|
|||||||
Reference in New Issue
Block a user