{"record":{"id":"2bdf3ef256f99f82","repo":"YunaiV/yudao-cloud","slug":"error-2bdf3e","errorCode":null,"errorMessage":"未知联系状态：{}","messagePattern":"未知联系状态：(.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":500,"severity":"error","filePath":"yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java","lineNumber":81,"sourceCode":"                .eqIfPresent(CrmCustomerDO::getMobile, pageReqVO.getMobile())\n                .eqIfPresent(CrmCustomerDO::getIndustryId, pageReqVO.getIndustryId())\n                .eqIfPresent(CrmCustomerDO::getLevel, pageReqVO.getLevel())\n                .eqIfPresent(CrmCustomerDO::getSource, pageReqVO.getSource())\n                .eqIfPresent(CrmCustomerDO::getFollowUpStatus, pageReqVO.getFollowUpStatus());\n\n        // backlog 查询\n        if (ObjUtil.isNotNull(pageReqVO.getContactStatus())) {\n            Assert.isNull(pageReqVO.getPool(), \"pool 必须是 null\");\n            LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());\n            LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now());\n            if (pageReqVO.getContactStatus().equals(CrmCustomerPageReqVO.CONTACT_TODAY)) { // 今天需联系\n                query.between(CrmCustomerDO::getContactNextTime, beginOfToday, endOfToday);\n            } else if (pageReqVO.getContactStatus().equals(CrmCustomerPageReqVO.CONTACT_EXPIRED)) { // 已逾期\n                query.lt(CrmCustomerDO::getContactNextTime, beginOfToday);\n            } else if (pageReqVO.getContactStatus().equals(CrmCustomerPageReqVO.CONTACT_ALREADY)) { // 已联系\n                query.between(CrmCustomerDO::getContactLastTime, beginOfToday, endOfToday);\n            } else {\n                throw new IllegalArgumentException(\"未知联系状态：\" + pageReqVO.getContactStatus());\n            }\n        }\n        return selectJoinPage(pageReqVO, CrmCustomerDO.class, query);\n    }\n\n    default CrmCustomerDO selectByCustomerName(String name) {\n        return selectOne(CrmCustomerDO::getName, name);\n    }\n\n    default PageResult<CrmCustomerDO> selectPutPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO,\n                                                                      CrmCustomerPoolConfigDO poolConfig,\n                                                                      Long ownerUserId) {\n        final MPJLambdaWrapperX<CrmCustomerDO> query = buildPutPoolRemindCustomerQuery(pageReqVO, poolConfig, ownerUserId);\n        return selectJoinPage(pageReqVO, CrmCustomerDO.class, query.selectAll(CrmCustomerDO.class));\n    }\n\n    default Long selectPutPoolRemindCustomerCount(CrmCustomerPageReqVO pageReqVO,\n                                                  CrmCustomerPoolConfigDO poolConfig,","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/YunaiV/yudao-cloud/blob/477be9dd49ab7223a972a6abdff0684d6423dec3/yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java#L63-L99","documentation":"CrmCustomerMapper.selectPage throws IllegalArgumentException when the backlog contactStatus filter is not one of the three supported constants: CONTACT_TODAY (今天需联系), CONTACT_EXPIRED (已逾期), CONTACT_ALREADY (已联系). The value is compared literally, so any other integer (or a wrong-cased/mispacked value) reaches the else branch. Note the method also asserts pool must be null when contactStatus is set.","triggerScenarios":"GET /admin-api/crm/customer/page with contactStatus=4 or 0; a custom CRM frontend passing its own enum numbering; chaining pool + contactStatus parameters (pool must be null); client sending contactStatus as string '1' where a different constant set is expected.","commonSituations":"Frontend filter dropdown extended with new statuses without a backend update; API consumers guessing the enum values instead of reading CrmCustomerPageReqVO constants; stale frontend after backend constants changed.","solutions":["Pass only contactStatus values defined in CrmCustomerPageReqVO: CONTACT_TODAY, CONTACT_EXPIRED, CONTACT_ALREADY","Do not combine pool with contactStatus in the same request (pool must be null)","Sync the frontend filter options with the backend constants after upgrades"],"exampleFix":"// before\nGET /admin-api/crm/customer/page?contactStatus=4&pool=false\n\n// after\nGET /admin-api/crm/customer/page?contactStatus=1  // 1 = CONTACT_TODAY, no pool param","handlingStrategy":"validation","validationCode":"Integer s = pageReqVO.getContactStatus();\nif (s != null && !(s.equals(CrmCustomerPageReqVO.CONTACT_TODAY)\n        || s.equals(CrmCustomerPageReqVO.CONTACT_EXPIRED)\n        || s.equals(CrmCustomerPageReqVO.CONTACT_ALREADY))) {\n    throw new IllegalArgumentException(\"contactStatus 必须为 1/2/3\");\n}","typeGuard":"boolean isBacklogStatus(Integer s) {\n    return s != null && Set.of(CrmCustomerPageReqVO.CONTACT_TODAY,\n        CrmCustomerPageReqVO.CONTACT_EXPIRED,\n        CrmCustomerPageReqVO.CONTACT_ALREADY).contains(s);\n}","tryCatchPattern":null,"preventionTips":["Bind the UI filter dropdown directly to the three VO constants","Never send pool together with contactStatus","Return 400 from the controller for unknown filter values instead of reaching the mapper"],"tags":["crm","customer","backlog","enum","validation"],"backgroundTag":null,"analyzedSha":"477be9dd49ab7223a972a6abdff0684d6423dec3","analyzedAt":"2026-08-14T13:35:31.121Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}