fix: improve batch tunnel migration and simplify batch action labels

This commit is contained in:
root
2026-02-06 04:00:06 +00:00
parent 529257c8d0
commit a9fadfc08c
5 changed files with 55 additions and 36 deletions
-9
View File
@@ -1,9 +0,0 @@
---
active: true
iteration: 1
max_iterations: 100
completion_promise: "DONE"
started_at: "2026-01-24T05:20:50.887Z"
session_id: "ses_41192b7d7ffewFHxf1dDS7ESHO"
---
Complete the task as instructed
@@ -1384,6 +1384,15 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
result.addFailedItem(forwardId, "已是目标隧道"); result.addFailedItem(forwardId, "已是目标隧道");
continue; continue;
} }
List<ForwardPort> existingForwardPorts = forwardPortService.list(
new QueryWrapper<ForwardPort>().eq("forward_id", forwardId).orderByAsc("id")
);
Integer originalInPort = existingForwardPorts.stream()
.map(ForwardPort::getPort)
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
Tunnel oldTunnel = validateTunnel(forward.getTunnelId()); Tunnel oldTunnel = validateTunnel(forward.getTunnelId());
if (oldTunnel != null) { if (oldTunnel != null) {
@@ -1424,7 +1433,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
List<ChainTunnel> newChainTunnels = chainTunnelService.list( List<ChainTunnel> newChainTunnels = chainTunnelService.list(
new QueryWrapper<ChainTunnel>().eq("tunnel_id", targetTunnel.getId()).eq("chain_type", 1) new QueryWrapper<ChainTunnel>().eq("tunnel_id", targetTunnel.getId()).eq("chain_type", 1)
); );
List<ChainTunnel> chainTunnelsWithPort = get_port(newChainTunnels, null, forwardId); List<ChainTunnel> chainTunnelsWithPort = allocatePortsForBatchTunnelChange(newChainTunnels, originalInPort, forwardId);
for (ChainTunnel chainTunnel : chainTunnelsWithPort) { for (ChainTunnel chainTunnel : chainTunnelsWithPort) {
ForwardPort forwardPort = new ForwardPort(); ForwardPort forwardPort = new ForwardPort();
@@ -1450,4 +1459,23 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
return R.ok(result); return R.ok(result);
} }
private List<ChainTunnel> allocatePortsForBatchTunnelChange(List<ChainTunnel> newChainTunnels, Integer originalInPort, Long forwardId) {
if (originalInPort == null) {
return get_port(newChainTunnels, null, forwardId);
}
try {
return get_port(newChainTunnels, originalInPort, forwardId);
} catch (RuntimeException originalPortError) {
try {
return get_port(newChainTunnels, null, forwardId);
} catch (RuntimeException autoAllocateError) {
throw new RuntimeException(
"原入口端口 " + originalInPort + " 在目标隧道不可用,自动分配新端口也失败: " + autoAllocateError.getMessage(),
autoAllocateError
);
}
}
}
} }
+13 -13
View File
@@ -1315,10 +1315,10 @@ export default function ForwardPage() {
setBatchDeleteModalOpen(false); setBatchDeleteModalOpen(false);
loadData(false); loadData(false);
} else { } else {
toast.error(res.msg || "批量删除失败"); toast.error(res.msg || "删除失败");
} }
} catch (e: any) { } catch (e: any) {
toast.error(e.message || "批量删除失败"); toast.error(e.message || "删除失败");
} finally { } finally {
setBatchLoading(false); setBatchLoading(false);
} }
@@ -1340,10 +1340,10 @@ export default function ForwardPage() {
setSelectMode(false); setSelectMode(false);
loadData(false); loadData(false);
} else { } else {
toast.error(res.msg || "批量重新下发失败"); toast.error(res.msg || "下发失败");
} }
} catch (e: any) { } catch (e: any) {
toast.error(e.message || "批量重新下发失败"); toast.error(e.message || "下发失败");
} finally { } finally {
setBatchLoading(false); setBatchLoading(false);
} }
@@ -1370,10 +1370,10 @@ export default function ForwardPage() {
setBatchTargetTunnelId(null); setBatchTargetTunnelId(null);
loadData(false); loadData(false);
} else { } else {
toast.error(res.msg || "批量换隧道失败"); toast.error(res.msg || "隧道失败");
} }
} catch (e: any) { } catch (e: any) {
toast.error(e.message || "批量换隧道失败"); toast.error(e.message || "隧道失败");
} finally { } finally {
setBatchLoading(false); setBatchLoading(false);
} }
@@ -1786,7 +1786,7 @@ export default function ForwardPage() {
color={selectMode ? "warning" : "default"} color={selectMode ? "warning" : "default"}
onPress={toggleSelectMode} onPress={toggleSelectMode}
> >
{selectMode ? "退出选择" : "批量操作"} {selectMode ? "退出" : "批量"}
</Button> </Button>
<Button size="sm" variant="flat" color="primary" onPress={handleAdd}> <Button size="sm" variant="flat" color="primary" onPress={handleAdd}>
@@ -1802,7 +1802,7 @@ export default function ForwardPage() {
全选 全选
</Button> </Button>
<Button size="sm" variant="flat" onPress={deselectAll}> <Button size="sm" variant="flat" onPress={deselectAll}>
取消全选 清空
</Button> </Button>
<Button <Button
size="sm" size="sm"
@@ -1810,7 +1810,7 @@ export default function ForwardPage() {
variant="flat" variant="flat"
onPress={() => setBatchDeleteModalOpen(true)} onPress={() => setBatchDeleteModalOpen(true)}
> >
批量删除 删除
</Button> </Button>
<Button <Button
size="sm" size="sm"
@@ -1819,7 +1819,7 @@ export default function ForwardPage() {
onPress={handleBatchRedeploy} onPress={handleBatchRedeploy}
isLoading={batchLoading} isLoading={batchLoading}
> >
批量重新下发 下发
</Button> </Button>
<Button <Button
size="sm" size="sm"
@@ -1827,7 +1827,7 @@ export default function ForwardPage() {
variant="flat" variant="flat"
onPress={() => setBatchChangeTunnelModalOpen(true)} onPress={() => setBatchChangeTunnelModalOpen(true)}
> >
批量换隧道 隧道
</Button> </Button>
</div> </div>
)} )}
@@ -3069,7 +3069,7 @@ export default function ForwardPage() {
<ModalContent> <ModalContent>
{(onClose) => ( {(onClose) => (
<> <>
<ModalHeader>确认批量删除</ModalHeader> <ModalHeader>确认删除</ModalHeader>
<ModalBody> <ModalBody>
<p>确定要删除选中的 {selectedIds.size} 项转发吗?此操作不可撤销。</p> <p>确定要删除选中的 {selectedIds.size} 项转发吗?此操作不可撤销。</p>
</ModalBody> </ModalBody>
@@ -3095,7 +3095,7 @@ export default function ForwardPage() {
<ModalContent> <ModalContent>
{(onClose) => ( {(onClose) => (
<> <>
<ModalHeader>批量换隧道</ModalHeader> <ModalHeader>隧道</ModalHeader>
<ModalBody> <ModalBody>
<p className="mb-4">将选中的 {selectedIds.size} 项转发迁移到新隧道:</p> <p className="mb-4">将选中的 {selectedIds.size} 项转发迁移到新隧道:</p>
<Select <Select
+4 -4
View File
@@ -939,7 +939,7 @@ export default function NodePage() {
setBatchDeleteModalOpen(false); setBatchDeleteModalOpen(false);
setSelectMode(false); setSelectMode(false);
} else { } else {
toast.error(res.msg || "批量删除失败"); toast.error(res.msg || "删除失败");
} }
} catch (error) { } catch (error) {
toast.error("网络错误,请重试"); toast.error("网络错误,请重试");
@@ -1041,7 +1041,7 @@ export default function NodePage() {
全选 全选
</Button> </Button>
<Button size="sm" variant="flat" onPress={deselectAll}> <Button size="sm" variant="flat" onPress={deselectAll}>
取消全选 清空
</Button> </Button>
<Button <Button
color="danger" color="danger"
@@ -1049,7 +1049,7 @@ export default function NodePage() {
variant="flat" variant="flat"
onPress={() => setBatchDeleteModalOpen(true)} onPress={() => setBatchDeleteModalOpen(true)}
> >
批量删除 删除
</Button> </Button>
</div> </div>
</div> </div>
@@ -1814,7 +1814,7 @@ export default function NodePage() {
{(onClose) => ( {(onClose) => (
<> <>
<ModalHeader className="flex flex-col gap-1"> <ModalHeader className="flex flex-col gap-1">
<h2 className="text-xl font-bold">确认批量删除</h2> <h2 className="text-xl font-bold">确认删除</h2>
</ModalHeader> </ModalHeader>
<ModalBody> <ModalBody>
<p> <p>
+9 -9
View File
@@ -696,10 +696,10 @@ export default function TunnelPage() {
setBatchDeleteModalOpen(false); setBatchDeleteModalOpen(false);
loadData(); loadData();
} else { } else {
toast.error(res.msg || "批量删除失败"); toast.error(res.msg || "删除失败");
} }
} catch (e: any) { } catch (e: any) {
toast.error(e.message || "批量删除失败"); toast.error(e.message || "删除失败");
} finally { } finally {
setBatchLoading(false); setBatchLoading(false);
} }
@@ -721,10 +721,10 @@ export default function TunnelPage() {
setSelectMode(false); setSelectMode(false);
loadData(); loadData();
} else { } else {
toast.error(res.msg || "批量重新下发失败"); toast.error(res.msg || "下发失败");
} }
} catch (e: any) { } catch (e: any) {
toast.error(e.message || "批量重新下发失败"); toast.error(e.message || "下发失败");
} finally { } finally {
setBatchLoading(false); setBatchLoading(false);
} }
@@ -845,7 +845,7 @@ export default function TunnelPage() {
color={selectMode ? "warning" : "default"} color={selectMode ? "warning" : "default"}
onPress={toggleSelectMode} onPress={toggleSelectMode}
> >
{selectMode ? "退出选择" : "批量操作"} {selectMode ? "退出" : "批量"}
</Button> </Button>
<Button color="primary" size="sm" variant="flat" onPress={handleAdd}> <Button color="primary" size="sm" variant="flat" onPress={handleAdd}>
新增 新增
@@ -860,7 +860,7 @@ export default function TunnelPage() {
全选 全选
</Button> </Button>
<Button size="sm" variant="flat" onPress={deselectAll}> <Button size="sm" variant="flat" onPress={deselectAll}>
取消全选 清空
</Button> </Button>
<Button <Button
size="sm" size="sm"
@@ -868,7 +868,7 @@ export default function TunnelPage() {
variant="flat" variant="flat"
onPress={() => setBatchDeleteModalOpen(true)} onPress={() => setBatchDeleteModalOpen(true)}
> >
批量删除 删除
</Button> </Button>
<Button <Button
size="sm" size="sm"
@@ -877,7 +877,7 @@ export default function TunnelPage() {
onPress={handleBatchRedeploy} onPress={handleBatchRedeploy}
isLoading={batchLoading} isLoading={batchLoading}
> >
批量重新下发 下发
</Button> </Button>
</div> </div>
)} )}
@@ -2514,7 +2514,7 @@ export default function TunnelPage() {
<ModalContent> <ModalContent>
{(onClose) => ( {(onClose) => (
<> <>
<ModalHeader>确认批量删除</ModalHeader> <ModalHeader>确认删除</ModalHeader>
<ModalBody> <ModalBody>
<p>确定要删除选中的 {selectedIds.size} 项隧道吗?此操作不可撤销,相关转发也将被删除。</p> <p>确定要删除选中的 {selectedIds.size} 项隧道吗?此操作不可撤销,相关转发也将被删除。</p>
</ModalBody> </ModalBody>