{"record":{"id":"0242b1486e1fb0ed","repo":"elunez/eladmin","slug":"error-0242b1","errorCode":null,"errorMessage":"上级不能为自己","messagePattern":"上级不能为自己","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/DeptServiceImpl.java","lineNumber":130,"sourceCode":"    @Transactional(rollbackFor = Exception.class)\n    public void create(Dept resources) {\n        deptRepository.save(resources);\n        // 计算子节点数目\n        resources.setSubCount(0);\n        // 清理缓存\n        updateSubCnt(resources.getPid());\n        // 清理自定义角色权限的datascope缓存\n        delCaches(resources.getPid());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void update(Dept resources) {\n        // 旧的部门\n        Long oldPid = findById(resources.getId()).getPid();\n        Long newPid = resources.getPid();\n        if(resources.getPid() != null && resources.getId().equals(resources.getPid())) {\n            throw new BadRequestException(\"上级不能为自己\");\n        }\n        Dept dept = deptRepository.findById(resources.getId()).orElseGet(Dept::new);\n        ValidationUtil.isNull( dept.getId(),\"Dept\",\"id\",resources.getId());\n        resources.setId(dept.getId());\n        deptRepository.save(resources);\n        // 更新父节点中子节点数目\n        updateSubCnt(oldPid);\n        updateSubCnt(newPid);\n        // 清理缓存\n        delCaches(resources.getId());\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void delete(Set<DeptDto> deptDtos) {\n        for (DeptDto deptDto : deptDtos) {\n            // 清理缓存\n            delCaches(deptDto.getId());","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/elunez/eladmin/blob/55fbf705956949697dbd68bf9003776609d3d029/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/DeptServiceImpl.java#L112-L148","documentation":"Thrown by DeptServiceImpl.update (line 130) on PUT /api/dept when the submitted pid equals the dept's own id — making the department its own parent. eladmin validates direct self-parenting before saving; deeper cycles (A→B→A) are NOT caught here and only surface later as broken tree queries.","triggerScenarios":"Editing a dept and selecting itself in the parent-dept tree picker; front-end defaulting the pid selector to the node being edited; migrating dept data where source ids and parent ids collide.","commonSituations":"Tree-select component not excluding the current node from selectable parents; admin drags a dept onto itself in a tree editor; hand-written SQL updates setting pid = id by accident.","solutions":["Pick a different parent (or null for a root dept) and resubmit.","Front end: filter the current node (and ideally its subtree) out of the parent options: options.filter(node => node.id !== form.id).","If importing data, pre-validate pid != id and no ancestor loops before calling the API."],"exampleFix":"// before\n<el-cascader :options=\"deptTree\" v-model=\"form.pid\" /> // includes self\n// after\n<el-cascader :options=\"deptTree.filter(d => d.id !== form.id)\" v-model=\"form.pid\" />","handlingStrategy":"validation","validationCode":"if (form.pid != null && form.pid === form.id) {\n  notifyError('上级不能为自己');\n  return;\n}\nawait axios.put('/api/dept', form);","typeGuard":"const pidIsValid = (form) => form.pid == null || form.pid !== form.id;","tryCatchPattern":null,"preventionTips":["Exclude the edited node from the parent tree-select options.","When importing dept data, validate pid != id (and no ancestor cycles) beforehand — the server only catches direct self-parenting."],"tags":["validation","tree-structure","eladmin","dept","self-reference"],"backgroundTag":null,"analyzedSha":"55fbf705956949697dbd68bf9003776609d3d029","analyzedAt":"2026-08-14T11:56:12.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}