{"record":{"id":"c3fe5f8f864e560d","repo":"yudaocode/SpringBoot-Labs","slug":"1001002001","errorCode":"1001002001","errorMessage":"用户已存在","messagePattern":"用户已存在","errorType":"error_code","errorClass":"ServiceException","httpStatus":null,"severity":"error","filePath":"lab-30/lab-30-dubbo-xml-demo/user-rpc-service-provider/src/main/java/cn/iocoder/springboot/lab30/rpc/service/UserRpcServiceImpl.java","lineNumber":25,"sourceCode":"import cn.iocoder.springboot.lab30.rpc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab30.rpc.dto.UserDTO;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class UserRpcServiceImpl implements UserRpcService {\n\n    @Override\n    public UserDTO get(Integer id) {\n        return new UserDTO().setId(id)\n                .setName(\"没有昵称：\" + id)\n                .setGender(id % 2 + 1); // 1 - 男；2 - 女\n    }\n\n    @Override\n    public Integer add(UserAddDTO addDTO) {\n        // 这里，模拟用户已经存在的情况\n        if (\"yudaoyuanma\".equals(addDTO.getName())) {\n            throw new ServiceException(ServiceExceptionEnum.USER_EXISTS);\n        }\n        return (int) (System.currentTimeMillis() / 1000); // 嘿嘿，随便返回一个 id\n    }\n\n}\n","sourceCodeStart":7,"sourceCodeEnd":31,"githubUrl":"https://github.com/yudaocode/SpringBoot-Labs/blob/6c12efaed06d12907a0f40dd2ad1f7020aec8798/lab-30/lab-30-dubbo-xml-demo/user-rpc-service-provider/src/main/java/cn/iocoder/springboot/lab30/rpc/service/UserRpcServiceImpl.java#L7-L31","documentation":"A Dubbo RPC provider (UserRpcServiceImpl.add) that throws ServiceException(USER_EXISTS, code 1001002001, '用户已存在') when the submitted username equals 'yudaoyuanma'. In Dubbo, a business exception thrown by the provider is serialized back to the consumer only if the exception class is on the consumer's classpath and whitelisted; otherwise the consumer receives a RuntimeException wrapper with the original message, which is why the lab keeps ServiceException in a shared api/enum package.","triggerScenarios":"Consumer calls UserRpcService.add(new UserAddDTO().setName(\"yudaoyuanma\")) over Dubbo. Any other name succeeds and returns a pseudo id.","commonSituations":"Standard duplicate-key simulation in RPC demos. Real-world Dubbo issues with this shape: consumer sees 'Checked exception is not allowed' or a wrapped RuntimeException because the ServiceException class is missing from the consumer's dependency tree; or the exception is unwrappable after serialization (stack trace is provider-side). Also common after renaming exception packages without re-publishing the api jar.","solutions":["If you are the consumer: avoid submitting name='yudaoyuanma', or catch ServiceException and branch on code 1001002001 for a friendly 'user exists' message.","Ensure the api jar containing ServiceException and ServiceExceptionEnum is declared as a dependency by BOTH provider and consumer so Dubbo can deserialize the typed exception.","If the consumer gets a generic RuntimeException wrapper, unwrap with ExceptionUtils.getRootCause and match on the message/code rather than the class.","For real duplicate checks, replace the hardcoded name with a DB unique index + DuplicateKeyException translation."],"exampleFix":"// before: consumer blows up on provider business exception\nInteger id = userRpcService.add(addDTO);\n\n// after: consumer handles the typed business error\ntry {\n    Integer id = userRpcService.add(addDTO);\n} catch (ServiceException e) {\n    if (ServiceExceptionEnum.USER_EXISTS.getCode().equals(e.getCode())) {\n        return \"用户已存在\";\n    }\n    throw e;\n}","handlingStrategy":"validation","validationCode":"// Consumer-side pre-check before RPC add():\nif (\"yudaoyuanma\".equals(addDTO.getName())) {\n    throw new IllegalArgumentException(\"用户已存在\"); // fail locally, skip the RPC\n}","typeGuard":null,"tryCatchPattern":"try {\n    Integer id = userRpcService.add(addDTO);\n} catch (ServiceException e) {\n    if (ServiceExceptionEnum.USER_EXISTS.getCode().equals(e.getCode())) {\n        return \"USER_EXISTS\"; // friendly path\n    }\n    throw e;\n}","preventionTips":["Ship ServiceException + enum in the shared api jar so Dubbo deserializes typed exceptions.","On the provider, translate real duplicate keys (DuplicateKeyException) to ServiceException.","Unwrap with getRootCause when a generic RuntimeException crosses the wire."],"tags":["dubbo","rpc","business-exception","provider","tutorial"],"backgroundTag":null,"analyzedSha":"6c12efaed06d12907a0f40dd2ad1f7020aec8798","analyzedAt":"2026-08-14T13:06:31.500Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}