Skip to content

Commit 767ab59

Browse files
committed
fix: tunnel sorts modify bug
1 parent 1524944 commit 767ab59

File tree

4 files changed

+134
-81
lines changed

4 files changed

+134
-81
lines changed

internal/api/tunnel.go

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -453,10 +453,12 @@ func (h *TunnelHandler) HandlePatchTunnels(c *gin.Context) {
453453
InstanceID string `json:"instanceId"`
454454
// 用于重命名
455455
ID int64 `json:"id"`
456-
// 操作类型:start | stop | restart | rename
456+
// 操作类型:start | stop | restart | rename | updateSort
457457
Action string `json:"action"`
458458
// 当 action 为 rename 时的新名称
459459
Name string `json:"name"`
460+
// 当 action 为 updateSort 时的新权重值
461+
Sorts *int `json:"sorts"`
460462
}
461463

462464
if err := c.ShouldBindJSON(&raw); err != nil {
@@ -556,10 +558,32 @@ func (h *TunnelHandler) HandlePatchTunnels(c *gin.Context) {
556558
Message: "隧道重命名成功",
557559
})
558560

561+
case "updateSort":
562+
if raw.ID == 0 {
563+
c.JSON(http.StatusBadRequest, tunnel.TunnelResponse{
564+
Success: false,
565+
Error: "更新权重操作需提供有效的 id",
566+
})
567+
return
568+
}
569+
570+
if err := h.tunnelService.UpdateTunnelSort(raw.ID, raw.Sorts); err != nil {
571+
c.JSON(http.StatusBadRequest, tunnel.TunnelResponse{
572+
Success: false,
573+
Error: err.Error(),
574+
})
575+
return
576+
}
577+
578+
c.JSON(http.StatusOK, tunnel.TunnelResponse{
579+
Success: true,
580+
Message: "隧道权重更新成功",
581+
})
582+
559583
default:
560584
c.JSON(http.StatusBadRequest, tunnel.TunnelResponse{
561585
Success: false,
562-
Error: "无效的操作类型,支持: start, stop, restart, reset, rename",
586+
Error: "无效的操作类型,支持: start, stop, restart, reset, rename, updateSort",
563587
})
564588
}
565589
}
@@ -3221,13 +3245,11 @@ func (h *TunnelHandler) HandleUpdateInstanceTags(c *gin.Context) {
32213245

32223246
// HandleUpdateTunnelsSorts 批量更新隧道排序 (POST /api/tunnels/sorts)
32233247
func (h *TunnelHandler) HandleUpdateTunnelsSorts(c *gin.Context) {
3224-
var req tunnel.UpdateTunnelsSortsRequest
3225-
if err := c.ShouldBindJSON(&req); err != nil {
3226-
c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误: " + err.Error()})
3227-
return
3228-
}
3229-
3230-
if err := h.tunnelService.UpdateTunnelsSorts(&req); err != nil {
3248+
idStr := c.Param("id")
3249+
id, _ := strconv.ParseInt(idStr, 10, 64)
3250+
sortsStr := c.Param("sorts")
3251+
sorts, _ := strconv.ParseInt(sortsStr, 10, 64)
3252+
if err := h.tunnelService.UpdateTunnelsSorts(id, sorts); err != nil {
32313253
c.JSON(http.StatusInternalServerError, gin.H{"error": "更新排序失败: " + err.Error()})
32323254
return
32333255
}

internal/nodepass/parse.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,6 @@ func TunnelToMap(tunnel *models.Tunnel) map[string]interface{} {
307307
"proxy_protocol": tunnel.ProxyProtocol,
308308
"config_line": tunnel.ConfigLine,
309309
"listen_type": tunnel.ListenType,
310-
"sorts": tunnel.Sorts,
311310
}
312311

313312
if tunnel.CertPath != nil {

internal/tunnel/service.go

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,7 @@ func (s *Service) NewCreateTunnelAndWait(req Tunnel, timeout time.Duration) (*Tu
13061306
updateFields := map[string]interface{}{
13071307
"name": req.Name,
13081308
"updated_at": now,
1309+
"sorts": req.Sorts,
13091310
}
13101311

13111312
err = s.db.Model(&models.Tunnel{}).Where("id = ?", tunnelID).Updates(updateFields).Error
@@ -1568,6 +1569,42 @@ func (s *Service) RenameTunnel(id int64, newName string) error {
15681569
return nil
15691570
}
15701571

1572+
// UpdateTunnelSort 更新隧道权重
1573+
func (s *Service) UpdateTunnelSort(id int64, sorts *int) error {
1574+
log.Infof("[API] 更新隧道权重: ID=%d, Sorts=%v", id, sorts)
1575+
1576+
// 获取隧道信息确认存在
1577+
var tunnel models.Tunnel
1578+
if err := s.db.Where("id = ?", id).First(&tunnel).Error; err != nil {
1579+
if err == gorm.ErrRecordNotFound {
1580+
return errors.New("隧道不存在")
1581+
}
1582+
return err
1583+
}
1584+
1585+
// 处理sorts值,如果为nil则设置为0
1586+
sortValue := 0
1587+
if sorts != nil {
1588+
sortValue = *sorts
1589+
}
1590+
1591+
// 更新本地数据库的sorts字段
1592+
result := s.db.Model(&models.Tunnel{}).Where("id = ?", id).Updates(map[string]interface{}{
1593+
"sorts": sortValue,
1594+
"updated_at": time.Now(),
1595+
})
1596+
if result.Error != nil {
1597+
return result.Error
1598+
}
1599+
if result.RowsAffected == 0 {
1600+
return errors.New("隧道不存在")
1601+
}
1602+
1603+
log.Infof("[API] 隧道权重更新成功: ID=%d, Sorts=%d", id, sortValue)
1604+
1605+
return nil
1606+
}
1607+
15711608
// DB 返回底层 *sql.DB 指针,供需要直接执行查询的调用者使用
15721609
func (s *Service) DB() *sql.DB {
15731610
sqlDB, err := s.db.DB()
@@ -2770,55 +2807,24 @@ func (s *Service) getEndpointWithGroup(endpointID int) (struct {
27702807
return endpoint, nil
27712808
}
27722809

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{}
2810+
// UpdateTunnelsSorts 更新单个隧道排序
2811+
func (s *Service) UpdateTunnelsSorts(id, sorts int64) error {
2812+
log.Infof("[API] 更新隧道排序: ID=%d, Sorts=%d", id, sorts)
28012813

2802-
for _, item := range req.Tunnels {
2803-
caseSQL += " WHEN ? THEN ?"
2804-
args = append(args, item.ID, item.Sorts)
2805-
ids = append(ids, item.ID)
2814+
// 更新本地数据库的sorts字段
2815+
result := s.db.Model(&models.Tunnel{}).Where("id = ?", id).Updates(map[string]interface{}{
2816+
"sorts": sorts,
2817+
"updated_at": time.Now(),
2818+
})
2819+
if result.Error != nil {
2820+
return result.Error
28062821
}
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)
2822+
if result.RowsAffected == 0 {
2823+
return errors.New("隧道不存在")
28142824
}
28152825

2816-
// 提交事务
2817-
if err := tx.Commit().Error; err != nil {
2818-
return fmt.Errorf("提交事务失败: %w", err)
2819-
}
2826+
log.Infof("[API] 隧道排序更新成功: ID=%d, Sorts=%d", id, sorts)
28202827

2821-
log.Infof("[Tunnel] 批量更新 %d 个隧道的排序成功", len(req.Tunnels))
28222828
return nil
28232829
}
28242830

web/src/components/tunnels/simple-create-tunnel-modal.tsx

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { motion, AnimatePresence } from "framer-motion";
3636

3737
import { buildApiUrl } from "@/lib/utils";
3838
import RenameTunnelModal from "./rename-tunnel-modal";
39+
import UpdateSortModal from "./update-sort-modal";
3940

4041
interface EndpointSimple {
4142
id: string;
@@ -123,6 +124,8 @@ export default function SimpleCreateTunnelModal({
123124
const [isEnableLoadBalancing, setEnableLoadBalancing] = useState(false);
124125
// 重命名modal状态
125126
const [renameModalOpen, setRenameModalOpen] = useState(false);
127+
// 修改权重modal状态
128+
const [updateSortModalOpen, setUpdateSortModalOpen] = useState(false);
126129

127130
// 表单数据
128131
const [formData, setFormData] = useState({
@@ -501,6 +504,34 @@ export default function SimpleCreateTunnelModal({
501504
);
502505
}, [formData.proxyProtocol, handleField]);
503506

507+
// 缓存权重字段渲染结果
508+
const sortInput = useMemo(() => {
509+
return (
510+
<Input
511+
readOnly={modalMode === "edit"}
512+
label="权重(越大越前)"
513+
placeholder="0"
514+
type="number"
515+
value={formData.sorts}
516+
onValueChange={(v) => handleField("sorts", v ? String(v) : "")}
517+
endContent={
518+
modalMode === "edit" && (
519+
<button
520+
className="focus:outline-none"
521+
type="button"
522+
onClick={() => setUpdateSortModalOpen(true)}
523+
>
524+
<FontAwesomeIcon
525+
className="text-sm text-default-400 pointer-events-none"
526+
icon={faPen}
527+
/>
528+
</button>
529+
)
530+
}
531+
/>
532+
);
533+
}, [modalMode, formData.sorts, handleField]);
534+
504535
return (
505536
<Modal
506537
isOpen={isOpen}
@@ -886,32 +917,20 @@ export default function SimpleCreateTunnelModal({
886917
onValueChange={(v) => handleField("min", v ? String(v) : "")}
887918
/>
888919
{proxyProtocolSelect}
889-
<Input
890-
label="权重(越大越前)"
891-
placeholder="0"
892-
type="number"
893-
value={formData.sorts}
894-
onValueChange={(v) => handleField("sorts", v ? String(v) : "")}
895-
/>
896-
<Input
897-
label="Dial"
898-
placeholder="出站源IP地址"
899-
value={formData.dial}
900-
onValueChange={(v) => handleField("dial", v ? String(v) : "")}
901-
/>
920+
{sortInput}
921+
<Input
922+
label="Dial"
923+
placeholder="出站源IP地址"
924+
value={formData.dial}
925+
onValueChange={(v) => handleField("dial", v ? String(v) : "")}
926+
/>
902927
</>
903928
)}
904929
{isClientType &&
905930
formData.mode === 1 && (
906931
<>
907932
{proxyProtocolSelect}
908-
<Input
909-
label="权重(越大越前)"
910-
placeholder="0"
911-
type="number"
912-
value={formData.sorts}
913-
onValueChange={(v) => handleField("sorts", v ? String(v) : "")}
914-
/>
933+
{sortInput}
915934
<Input
916935
label="Dial"
917936
placeholder="出站源IP地址"
@@ -981,13 +1000,7 @@ export default function SimpleCreateTunnelModal({
9811000
<SelectItem key="false">关闭</SelectItem>
9821001
<SelectItem key="true">启用</SelectItem>
9831002
</Select>
984-
<Input
985-
label="权重(越大越前)"
986-
placeholder="0"
987-
type="number"
988-
value={formData.sorts}
989-
onValueChange={(v) => handleField("sorts", v ? String(v) : "")}
990-
/>
1003+
{sortInput}
9911004
<Input
9921005
label="Dial"
9931006
placeholder="出站源IP地址"
@@ -1072,6 +1085,19 @@ export default function SimpleCreateTunnelModal({
10721085
}}
10731086
/>
10741087
)}
1088+
1089+
{/* 修改权重模态框 */}
1090+
{modalMode === "edit" && tunnelId && (
1091+
<UpdateSortModal
1092+
isOpen={updateSortModalOpen}
1093+
tunnelId={String(realTunnelId)}
1094+
currentSort={formData.sorts || "0"}
1095+
onOpenChange={setUpdateSortModalOpen}
1096+
onUpdated={(newSort) => {
1097+
setFormData((prev) => ({ ...prev, sorts: newSort }));
1098+
}}
1099+
/>
1100+
)}
10751101
</Modal>
10761102
);
10771103
}

0 commit comments

Comments
 (0)