{"record":{"id":"5d0b895e0b84fa9b","repo":"flipped-aurora/gin-vue-admin","slug":"error-5d0b89","errorCode":null,"errorMessage":"不能将字典详情设置为自己或其子项的父级","messagePattern":"不能将字典详情设置为自己或其子项的父级","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/service/system/sys_dictionary_detail.go","lineNumber":84,"sourceCode":"\n//@author: [piexlmax](https://github.com/piexlmax)\n//@function: UpdateSysDictionaryDetail\n//@description: 更新字典详情数据\n//@param: sysDictionaryDetail *model.SysDictionaryDetail\n//@return: err error\n\nfunc (dictionaryDetailService *DictionaryDetailService) UpdateSysDictionaryDetail(ctx context.Context, sysDictionaryDetail *system.SysDictionaryDetail) (err error) {\n\t// 如果更新了父级ID，需要重新计算层级和路径\n\tif sysDictionaryDetail.ParentID != nil {\n\t\tvar parent system.SysDictionaryDetail\n\t\terr = global.GVA_DB.WithContext(ctx).First(&parent, *sysDictionaryDetail.ParentID).Error\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// 检查循环引用\n\t\tif dictionaryDetailService.checkCircularReference(ctx, sysDictionaryDetail.ID, *sysDictionaryDetail.ParentID) {\n\t\t\treturn fmt.Errorf(\"不能将字典详情设置为自己或其子项的父级\")\n\t\t}\n\n\t\tsysDictionaryDetail.Level = parent.Level + 1\n\t\tif parent.Path == \"\" {\n\t\t\tsysDictionaryDetail.Path = strconv.Itoa(int(parent.ID))\n\t\t} else {\n\t\t\tsysDictionaryDetail.Path = parent.Path + \",\" + strconv.Itoa(int(parent.ID))\n\t\t}\n\t} else {\n\t\tsysDictionaryDetail.Level = 0\n\t\tsysDictionaryDetail.Path = \"\"\n\t}\n\n\terr = global.GVA_DB.WithContext(ctx).Save(sysDictionaryDetail).Error\n\tif err != nil {\n\t\treturn err\n\t}\n","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/service/system/sys_dictionary_detail.go#L66-L102","documentation":"UpdateSysDictionaryDetail rejects updates that would make a detail's parent be itself or one of its own descendants, which would create a cycle in the parent/child hierarchy. It uses checkCircularReference to walk the proposed parent's ancestry chain.","triggerScenarios":"Calling UpdateSysDictionaryDetail with a ParentID equal to the detail's own ID, or pointing at any detail whose ancestor chain contains the detail being updated.","commonSituations":"Bulk edits or drag-and-drop tree UIs moving a subtree under its own descendant; scripts re-parenting details without cycle checks; concurrent edits creating transient loops.","solutions":["Choose a ParentID outside the detail's own subtree","Verify the proposed parent's Path does not start with the detail's Path before saving","In UI, disable drop targets that are the node itself or its descendants"],"exampleFix":"// before\nawait updateDetail({ id: node.id, parentId: node.id }) // cycle\n// after\nconst parent = await getDetail(proposedParentId)\nif (!parent.path.startsWith(`${node.path}/`)) {\n  await updateDetail({ id: node.id, parentId: proposedParentId })\n}","handlingStrategy":"validation","validationCode":"const target = detailMap[proposedParentId]\nif (target.path.startsWith(`${node.path}/`)) {\n  throw new Error('cannot move node into its own subtree')\n}","typeGuard":null,"tryCatchPattern":"try {\n  await updateDetail(payload)\n} catch (e) {\n  if (e.message.includes('父级')) {\n    ElMessage.error('不能移动到自身或子节点')\n  }\n}","preventionTips":["Use the tree component's built-in drop restrictions","Keep node.path authoritative for hierarchy checks"],"tags":["go","dictionary","circular-reference","business-rule"],"backgroundTag":"circular-reference-detected","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}