{"record":{"id":"f4d85b4df6a92ffd","repo":"flipped-aurora/gin-vue-admin","slug":"d-w","errorCode":null,"errorMessage":"岗位 %d 校验失败: %w","messagePattern":"岗位 (.+?) 校验失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/mcp/org_member_assigner.go","lineNumber":117,"sourceCode":"\t}\n\n\t// 校验目标部门/岗位真实存在,把无效ID挡在写入之前\n\tif len(deptIDs) > 0 {\n\t\tdeptIndex, err := fetchDeptIndex(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, id := range deptIDs {\n\t\t\tif _, ok := deptIndex[id]; !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"部门 %d 不存在,可先用 query_org_structure 查询部门树\", id)\n\t\t\t}\n\t\t}\n\t}\n\tfor _, id := range positionIDs {\n\t\tquery := url.Values{}\n\t\tquery.Set(\"id\", strconv.FormatUint(uint64(id), 10))\n\t\tif _, err := getUpstream[system.SysPosition](ctx, \"/position/findPosition\", query); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"岗位 %d 校验失败: %w\", id, err)\n\t\t}\n\t}\n\n\tuser, err := findUserByID(ctx, userID, usernameHint)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcurrentDeptIDs := make([]uint, 0, len(user.Departments))\n\tfor _, dept := range user.Departments {\n\t\tcurrentDeptIDs = append(currentDeptIDs, dept.ID)\n\t}\n\tcurrentPositionIDs := make([]uint, 0, len(user.Positions))\n\tfor _, pos := range user.Positions {\n\t\tcurrentPositionIDs = append(currentPositionIDs, pos.ID)\n\t}\n\n\tresult := orgMemberAssignResponse{","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/mcp/org_member_assigner.go#L99-L135","documentation":"assign_user_org validates each positionId by calling GET /position/findPosition upstream; any failure (network error, 404/not-found semantics, non-success code) is wrapped as 岗位 %d 校验失败 with the underlying cause. This prevents writing nonexistent position IDs.","triggerScenarios":"Calling assign_user_org with a positionId that does not exist (upstream returns not-found) or when the findPosition request itself fails (network, token, upstream 5xx).","commonSituations":"Deleted or mistyped position IDs; IDs copied from another environment; upstream service unreachable; unlike departments, positions are validated one-by-one so a single bad ID aborts the whole call.","solutions":["Verify the positionId exists via the admin UI position management page or a position query tool","Check the wrapped cause: if it is a not-found, fix the ID; if network, check upstream connectivity/token","Remove stale position IDs from your automation config","Confirm environment (dev vs prod position IDs)"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// validate each positionId before the assign call\nfor (const id of positionIds) {\n  const res = await fetch(`${baseUrl}/position/findPosition?id=${id}`)\n  const body = await res.json()\n  if (!res.ok || body.code !== 0) throw new Error(`position ${id} invalid`)\n}","typeGuard":"function positionResolved(body) {\n  return body && body.code === 0 && body.data && body.data.ID > 0\n}","tryCatchPattern":"try {\n  await callMcpTool('assign_user_org', args)\n} catch (e) {\n  if (/岗位 \\d+ 校验失败/.test(e.message)) {\n    // parse which id failed and the cause: not-found → fix the ID; network → check upstream/token\n  }\n}","preventionTips":["Verify position IDs against the admin UI or a query tool before assigning","Remove deleted positions from automation configs promptly","Keep environment-specific position ID maps; do not cross-use dev/prod IDs"],"tags":["mcp","validation","not-found","positions","upstream-api"],"backgroundTag":"referenced-entity-not-found","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}