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
+73 -28

No files matched your search

+71 -27
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"})
@@ -47,14 +48,16 @@ func UpdateInstanceGroup(c *gin.Context) {
}
var input struct {
Name *string `json:"name"`
Description *string `json:"description"`
DBType *string `json:"db_type"`
Permissions *string `json:"permissions"`
IsActive *bool `json:"is_active"`
WorkflowID *int `json:"workflow_id"`
AccessType *string `json:"access_type"`
AllowedDatabases *string `json:"allowed_databases"`
Name *string `json:"name"`
Description *string `json:"description"`
DBType *string `json:"db_type"`
Permissions *string `json:"permissions"`
IsActive *bool `json:"is_active"`
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,14 +90,41 @@ 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
}
}
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)
@@ -263,19 +293,33 @@ func enrichGroup(group model.InstanceGroup) gin.H {
}
return gin.H{
"id": group.ID,
"name": group.Name,
"description": group.Description,
"db_type": group.DBType,
"permissions": group.Permissions,
"is_active": group.IsActive,
"created_at": group.CreatedAt,
"updated_at": group.UpdatedAt,
"workflow_id": group.WorkflowID,
"access_type": group.AccessType,
"allowed_databases": group.AllowedDatabases,
"id": group.ID,
"name": group.Name,
"description": group.Description,
"db_type": group.DBType,
"permissions": group.Permissions,
"is_active": group.IsActive,
"created_at": group.CreatedAt,
"updated_at": group.UpdatedAt,
"workflow_id": group.WorkflowID,
"access_type": group.AccessType,
"allowed_databases": group.AllowedDatabases,
"readonly_instance_count": readonlyCount,
"write_instance_count": writeCount,
"user_count": userCount,
"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">