{"record":{"id":"d89611f2a38a8d62","repo":"elunez/eladmin","slug":"a-new-dictdetail-cannot-already-have-an-id","errorCode":null,"errorMessage":"A new dictDetail cannot already have an ID","messagePattern":"A new dictDetail cannot already have an ID","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"eladmin-system/src/main/java/me/zhengjie/modules/system/rest/DictDetailController.java","lineNumber":77,"sourceCode":"\n    @ApiOperation(\"查询多个字典详情\")\n    @GetMapping(value = \"/map\")\n    public ResponseEntity<Object> getDictDetailMaps(@RequestParam String dictName){\n        String[] names = dictName.split(\"[,，]\");\n        Map<String, List<DictDetailDto>> dictMap = new HashMap<>(16);\n        for (String name : names) {\n            dictMap.put(name, dictDetailService.getDictByName(name));\n        }\n        return new ResponseEntity<>(dictMap, HttpStatus.OK);\n    }\n\n    @Log(\"新增字典详情\")\n    @ApiOperation(\"新增字典详情\")\n    @PostMapping\n    @PreAuthorize(\"@el.check('dict:add')\")\n    public ResponseEntity<Object> createDictDetail(@Validated @RequestBody DictDetail resources){\n        if (resources.getId() != null) {\n            throw new BadRequestException(\"A new \"+ ENTITY_NAME +\" cannot already have an ID\");\n        }\n        dictDetailService.create(resources);\n        return new ResponseEntity<>(HttpStatus.CREATED);\n    }\n\n    @Log(\"修改字典详情\")\n    @ApiOperation(\"修改字典详情\")\n    @PutMapping\n    @PreAuthorize(\"@el.check('dict:edit')\")\n    public ResponseEntity<Object> updateDictDetail(@Validated(DictDetail.Update.class) @RequestBody DictDetail resources){\n        dictDetailService.update(resources);\n        return new ResponseEntity<>(HttpStatus.NO_CONTENT);\n    }\n\n    @Log(\"删除字典详情\")\n    @ApiOperation(\"删除字典详情\")\n    @DeleteMapping(value = \"/{id}\")\n    @PreAuthorize(\"@el.check('dict:del')\")","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/elunez/eladmin/blob/55fbf705956949697dbd68bf9003776609d3d029/eladmin-system/src/main/java/me/zhengjie/modules/system/rest/DictDetailController.java#L59-L95","documentation":"Thrown by DictDetailController.createDictDetail (line 77) when the POST /api/dictDetail body contains an id. Guard runs ahead of dictDetailService.create; dict details are owned by their parent dict and get server-assigned ids, so a client-supplied id on create is a contract violation returning 400.","triggerScenarios":"Submitting the dict-detail edit form in create mode without clearing id; round-tripping a DictDetail from GET /api/dictDetail into POST; front-end detail list re-using row objects for the 'add detail' dialog.","commonSituations":"Dialog component shared between edit and create without a form reset; importing dict details exported from another instance; naive copy of the PUT payload into a POST during API testing.","solutions":["Remove id from the payload: destructure or delete payload.id before POST /api/dictDetail.","Clear the detail form (including id) each time the create dialog opens.","Keep dict and id in separate variables in the dialog state so create never sees the edit row's id."],"exampleFix":"// before\nthis.$axios.post('/api/dictDetail', this.detailForm) // id left from edited row\n// after\nconst { id, ...payload } = this.detailForm;\nthis.$axios.post('/api/dictDetail', payload)","handlingStrategy":"validation","validationCode":"const { id, ...payload } = detailForm;\naxios.post('/api/dictDetail', { ...payload, dict: { id: parentDictId } });","typeGuard":"const canCreate = (d) => d.id === null || d.id === undefined;","tryCatchPattern":null,"preventionTips":["Bind the create dialog to a fresh object, not the selected detail row.","Pass the parent dict id explicitly rather than reusing a fetched DictDetail."],"tags":["rest-api","validation","eladmin","dict-detail","crud"],"backgroundTag":null,"analyzedSha":"55fbf705956949697dbd68bf9003776609d3d029","analyzedAt":"2026-08-14T11:56:12.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}