diff --git a/src/main/java/com/company/agentfoundation/assistant/api/AssistantApiController.java b/src/main/java/com/company/agentfoundation/assistant/api/AssistantApiController.java index 499a152..749d8f3 100644 --- a/src/main/java/com/company/agentfoundation/assistant/api/AssistantApiController.java +++ b/src/main/java/com/company/agentfoundation/assistant/api/AssistantApiController.java @@ -20,6 +20,7 @@ import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Enumeration; import java.util.List; import java.util.Locale; import java.util.Set; @@ -70,6 +71,8 @@ public class AssistantApiController { String userId = resolveUserId(request, httpRequest); if (query.isEmpty()) return AssistantQueryResponse.failure("query不能为空"); if (userId.isEmpty()) { + log.warn("assistant sports venue missing WeCom UserID identityHeaders={}", + identityHeaderNames(httpRequest)); return AssistantQueryResponse.failure( "未能从企业微信调用上下文获取UserID,暂时无法判断数据权限。"); } @@ -149,6 +152,24 @@ public class AssistantApiController { String value = httpRequest.getHeader(headerName); if (value != null && !value.isBlank()) return value.trim(); } + Enumeration requestHeaderNames = httpRequest.getHeaderNames(); + while (requestHeaderNames != null && requestHeaderNames.hasMoreElements()) { + String headerName = requestHeaderNames.nextElement(); + String normalizedName = headerName + .toLowerCase(Locale.ROOT) + .replace("-", "") + .replace("_", ""); + if (!normalizedName.contains("userid")) continue; + String value = httpRequest.getHeader(headerName); + if (value != null && !value.isBlank()) return value.trim(); + } + String parameterUserId = httpRequest.getParameter("userid"); + if (parameterUserId == null || parameterUserId.isBlank()) { + parameterUserId = httpRequest.getParameter("userId"); + } + if (parameterUserId != null && !parameterUserId.isBlank()) { + return parameterUserId.trim(); + } if (httpRequest.getUserPrincipal() != null && httpRequest.getUserPrincipal().getName() != null && !httpRequest.getUserPrincipal().getName().isBlank()) { @@ -156,4 +177,20 @@ public class AssistantApiController { } return request == null ? "" : request.normalizedUserId(); } + + private List identityHeaderNames(HttpServletRequest httpRequest) { + List names = new ArrayList<>(); + Enumeration values = httpRequest.getHeaderNames(); + while (values != null && values.hasMoreElements()) { + String name = values.nextElement(); + String normalized = name.toLowerCase(Locale.ROOT); + if (normalized.contains("user") + || normalized.contains("wecom") + || normalized.contains("wework") + || normalized.startsWith("wx")) { + names.add(name); + } + } + return List.copyOf(names); + } } diff --git a/src/test/java/com/company/agentfoundation/assistant/api/AssistantApiControllerTest.java b/src/test/java/com/company/agentfoundation/assistant/api/AssistantApiControllerTest.java index ece8ef4..8130d96 100644 --- a/src/test/java/com/company/agentfoundation/assistant/api/AssistantApiControllerTest.java +++ b/src/test/java/com/company/agentfoundation/assistant/api/AssistantApiControllerTest.java @@ -129,6 +129,24 @@ class AssistantApiControllerTest { .andExpect(jsonPath("$.success").value(true)); } + @Test + void sportsVenueApiRecognizesPrefixedWecomUserIdHeader() throws Exception { + SemanticDispatchService semantic = mock(SemanticDispatchService.class); + when(semantic.dispatchWithAttachments("YiNianYiQingJing", "客资日报")) + .thenReturn(new SemanticDispatchResult("客资日报结果", List.of())); + MockMvc mvc = mvc( + mock(CozeWorkflowService.class), + semantic, + new AssistantFileStore()); + + mvc.perform(post("/api/assistant/sports-venue/query/") + .header("X-WeCom-From-UserId", "YiNianYiQingJing") + .contentType(MediaType.APPLICATION_JSON) + .content("{\"query\":\"客资日报\"}")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.success").value(true)); + } + @Test void sportsVenueApiRejectsRequestWhenWecomContextHasNoUserId() throws Exception { MockMvc mvc = mvc(