@@ -56,7 +56,7 @@ func (s *Service) GetTunnels() ([]TunnelWithStats, error) {
5656 COALESCE(e.ver, '') as version
5757 FROM tunnels t
5858 LEFT JOIN endpoints e ON t.endpoint_id = e.id
59- ORDER BY t.created_at DESC
59+ ORDER BY t.sorts DESC, t.id DESC
6060 `
6161
6262 rows , err := sqlDB .Query (query )
@@ -214,6 +214,10 @@ func (s *Service) CreateTunnel(req CreateTunnelRequest) (*Tunnel, error) {
214214 var tunnelID int64
215215
216216 if err == gorm .ErrRecordNotFound {
217+ // 查询当前最大 sorts 值并 +1(自动设置排序)
218+ var maxSorts int64
219+ tx .Model (& models.Tunnel {}).Select ("COALESCE(MAX(sorts), -1)" ).Scan (& maxSorts )
220+
217221 // 创建新记录
218222 newTunnel := models.Tunnel {
219223 InstanceID : & response .ID ,
@@ -229,10 +233,13 @@ func (s *Service) CreateTunnel(req CreateTunnelRequest) (*Tunnel, error) {
229233 CommandLine : commandLine ,
230234 Restart : & req .Restart ,
231235 Status : models .TunnelStatusRunning ,
236+ Sorts : maxSorts + 1 ,
232237 CreatedAt : now ,
233238 UpdatedAt : now ,
234239 }
235240
241+ log .Infof ("[API] 新隧道自动设置 sorts=%d" , newTunnel .Sorts )
242+
236243 // 处理可选字段
237244 if req .CertPath != "" {
238245 newTunnel .CertPath = & req .CertPath
@@ -2431,24 +2438,24 @@ func (s *Service) GetTunnelsWithPagination(params TunnelQueryParams) (*TunnelLis
24312438 if params .SortBy != "" {
24322439 switch params .SortBy {
24332440 case "name" :
2434- orderClause = fmt .Sprintf (" ORDER BY t.name %s" , params .SortOrder )
2441+ orderClause = fmt .Sprintf (" ORDER BY t.name %s, t.sorts DESC, t.id DESC " , params .SortOrder )
24352442 case "created_at" :
2436- orderClause = fmt .Sprintf (" ORDER BY t.created_at %s" , params .SortOrder )
2443+ orderClause = fmt .Sprintf (" ORDER BY t.created_at %s, t.sorts DESC, t.id DESC " , params .SortOrder )
24372444 case "status" :
2438- orderClause = fmt .Sprintf (" ORDER BY t.status %s" , params .SortOrder )
2445+ orderClause = fmt .Sprintf (" ORDER BY t.status %s, t.sorts DESC, t.id DESC " , params .SortOrder )
24392446 case "tunnelAddress" :
2440- orderClause = fmt .Sprintf (" ORDER BY t.tunnel_address %s, t.tunnel_port %s" , params .SortOrder , params .SortOrder )
2447+ orderClause = fmt .Sprintf (" ORDER BY t.tunnel_address %s, t.tunnel_port %s, t.sorts DESC, t.id DESC " , params .SortOrder , params .SortOrder )
24412448 case "targetAddress" :
2442- orderClause = fmt .Sprintf (" ORDER BY t.target_address %s, t.target_port %s" , params .SortOrder , params .SortOrder )
2449+ orderClause = fmt .Sprintf (" ORDER BY t.target_address %s, t.target_port %s, t.sorts DESC, t.id DESC " , params .SortOrder , params .SortOrder )
24432450 case "type" :
2444- orderClause = fmt .Sprintf (" ORDER BY t.type %s" , params .SortOrder )
2451+ orderClause = fmt .Sprintf (" ORDER BY t.type %s, t.sorts DESC, t.id DESC " , params .SortOrder )
24452452 case "updated_at" :
2446- orderClause = fmt .Sprintf (" ORDER BY t.updated_at %s" , params .SortOrder )
2453+ orderClause = fmt .Sprintf (" ORDER BY t.updated_at %s, t.sorts DESC, t.id DESC " , params .SortOrder )
24472454 default :
2448- orderClause = " ORDER BY t.created_at DESC"
2455+ orderClause = " ORDER BY t.sorts DESC, t.id DESC"
24492456 }
24502457 } else {
2451- orderClause = " ORDER BY t.created_at DESC"
2458+ orderClause = " ORDER BY t.sorts DESC, t.id DESC"
24522459 }
24532460
24542461 // 构建分页
@@ -2763,6 +2770,58 @@ func (s *Service) getEndpointWithGroup(endpointID int) (struct {
27632770 return endpoint , nil
27642771}
27652772
2773+ // UpdateTunnelsSorts 批量更新隧道排序(优化版:使用 CASE WHEN 单条 SQL)
2774+ func (s * Service ) UpdateTunnelsSorts (req * UpdateTunnelsSortsRequest ) error {
2775+ if len (req .Tunnels ) == 0 {
2776+ return nil
2777+ }
2778+
2779+ // 开启事务
2780+ tx := s .db .Begin ()
2781+ if tx .Error != nil {
2782+ return fmt .Errorf ("开启事务失败: %w" , tx .Error )
2783+ }
2784+ defer func () {
2785+ if r := recover (); r != nil {
2786+ tx .Rollback ()
2787+ }
2788+ }()
2789+
2790+ // 构建批量更新 SQL(使用 CASE WHEN)
2791+ // UPDATE tunnels SET sorts = CASE id
2792+ // WHEN 1 THEN 10
2793+ // WHEN 2 THEN 9
2794+ // ELSE sorts
2795+ // END
2796+ // WHERE id IN (1, 2, ...)
2797+
2798+ var caseSQL string
2799+ var ids []int64
2800+ var args []interface {}
2801+
2802+ for _ , item := range req .Tunnels {
2803+ caseSQL += " WHEN ? THEN ?"
2804+ args = append (args , item .ID , item .Sorts )
2805+ ids = append (ids , item .ID )
2806+ }
2807+
2808+ sql := fmt .Sprintf ("UPDATE tunnels SET sorts = CASE id %s ELSE sorts END WHERE id IN (?)" , caseSQL )
2809+
2810+ // 执行批量更新
2811+ if err := tx .Exec (sql , append (args , ids )... ).Error ; err != nil {
2812+ tx .Rollback ()
2813+ return fmt .Errorf ("批量更新隧道排序失败: %w" , err )
2814+ }
2815+
2816+ // 提交事务
2817+ if err := tx .Commit ().Error ; err != nil {
2818+ return fmt .Errorf ("提交事务失败: %w" , err )
2819+ }
2820+
2821+ log .Infof ("[Tunnel] 批量更新 %d 个隧道的排序成功" , len (req .Tunnels ))
2822+ return nil
2823+ }
2824+
27662825// getTunnelsByIDs 根据ID列表获取隧道信息
27672826func (s * Service ) getTunnelsByIDs (tunnelIDs []int ) ([]models.Tunnel , error ) {
27682827 var tunnels []models.Tunnel
0 commit comments