From de8e487dff9422e488d7dc4799ad6c37e63e2e4a Mon Sep 17 00:00:00 2001 From: root Date: Sat, 31 Jan 2026 05:52:14 +0000 Subject: [PATCH] feat: add update-order APIs for tunnel and node --- .../com/admin/controller/NodeController.java | 30 +++- .../admin/controller/TunnelController.java | 12 ++ .../java/com/admin/service/NodeService.java | 20 ++- .../java/com/admin/service/TunnelService.java | 18 +- .../admin/service/impl/NodeServiceImpl.java | 48 +++++- .../admin/service/impl/TunnelServiceImpl.java | 31 +++- .../java/com/admin/OrderingUpdateTests.java | 162 ++++++++++++++++++ 7 files changed, 291 insertions(+), 30 deletions(-) create mode 100644 springboot-backend/src/test/java/com/admin/OrderingUpdateTests.java diff --git a/springboot-backend/src/main/java/com/admin/controller/NodeController.java b/springboot-backend/src/main/java/com/admin/controller/NodeController.java index 3a6f3de..3e7c1a8 100644 --- a/springboot-backend/src/main/java/com/admin/controller/NodeController.java +++ b/springboot-backend/src/main/java/com/admin/controller/NodeController.java @@ -54,12 +54,24 @@ public class NodeController extends BaseController { return nodeService.deleteNode(id); } - @LogAnnotation - @RequireRole - @PostMapping("/install") - public R getInstallCommand(@RequestBody Map params) { - Long id = Long.valueOf(params.get("id").toString()); - return nodeService.getInstallCommand(id); - } - -} + @LogAnnotation + @RequireRole + @PostMapping("/install") + public R getInstallCommand(@RequestBody Map params) { + Long id = Long.valueOf(params.get("id").toString()); + return nodeService.getInstallCommand(id); + } + + /** + * 更新节点排序 + * @param params 包含nodes数组的参数,每个元素包含id和inx + * @return 更新结果 + */ + @LogAnnotation + @RequireRole + @PostMapping("/update-order") + public R updateNodeOrder(@RequestBody Map params) { + return nodeService.updateNodeOrder(params); + } + +} diff --git a/springboot-backend/src/main/java/com/admin/controller/TunnelController.java b/springboot-backend/src/main/java/com/admin/controller/TunnelController.java index f0eefcb..96db499 100644 --- a/springboot-backend/src/main/java/com/admin/controller/TunnelController.java +++ b/springboot-backend/src/main/java/com/admin/controller/TunnelController.java @@ -137,4 +137,16 @@ public class TunnelController extends BaseController { return tunnelService.diagnoseTunnel(tunnelId); } + /** + * 更新隧道排序 + * @param params 包含tunnels数组的参数,每个元素包含id和inx + * @return 更新结果 + */ + @LogAnnotation + @RequireRole + @PostMapping("/update-order") + public R updateTunnelOrder(@RequestBody Map params) { + return tunnelService.updateTunnelOrder(params); + } + } diff --git a/springboot-backend/src/main/java/com/admin/service/NodeService.java b/springboot-backend/src/main/java/com/admin/service/NodeService.java index 0a70730..b3440cb 100644 --- a/springboot-backend/src/main/java/com/admin/service/NodeService.java +++ b/springboot-backend/src/main/java/com/admin/service/NodeService.java @@ -3,8 +3,10 @@ package com.admin.service; import com.admin.common.dto.NodeDto; import com.admin.common.dto.NodeUpdateDto; import com.admin.common.lang.R; -import com.admin.entity.Node; -import com.baomidou.mybatisplus.extension.service.IService; +import com.admin.entity.Node; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.Map; /** *

@@ -14,7 +16,7 @@ import com.baomidou.mybatisplus.extension.service.IService; * @author QAQ * @since 2025-06-03 */ -public interface NodeService extends IService { +public interface NodeService extends IService { R createNode(NodeDto nodeDto); @@ -24,6 +26,12 @@ public interface NodeService extends IService { R deleteNode(Long id); - R getInstallCommand(Long id); - -} + R getInstallCommand(Long id); + + /** + * 更新节点排序(管理员) + * @param params 包含nodes数组的参数,每个元素包含id和inx + */ + R updateNodeOrder(Map params); + +} diff --git a/springboot-backend/src/main/java/com/admin/service/TunnelService.java b/springboot-backend/src/main/java/com/admin/service/TunnelService.java index f949f57..6bd4b97 100644 --- a/springboot-backend/src/main/java/com/admin/service/TunnelService.java +++ b/springboot-backend/src/main/java/com/admin/service/TunnelService.java @@ -4,8 +4,10 @@ import com.admin.common.dto.TunnelDto; import com.admin.common.dto.TunnelUpdateDto; import com.admin.common.lang.R; -import com.admin.entity.Tunnel; -import com.baomidou.mybatisplus.extension.service.IService; +import com.admin.entity.Tunnel; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.Map; /** *

@@ -15,7 +17,7 @@ import com.baomidou.mybatisplus.extension.service.IService; * @author QAQ * @since 2025-06-03 */ -public interface TunnelService extends IService { +public interface TunnelService extends IService { /** * 创建隧道 @@ -55,5 +57,11 @@ public interface TunnelService extends IService { * @param tunnelId 隧道ID * @return 诊断结果 */ - R diagnoseTunnel(Long tunnelId); -} + R diagnoseTunnel(Long tunnelId); + + /** + * 更新隧道排序(管理员) + * @param params 包含tunnels数组的参数,每个元素包含id和inx + */ + R updateTunnelOrder(Map params); +} diff --git a/springboot-backend/src/main/java/com/admin/service/impl/NodeServiceImpl.java b/springboot-backend/src/main/java/com/admin/service/impl/NodeServiceImpl.java index 03a3823..b8f75e4 100644 --- a/springboot-backend/src/main/java/com/admin/service/impl/NodeServiceImpl.java +++ b/springboot-backend/src/main/java/com/admin/service/impl/NodeServiceImpl.java @@ -24,13 +24,15 @@ import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.HashMap; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; import java.util.regex.Pattern; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.transaction.annotation.Transactional; @Service @Slf4j @@ -68,12 +70,40 @@ public class NodeServiceImpl extends ServiceImpl implements No return R.ok(); } - @Override - public R getAllNodes() { - List nodeList = this.list(new QueryWrapper().orderByDesc("status")); - nodeList.forEach(node -> node.setSecret(null)); - return R.ok(nodeList); - } + @Override + public R getAllNodes() { + List nodeList = this.list(new QueryWrapper().orderByAsc("inx").orderByAsc("id")); + nodeList.forEach(node -> node.setSecret(null)); + return R.ok(nodeList); + } + + @Override + @Transactional + public R updateNodeOrder(Map params) { + if (!params.containsKey("nodes")) { + return R.err("缺少nodes参数"); + } + + @SuppressWarnings("unchecked") + List> nodesList = (List>) params.get("nodes"); + if (nodesList == null || nodesList.isEmpty()) { + return R.err("nodes参数不能为空"); + } + + List nodesToUpdate = new ArrayList<>(); + for (Map nodeData : nodesList) { + Long id = Long.valueOf(nodeData.get("id").toString()); + Integer inx = Integer.valueOf(nodeData.get("inx").toString()); + + Node node = new Node(); + node.setId(id); + node.setInx(inx); + nodesToUpdate.add(node); + } + + this.updateBatchById(nodesToUpdate); + return R.ok(); + } @Override public R updateNode(NodeUpdateDto nodeUpdateDto) { @@ -347,8 +377,8 @@ public class NodeServiceImpl extends ServiceImpl implements No ViteConfig viteConfig = viteConfigService.getOne(new QueryWrapper().eq("name", "ip")); if (viteConfig == null) return R.err("请先前往网站配置中设置ip"); StringBuilder command = new StringBuilder(); - command.append("curl -L https://github.com/bqlpfy/flux-panel/releases/download/2.0.7-beta/install.sh") - .append(" -o ./install.sh && chmod +x ./install.sh && "); + command.append("curl -L https://github.com/Sagit-chu/flux-panel/releases/latest/download/install.sh") + .append(" -o ./install.sh && chmod +x ./install.sh && "); String processedServerAddr = GostUtil.processServerAddress(viteConfig.getValue()); command.append("./install.sh") .append(" -a ").append(processedServerAddr) // 服务器地址 diff --git a/springboot-backend/src/main/java/com/admin/service/impl/TunnelServiceImpl.java b/springboot-backend/src/main/java/com/admin/service/impl/TunnelServiceImpl.java index a5eafaa..7620b26 100644 --- a/springboot-backend/src/main/java/com/admin/service/impl/TunnelServiceImpl.java +++ b/springboot-backend/src/main/java/com/admin/service/impl/TunnelServiceImpl.java @@ -18,6 +18,7 @@ import lombok.Data; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.util.*; @@ -252,7 +253,7 @@ public class TunnelServiceImpl extends ServiceImpl impleme @Override public R getAllTunnels() { - List tunnelList = this.list(); + List tunnelList = this.list(new QueryWrapper().orderByAsc("inx").orderByAsc("id")); // 查询所有隧道的ChainTunnel信息 List tunnelIds = tunnelList.stream() @@ -317,6 +318,34 @@ public class TunnelServiceImpl extends ServiceImpl impleme return R.ok(detailDtoList); } + @Override + @Transactional + public R updateTunnelOrder(Map params) { + if (!params.containsKey("tunnels")) { + return R.err("缺少tunnels参数"); + } + + @SuppressWarnings("unchecked") + List> tunnelsList = (List>) params.get("tunnels"); + if (tunnelsList == null || tunnelsList.isEmpty()) { + return R.err("tunnels参数不能为空"); + } + + List tunnelsToUpdate = new ArrayList<>(); + for (Map tunnelData : tunnelsList) { + Long id = Long.valueOf(tunnelData.get("id").toString()); + Integer inx = Integer.valueOf(tunnelData.get("inx").toString()); + + Tunnel tunnel = new Tunnel(); + tunnel.setId(id); + tunnel.setInx(inx); + tunnelsToUpdate.add(tunnel); + } + + this.updateBatchById(tunnelsToUpdate); + return R.ok(); + } + @Override public R updateTunnel(TunnelUpdateDto tunnelUpdateDto) { diff --git a/springboot-backend/src/test/java/com/admin/OrderingUpdateTests.java b/springboot-backend/src/test/java/com/admin/OrderingUpdateTests.java new file mode 100644 index 0000000..d412d34 --- /dev/null +++ b/springboot-backend/src/test/java/com/admin/OrderingUpdateTests.java @@ -0,0 +1,162 @@ +package com.admin; + +import com.admin.common.lang.R; +import com.admin.entity.Node; +import com.admin.entity.Tunnel; +import com.admin.service.NodeService; +import com.admin.service.TunnelService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest(properties = { + // use a local sqlite file for tests (resolved via ${DB_PATH} placeholder) + "DB_PATH=./target/test-gost-ordering.db", +}) +class OrderingUpdateTests { + + @Resource + private JdbcTemplate jdbcTemplate; + + @Resource + private NodeService nodeService; + + @Resource + private TunnelService tunnelService; + + @BeforeEach + void cleanup() { + // keep it simple; other tables may have foreign references in real runs + jdbcTemplate.execute("DELETE FROM node"); + jdbcTemplate.execute("DELETE FROM tunnel"); + } + + @Test + void updateNodeOrder_updatesInx() { + Node n1 = new Node(); + n1.setName("n1"); + n1.setSecret("s1"); + n1.setServerIp("127.0.0.1"); + n1.setPort("1000-2000"); + n1.setInterfaceName(""); + n1.setHttp(0); + n1.setTls(0); + n1.setSocks(0); + n1.setTcpListenAddr("[::]"); + n1.setUdpListenAddr("[::]"); + n1.setStatus(0); + n1.setInx(0); + long now = System.currentTimeMillis(); + n1.setCreatedTime(now); + n1.setUpdatedTime(now); + assertTrue(nodeService.save(n1)); + + Node n2 = new Node(); + n2.setName("n2"); + n2.setSecret("s2"); + n2.setServerIp("127.0.0.2"); + n2.setPort("1000-2000"); + n2.setInterfaceName(""); + n2.setHttp(0); + n2.setTls(0); + n2.setSocks(0); + n2.setTcpListenAddr("[::]"); + n2.setUdpListenAddr("[::]"); + n2.setStatus(0); + n2.setInx(0); + n2.setCreatedTime(now); + n2.setUpdatedTime(now); + assertTrue(nodeService.save(n2)); + + Node n3 = new Node(); + n3.setName("n3"); + n3.setSecret("s3"); + n3.setServerIp("127.0.0.3"); + n3.setPort("1000-2000"); + n3.setInterfaceName(""); + n3.setHttp(0); + n3.setTls(0); + n3.setSocks(0); + n3.setTcpListenAddr("[::]"); + n3.setUdpListenAddr("[::]"); + n3.setStatus(0); + n3.setInx(0); + n3.setCreatedTime(now); + n3.setUpdatedTime(now); + assertTrue(nodeService.save(n3)); + + List> nodes = new ArrayList<>(); + nodes.add(mapIdInx(n2.getId(), 0)); + nodes.add(mapIdInx(n1.getId(), 1)); + nodes.add(mapIdInx(n3.getId(), 2)); + + Map params = new HashMap<>(); + params.put("nodes", nodes); + + R res = nodeService.updateNodeOrder(params); + assertEquals(0, res.getCode()); + + assertEquals(1, nodeService.getById(n1.getId()).getInx()); + assertEquals(0, nodeService.getById(n2.getId()).getInx()); + assertEquals(2, nodeService.getById(n3.getId()).getInx()); + } + + @Test + void updateTunnelOrder_updatesInx() { + long now = System.currentTimeMillis(); + + Tunnel t1 = new Tunnel(); + t1.setName("t1"); + t1.setType(1); + t1.setFlow(1); + t1.setTrafficRatio(new BigDecimal("1.0")); + t1.setInIp(""); + t1.setStatus(1); + t1.setInx(0); + t1.setCreatedTime(now); + t1.setUpdatedTime(now); + assertTrue(tunnelService.save(t1)); + + Tunnel t2 = new Tunnel(); + t2.setName("t2"); + t2.setType(2); + t2.setFlow(2); + t2.setTrafficRatio(new BigDecimal("1.0")); + t2.setInIp(""); + t2.setStatus(1); + t2.setInx(0); + t2.setCreatedTime(now); + t2.setUpdatedTime(now); + assertTrue(tunnelService.save(t2)); + + List> tunnels = new ArrayList<>(); + tunnels.add(mapIdInx(t2.getId(), 0)); + tunnels.add(mapIdInx(t1.getId(), 1)); + + Map params = new HashMap<>(); + params.put("tunnels", tunnels); + + R res = tunnelService.updateTunnelOrder(params); + assertEquals(0, res.getCode()); + + assertEquals(1, tunnelService.getById(t1.getId()).getInx()); + assertEquals(0, tunnelService.getById(t2.getId()).getInx()); + } + + private static Map mapIdInx(Long id, int inx) { + Map m = new HashMap<>(); + m.put("id", id); + m.put("inx", inx); + return m; + } +}