复现步骤
- 创建一个节点,不要添加标签。
- 在订阅中选择该节点,此时拉取订阅是正常的。
- 创建一个新的节点,并且全部添加标签。
- 在订阅中选择该节点标签,拉取订阅时会发现只有之前第一个创建的节点,第二个节点死都无法显示。
原因
在第四步的时候,后端返回的节点列表只有打上标签的部分,此时 subscribe 的 nodes 字段存储了第二个步骤的节点ID,完成第四个步骤后,tags 字段更新成功,但是 nodes 字段并没有清空,因为后端的 update 是默认行为只更新的字段,前端请求的时候只附带了更新的 tag.
|
func (m *defaultSubscribeModel) Update(ctx context.Context, data *Subscribe, tx ...*gorm.DB) error { |
|
old, err := m.FindOne(ctx, data.Id) |
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { |
|
return err |
|
} |
|
err = m.ExecCtx(ctx, func(conn *gorm.DB) error { |
|
db := conn |
|
if len(tx) > 0 { |
|
db = tx[0] |
|
} |
|
return db.Save(data).Error |
|
}, m.getCacheKeys(old)...) |
|
return err |
|
} |
这就导致执行下述代码时候,此时的查询参数 NodeId 和 Tag 都会存在,两个条件会以 AND 关系组合在 SQL 查询中,也就是从指定的节点ID列表中,筛选出包含指定标签的节点,最终返回的只有第一个创建的节点。
|
func (m *customServerModel) FilterNodeList(ctx context.Context, params *FilterNodeParams) (int64, []*Node, error) { |
|
var nodes []*Node |
|
var total int64 |
|
query := m.WithContext(ctx).Model(&Node{}) |
|
if params == nil { |
|
params = &FilterNodeParams{ |
|
Page: 1, |
|
Size: 10, |
|
} |
|
} |
|
if params.Search != "" { |
|
s := "%" + params.Search + "%" |
|
query = query.Where("`name` LIKE ? OR `address` LIKE ? OR `tags` LIKE ? OR `port` LIKE ? ", s, s, s, s) |
|
} |
|
if len(params.NodeId) > 0 { |
|
query = query.Where("id IN ?", params.NodeId) |
|
} |
|
if len(params.ServerId) > 0 { |
|
query = query.Where("server_id IN ?", params.ServerId) |
|
} |
|
if len(params.Tag) > 0 { |
|
query = query.Scopes(InSet("tags", params.Tag)) |
|
} |
|
if params.Protocol != "" { |
|
query = query.Where("protocol = ?", params.Protocol) |
|
} |
|
|
|
if params.Enabled != nil { |
|
query = query.Where("enabled = ?", *params.Enabled) |
|
} |
|
|
|
if params.Preload { |
|
query = query.Preload("Server") |
|
} |
|
|
|
err := query.Count(&total).Order("sort ASC").Limit(params.Size).Offset((params.Page - 1) * params.Size).Find(&nodes).Error |
|
return total, nodes, err |
|
} |
生成 SQL 简单预览
SELECT * FROM nodes
WHERE id IN (node_id_list)
AND (tags字段包含指定的tag)
后果
- 后面的新添节点都打上了第三步骤的标签 ,拉取节点列表无论如何都只显示一个节点,也就是第一步创建的节点。
- 从 1.0 迁移 1.1 版本后,因为迁移代码并没有更新
tag 只更新了 nodes ,后续新添节点打上标签,也会遇到这个问题。
- 在订阅中勾选未打上的标签和打上标签的节点,订阅下发时会出现空白。
复现步骤
原因
在第四步的时候,后端返回的节点列表只有打上标签的部分,此时
subscribe的nodes字段存储了第二个步骤的节点ID,完成第四个步骤后,tags字段更新成功,但是nodes字段并没有清空,因为后端的update是默认行为只更新的字段,前端请求的时候只附带了更新的tag.server/internal/model/subscribe/default.go
Lines 117 to 130 in a5abbc1
这就导致执行下述代码时候,此时的查询参数
NodeId和Tag都会存在,两个条件会以 AND 关系组合在 SQL 查询中,也就是从指定的节点ID列表中,筛选出包含指定标签的节点,最终返回的只有第一个创建的节点。server/internal/model/node/model.go
Lines 75 to 112 in a5abbc1
生成 SQL 简单预览
后果
tag只更新了nodes,后续新添节点打上标签,也会遇到这个问题。