{"record":{"id":"d62b04b038d2f600","repo":"flipped-aurora/gin-vue-admin","slug":"error-d62b04","errorCode":null,"errorMessage":"父部门不存在","messagePattern":"父部门不存在","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/service/system/sys_department.go","lineNumber":25,"sourceCode":"\t\"strings\"\n\n\t\"github.com/flipped-aurora/gin-vue-admin/server/global\"\n\t\"github.com/flipped-aurora/gin-vue-admin/server/model/system\"\n\t\"gorm.io/gorm\"\n)\n\ntype SysDepartmentService struct{}\n\nvar SysDepartmentServiceApp = new(SysDepartmentService)\n\n// buildAncestors 依据父部门推算祖级链: 顶级为 \"0\", 子级为 父.Ancestors + \",\" + 父ID\nfunc (s *SysDepartmentService) buildAncestors(ctx context.Context, parentId uint) (string, error) {\n\tif parentId == 0 {\n\t\treturn \"0\", nil\n\t}\n\tvar parent system.SysDepartment\n\tif err := global.GVA_DB.WithContext(ctx).First(&parent, parentId).Error; err != nil {\n\t\treturn \"\", errors.New(\"父部门不存在\")\n\t}\n\treturn parent.Ancestors + \",\" + strconv.Itoa(int(parent.ID)), nil\n}\n\n// buildDepartmentNamePath 依据部门自身与 id→name 映射, 拼出 \"公司/部门\" 全路径名(纯函数)\n// dept.Ancestors 为祖级 ID 链(顶级 \"0\"、不含自身, 如 \"0,1,5\"); \"0\" 与映射中缺失的祖级会被跳过\nfunc buildDepartmentNamePath(dept system.SysDepartment, nameByID map[uint]string) string {\n\tvar parts []string\n\tfor _, seg := range strings.Split(dept.Ancestors, \",\") {\n\t\tseg = strings.TrimSpace(seg)\n\t\tif seg == \"\" || seg == \"0\" {\n\t\t\tcontinue\n\t\t}\n\t\tid, err := strconv.ParseUint(seg, 10, 64)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif name := nameByID[uint(id)]; name != \"\" {","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/service/system/sys_department.go#L7-L43","documentation":"buildAncestors looks up the parent department to compose the ancestor ID chain for a new/updated department. When the given parentId does not exist in sys_department, GORM returns record-not-found and the service replaces it with this clearer error. It prevents creating departments pointing at non-existent parents.","triggerScenarios":"CreateSysDepartment or UpdateSysDepartment called with a ParentId that is non-zero and not present in the database (e.g. parent deleted concurrently, wrong ID, stale form data).","commonSituations":"Client submitted a department form where the parent was deleted by another user; API consumer passing parentId of another tenant/dept scope filtered out by data permissions; seeded data referencing deleted IDs.","solutions":["Verify the parentId exists (refresh the department tree in the frontend and re-submit)","If creating a top-level department, send parentId=0 instead of a stale ID","Check data-permission filters aren't hiding the parent row for the current user","Wrap parent+child creation in one transaction to avoid the parent disappearing mid-operation"],"exampleFix":"// before\nawait createDepartment({ name: 'B', parentId: oldId })\n// after\nconst parent = await getDepartment(oldId)\nif (!parent) throw new Error('parent missing; refresh tree')\nawait createDepartment({ name: 'B', parentId: parent.ID })","handlingStrategy":"validation","validationCode":"var count int64\nglobal.GVA_DB.Model(&system.SysDepartment{}).Where(\"id = ?\", parentId).Count(&count)\nif parentId != 0 && count == 0 {\n    return errors.New(\"parent department does not exist\")\n}","typeGuard":null,"tryCatchPattern":"if err := deptService.CreateSysDepartment(ctx, dept); err != nil {\n    if err.Error() == \"父部门不存在\" {\n        // refresh tree data and ask user to reselect parent\n    }\n    return err\n}","preventionTips":["Refresh the department tree before submitting parent selection","Exclude deleted parents from frontend dropdown options","Send parentId=0 for top-level departments instead of stale IDs"],"tags":["department","validation","gorm","go"],"backgroundTag":"parent-record-not-found","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}