feat: 添加推送已过滤订单功能并修复相关问题

- 新增推送已过滤订单功能(pushFilteredOrders),默认关闭
  - 数据库迁移:添加 pushFilteredOrders 字段到模板表和跟单配置表
  - 后端:在实体、DTO、Service 中添加 pushFilteredOrders 字段支持
  - 前端:在模板新增、编辑、推送页面和跟单配置新增、编辑页面添加开关
  - 多语言:添加中文、繁体中文、英文翻译
  - 通知逻辑:发送过滤订单通知时检查 pushFilteredOrders 字段

- 修复新建跟单配置时 pushFilteredOrders 字段未生效的问题
  - 修复手动输入模式下 pushFilteredOrders 被硬编码为 false 的问题
  - 修复从模板填充表单时未加载 pushFilteredOrders 的问题
  - 添加 CopyTradingTemplate 接口的 pushFilteredOrders 字段定义

- 修复新建和编辑页面的截止时间输入框交互问题
  - 修复删除后失去焦点自动填充1的问题
  - 优化 onChange 和 onBlur 处理逻辑,支持清空操作
This commit is contained in:
WrBug
2026-01-12 14:29:12 +08:00
parent cb8e46919f
commit 9c303e0a82
17 changed files with 183 additions and 40 deletions
@@ -120,7 +120,8 @@ const AddModal: React.FC<AddModalProps> = ({
minPrice: template.minPrice ? parseFloat(template.minPrice) : undefined,
maxPrice: template.maxPrice ? parseFloat(template.maxPrice) : undefined,
maxPositionValue: (template as any).maxPositionValue ? parseFloat((template as any).maxPositionValue) : undefined,
maxPositionCount: (template as any).maxPositionCount
maxPositionCount: (template as any).maxPositionCount,
pushFilteredOrders: template.pushFilteredOrders ?? false
})
setCopyMode(template.copyMode)
setTemplateModalVisible(false)
@@ -255,6 +256,7 @@ const AddModal: React.FC<AddModalProps> = ({
: undefined,
configName: values.configName?.trim(),
pushFailedOrders: values.pushFailedOrders ?? false,
pushFilteredOrders: values.pushFilteredOrders ?? false,
maxMarketEndDate
}
@@ -307,6 +309,7 @@ const AddModal: React.FC<AddModalProps> = ({
websocketMaxRetries: 10,
supportSell: true,
pushFailedOrders: false,
pushFilteredOrders: false,
keywordFilterMode: 'DISABLED'
}}
>
@@ -804,18 +807,35 @@ const AddModal: React.FC<AddModalProps> = ({
>
<Input.Group compact style={{ display: 'flex' }}>
<InputNumber
min={1}
min={0}
max={9999}
step={1}
precision={0}
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%' }}
placeholder={t('copyTradingAdd.maxMarketEndDatePlaceholder') || '输入时间值(可选)'}
parser={(value) => {
if (!value) return 0
if (!value) return ''
const num = parseInt(value.replace(/\D/g, ''), 10)
return isNaN(num) ? 0 : num
return isNaN(num) ? '' : num.toString()
}}
formatter={(value) => {
if (!value && value !== 0) return ''
@@ -862,6 +882,16 @@ const AddModal: React.FC<AddModalProps> = ({
<Switch />
</Form.Item>
{/* 推送已过滤订单 */}
<Form.Item
label={t('copyTradingAdd.pushFilteredOrders') || '推送已过滤订单'}
name="pushFilteredOrders"
tooltip={t('copyTradingAdd.pushFilteredOrdersTooltip') || '开启后,被过滤的订单会推送到 Telegram'}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item>
<Space>
<Button
@@ -91,7 +91,8 @@ const EditModal: React.FC<EditModalProps> = ({
maxPositionCount: found.maxPositionCount,
keywordFilterMode: found.keywordFilterMode || 'DISABLED',
configName: found.configName || '',
pushFailedOrders: found.pushFailedOrders ?? false
pushFailedOrders: found.pushFailedOrders ?? false,
pushFilteredOrders: found.pushFilteredOrders ?? false
})
// 设置关键字列表
setKeywords(found.keywords || [])
@@ -214,6 +215,7 @@ const EditModal: React.FC<EditModalProps> = ({
: undefined,
configName: values.configName?.trim() || undefined,
pushFailedOrders: values.pushFailedOrders,
pushFilteredOrders: values.pushFilteredOrders,
maxMarketEndDate
}
@@ -802,6 +804,15 @@ const EditModal: React.FC<EditModalProps> = ({
<Switch />
</Form.Item>
<Form.Item
label={t('copyTradingEdit.pushFilteredOrders') || '推送已过滤订单'}
name="pushFilteredOrders"
tooltip={t('copyTradingEdit.pushFilteredOrdersTooltip') || '开启后,被过滤的订单会推送到 Telegram'}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item>
<Space>
<Button
+13 -2
View File
@@ -55,7 +55,8 @@ const TemplateAdd: React.FC = () => {
minOrderDepth: values.minOrderDepth?.toString(),
maxSpread: values.maxSpread?.toString(),
minPrice: values.minPrice?.toString(),
maxPrice: values.maxPrice?.toString()
maxPrice: values.maxPrice?.toString(),
pushFilteredOrders: values.pushFilteredOrders ?? false
})
if (response.data.code === 0) {
@@ -96,7 +97,8 @@ const TemplateAdd: React.FC = () => {
minOrderSize: 1,
maxDailyOrders: 100,
priceTolerance: 5,
supportSell: true
supportSell: true,
pushFilteredOrders: false
}}
>
<Form.Item
@@ -373,6 +375,15 @@ const TemplateAdd: React.FC = () => {
<Switch />
</Form.Item>
<Form.Item
label={t('templateAdd.pushFilteredOrders') || '推送已过滤订单'}
name="pushFilteredOrders"
tooltip={t('templateAdd.pushFilteredOrdersTooltip') || '开启后,被过滤的订单会推送到 Telegram'}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{({ getFieldsError }) => {
const errors = getFieldsError()
+13 -2
View File
@@ -41,7 +41,8 @@ const TemplateEdit: React.FC = () => {
minOrderDepth: template.minOrderDepth ? parseFloat(template.minOrderDepth) : undefined,
maxSpread: template.maxSpread ? parseFloat(template.maxSpread) : undefined,
minPrice: template.minPrice ? parseFloat(template.minPrice) : undefined,
maxPrice: template.maxPrice ? parseFloat(template.maxPrice) : undefined
maxPrice: template.maxPrice ? parseFloat(template.maxPrice) : undefined,
pushFilteredOrders: template.pushFilteredOrders ?? false
})
} else {
message.error(response.data.msg || t('templateEdit.fetchFailed') || '获取模板详情失败')
@@ -99,7 +100,8 @@ const TemplateEdit: React.FC = () => {
minOrderDepth: values.minOrderDepth?.toString(),
maxSpread: values.maxSpread?.toString(),
minPrice: values.minPrice?.toString(),
maxPrice: values.maxPrice?.toString()
maxPrice: values.maxPrice?.toString(),
pushFilteredOrders: values.pushFilteredOrders
})
if (response.data.code === 0) {
@@ -409,6 +411,15 @@ const TemplateEdit: React.FC = () => {
<Switch />
</Form.Item>
<Form.Item
label={t('templateEdit.pushFilteredOrders') || '推送已过滤订单'}
name="pushFilteredOrders"
tooltip={t('templateEdit.pushFilteredOrdersTooltip') || '开启后,被过滤的订单会推送到 Telegram'}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{({ getFieldsError }) => {
const errors = getFieldsError()
+12 -1
View File
@@ -72,6 +72,7 @@ const TemplateList: React.FC = () => {
maxDailyOrders: template.maxDailyOrders,
priceTolerance: parseFloat(template.priceTolerance),
supportSell: template.supportSell,
pushFilteredOrders: template.pushFilteredOrders ?? false,
minOrderDepth: template.minOrderDepth ? parseFloat(template.minOrderDepth) : undefined,
maxSpread: template.maxSpread ? parseFloat(template.maxSpread) : undefined,
minPrice: template.minPrice ? parseFloat(template.minPrice) : undefined,
@@ -122,7 +123,8 @@ const TemplateList: React.FC = () => {
minOrderDepth: values.minOrderDepth?.toString(),
maxSpread: values.maxSpread?.toString(),
minPrice: values.minPrice?.toString(),
maxPrice: values.maxPrice?.toString()
maxPrice: values.maxPrice?.toString(),
pushFilteredOrders: values.pushFilteredOrders ?? false
})
if (response.data.code === 0) {
@@ -631,6 +633,15 @@ const TemplateList: React.FC = () => {
<Switch />
</Form.Item>
<Form.Item
label={t('templateList.pushFilteredOrders') || '推送已过滤订单'}
name="pushFilteredOrders"
tooltip={t('templateList.pushFilteredOrdersTooltip') || '开启后,被过滤的订单会推送到 Telegram'}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Divider></Divider>
<Form.Item