fix: 实例分组编辑保存实例关联 — UpdateInstanceGroup 接收 readonly/write_instance_ids

- UpdateInstanceGroup 新增 readonly_instance_ids/write_instance_ids 字段处理
- 清除旧成员后重新写入 instance_group_members 表
- enrichGroup 返回 readonly_instance_ids/write_instance_ids 供前端穿梭框回显
- 新增 collectInstanceIDs helper 去重提取 ID 列表
This commit is contained in:
logan committed 2026-05-25 23:47:21 +08:00
1 parent 2957075345
commit e5af40466c
2 files changed
+51 -6

No files matched your search

+49 -5
View File
@@ -40,6 +40,7 @@ func CreateInstanceGroup(c *gin.Context) {
// PUT /api/v1/instance-groups/:id
func UpdateInstanceGroup(c *gin.Context) {
id := c.Param("id")
gid, _ := strconv.Atoi(id)
var group model.InstanceGroup
if err := model.DB.First(&group, id).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"detail": "Instance group not found"})
@@ -55,6 +56,8 @@ func UpdateInstanceGroup(c *gin.Context) {
WorkflowID *int `json:"workflow_id"`
AccessType *string `json:"access_type"`
AllowedDatabases *string `json:"allowed_databases"`
ReadonlyInstanceIDs *[]int `json:"readonly_instance_ids"`
WriteInstanceIDs *[]int `json:"write_instance_ids"`
}
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()})
@@ -87,15 +90,42 @@ func UpdateInstanceGroup(c *gin.Context) {
updates["allowed_databases"] = *input.AllowedDatabases
}
if len(updates) == 0 {
c.JSON(http.StatusOK, enrichGroup(group))
return
}
if len(updates) > 0 {
if err := model.DB.Model(&group).Updates(updates).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"detail": err.Error()})
return
}
}
// Update instance members if provided
if input.ReadonlyInstanceIDs != nil || input.WriteInstanceIDs != nil {
// Clear all existing members for this group
model.DB.Where("group_id = ?", gid).Delete(&model.InstanceGroupMember{})
// Add readonly instances
if input.ReadonlyInstanceIDs != nil {
for _, iid := range *input.ReadonlyInstanceIDs {
member := model.InstanceGroupMember{
GroupID: gid,
InstanceID: iid,
AccessType: "readonly",
}
model.DB.Create(&member)
}
}
// Add write instances
if input.WriteInstanceIDs != nil {
for _, iid := range *input.WriteInstanceIDs {
member := model.InstanceGroupMember{
GroupID: gid,
InstanceID: iid,
AccessType: "write",
}
model.DB.Create(&member)
}
}
}
model.DB.First(&group, id)
c.JSON(http.StatusOK, enrichGroup(group))
@@ -277,5 +307,19 @@ func enrichGroup(group model.InstanceGroup) gin.H {
"readonly_instance_count": readonlyCount,
"write_instance_count": writeCount,
"user_count": userCount,
"readonly_instance_ids": collectInstanceIDs(readonlyMembers),
"write_instance_ids": collectInstanceIDs(writeMembers),
}
}
func collectInstanceIDs(members []model.InstanceGroupMember) []int {
ids := []int{}
seen := map[int]bool{}
for _, m := range members {
if !seen[m.InstanceID] {
seen[m.InstanceID] = true
ids = append(ids, m.InstanceID)
}
}
return ids
}
+2 -1
View File
@@ -635,7 +635,8 @@ export default function WorkflowPage() {
<div className="flex items-center gap-1 mt-4 p-4 bg-[var(--dg-bg-tertiary)] rounded-lg overflow-x-auto">
{(wf.steps || []).map((step, i) => {
const StepIcon = STEP_TYPE_ICONS[step.step_type] || Shield;
const uids: number[] = step.approver_ids ? JSON.parse(step.approver_ids) : [];
let uids: number[] = [];
try { const parsed = JSON.parse(step.approver_ids); if (Array.isArray(parsed)) uids = parsed; } catch {}
const stepsArr = wf.steps || [];
return (
<div key={step.id || i} className="flex items-center gap-1 flex-shrink-0">