{"record":{"id":"364c606403ccad0a","repo":"elunez/eladmin","slug":"error-364c60","errorCode":null,"errorMessage":"服务异常，请联系网站负责人","messagePattern":"服务异常，请联系网站负责人","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/VerifyServiceImpl.java","lineNumber":60,"sourceCode":"    @Value(\"${code.expiration}\")\n    private Long expiration;\n    private final RedisUtils redisUtils;\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public EmailVo sendEmail(String email, String key) {\n        EmailVo emailVo;\n        String content;\n        String redisKey = key + email;\n        // 如果不存在有效的验证码，就创建一个新的\n        TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig(\"template\", TemplateConfig.ResourceMode.CLASSPATH));\n        Template template = engine.getTemplate(\"email.ftl\");\n        String oldCode =  redisUtils.get(redisKey, String.class);\n        if(oldCode == null){\n            String code = RandomUtil.randomNumbers (6);\n            // 存入缓存\n            if(!redisUtils.set(redisKey, code, expiration)){\n                throw new BadRequestException(\"服务异常，请联系网站负责人\");\n            }\n            content = template.render(Dict.create().set(\"code\",code));\n            // 存在就再次发送原来的验证码\n        } else {\n            content = template.render(Dict.create().set(\"code\",oldCode));\n        }\n        emailVo = new EmailVo(Collections.singletonList(email),\"ELADMIN后台管理系统\",content);\n        return emailVo;\n    }\n\n    @Override\n    public void validated(String key, String code) {\n        String value = redisUtils.get(key, String.class);\n        if(!code.equals(value)){\n            throw new BadRequestException(\"无效验证码\");\n        } else {\n            redisUtils.del(key);\n        }","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/elunez/eladmin/blob/55fbf705956949697dbd68bf9003776609d3d029/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/VerifyServiceImpl.java#L42-L78","documentation":"Thrown by VerifyServiceImpl.sendEmail when RedisUtils.set() returns false while trying to store a newly generated 6-digit email verification code under key `key + email`. The code was already generated from the email.ftl template engine, but the cache write failed, so the whole send is aborted with a generic 'service exception, contact the site owner' message. It is a BadRequestException (HTTP 400 surfaced via the global exception handler), masking an infrastructure failure in Redis.","triggerScenarios":"POST /api/code/sendEmail (or the equivalent verifyCode controller) for an email that has no unexpired code in Redis; redisUtils.set(redisKey, code, expiration) returns false — e.g. Redis is down, connection refused, maxmemory reached, or the setex fails. Any first-time code request while Redis is unhealthy produces this.","commonSituations":"Redis not started or wrong host/port/password in application.yml (spring.redis.*) in dev; Redis evicting keys under memory pressure so set fails; network partition between the app and Redis in containerized deployments; expiration misconfigured to an illegal value.","solutions":["Check Redis connectivity: verify spring.redis host/port/password in the running profile and test with redis-cli ping from the app host.","Inspect Redis health: run INFO memory / INFO stats to check for maxmemory errors (used_memory > maxmemory, evicted_keys, rejected_connections).","Look at the application log for the underlying Lettuce/Jedis exception thrown inside RedisUtils.set — the BadRequestException hides it.","If Redis must be optional in some environment, wrap the sendEmail flow in a health check (e.g. redisUtils.get on a ping key) and return a specific 503-style error instead of this generic message.","Improve the message or map the failure to a dedicated handler so operators can distinguish 'cache down' from other 400s."],"exampleFix":"// before\nif(!redisUtils.set(redisKey, code, expiration)){\n    throw new BadRequestException(\"服务异常，请联系网站负责人\");\n}\n\n// after: surface the infrastructure cause for logs while keeping a client-safe message\nif(!redisUtils.set(redisKey, code, expiration)){\n    log.error(\"Failed to write verify code to Redis, key={}\", redisKey);\n    throw new BadRequestException(\"验证码服务暂不可用，请稍后再试\");\n}","handlingStrategy":"try-catch","validationCode":"// pre-flight: confirm the cache backend answers before offering code sending\nboolean cacheOk = false;\ntry {\n    cacheOk = redisUtils.get(\"ping\", String.class) != null || true; // any round-trip proves reachability\n} catch (Exception ignore) { cacheOk = false; }\nif (!cacheOk) {\n    return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(\"验证码服务不可用\");\n}","typeGuard":null,"tryCatchPattern":"try { verifyService.sendEmail(email, key); } catch (BadRequestException e) { if (\"服务异常，请联系网站负责人\".equals(e.getMessage())) { /* infra issue: alert ops, do NOT retry immediately */ } else { throw e; } }","preventionTips":["Monitor Redis availability (health indicator + alerts) since the verify-code feature hard-depends on it.","Include Redis in docker-compose/healthchecks for local dev so the dependency is always up before tests.","Alert on the exact message string in APM so cache-write failures are not mistaken for user errors."],"tags":["redis","verification-code","email","cache","infrastructure"],"backgroundTag":null,"analyzedSha":"55fbf705956949697dbd68bf9003776609d3d029","analyzedAt":"2026-08-14T11:56:12.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}