新版本
This commit is contained in:
parent
52d5bdacf3
commit
e89fbd3141
@ -25,13 +25,13 @@
|
||||
|
||||
```json
|
||||
{
|
||||
"query": "查看本月EC电话合规监控日报",
|
||||
"userId": "YiNianYiQingJing"
|
||||
"query": "查看本月EC电话合规监控日报"
|
||||
}
|
||||
```
|
||||
|
||||
- `query` 同时兼容 `content`、`userText`、`message` 和 `input`。
|
||||
- `userId` 同时兼容 `wecomUserId`、`userid` 和 `user_id`。
|
||||
- UserID 由后端从企业微信调用上下文请求头自动读取,不需要作为插件业务参数填写。
|
||||
- 本地联调仍兼容请求体中的 `userId`、`wecomUserId`、`userid` 和 `user_id`。
|
||||
- 处理逻辑保持不变:DeepSeek 语义识别 → 日期解析 → UserID 数据权限 → 对应日报处理器。
|
||||
- 同一个 UserID 同时只能执行一条运动馆指令。
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import com.company.agentfoundation.integration.coze.service.CozeWorkflowService;
|
||||
import com.company.agentfoundation.semantic.SemanticDispatchResult;
|
||||
import com.company.agentfoundation.semantic.SemanticDispatchService;
|
||||
import com.company.agentfoundation.semantic.SemanticReplyAttachment;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.ContentDisposition;
|
||||
@ -57,11 +58,17 @@ public class AssistantApiController {
|
||||
}
|
||||
|
||||
@PostMapping("/sports-venue/query")
|
||||
public AssistantQueryResponse sportsVenueQuery(@RequestBody AssistantQueryRequest request) {
|
||||
public AssistantQueryResponse sportsVenueQuery(
|
||||
@RequestBody AssistantQueryRequest request,
|
||||
HttpServletRequest httpRequest
|
||||
) {
|
||||
String query = request == null ? "" : request.normalizedQuery();
|
||||
String userId = request == null ? "" : request.normalizedUserId();
|
||||
String userId = resolveUserId(request, httpRequest);
|
||||
if (query.isEmpty()) return AssistantQueryResponse.failure("query不能为空");
|
||||
if (userId.isEmpty()) return AssistantQueryResponse.failure("userId不能为空,数据权限需要企业微信UserID");
|
||||
if (userId.isEmpty()) {
|
||||
return AssistantQueryResponse.failure(
|
||||
"未能从企业微信调用上下文获取UserID,暂时无法判断数据权限。");
|
||||
}
|
||||
|
||||
String runningKey = userId.toLowerCase(Locale.ROOT);
|
||||
if (!runningUsers.add(runningKey)) {
|
||||
@ -121,4 +128,28 @@ public class AssistantApiController {
|
||||
}
|
||||
return content.toString();
|
||||
}
|
||||
|
||||
private String resolveUserId(
|
||||
AssistantQueryRequest request,
|
||||
HttpServletRequest httpRequest
|
||||
) {
|
||||
String[] headerNames = {
|
||||
"X-WeCom-UserId",
|
||||
"X-WeWork-UserId",
|
||||
"X-WXWork-UserId",
|
||||
"X-WX-UserId",
|
||||
"X-User-Id",
|
||||
"UserId"
|
||||
};
|
||||
for (String headerName : headerNames) {
|
||||
String value = httpRequest.getHeader(headerName);
|
||||
if (value != null && !value.isBlank()) return value.trim();
|
||||
}
|
||||
if (httpRequest.getUserPrincipal() != null
|
||||
&& httpRequest.getUserPrincipal().getName() != null
|
||||
&& !httpRequest.getUserPrincipal().getName().isBlank()) {
|
||||
return httpRequest.getUserPrincipal().getName().trim();
|
||||
}
|
||||
return request == null ? "" : request.normalizedUserId();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@ package com.company.agentfoundation.assistant.api;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@ -18,8 +17,7 @@ public class AssistantOpenApiController {
|
||||
"问答小助手API",
|
||||
"/api/assistant/general/query",
|
||||
"generalAssistantQuery",
|
||||
"将用户输入发送给扣子问答工作流并返回回答",
|
||||
false);
|
||||
"将用户输入发送给扣子问答工作流并返回回答");
|
||||
}
|
||||
|
||||
@GetMapping({"/sports-venue", "/sports-venue.json"})
|
||||
@ -28,32 +26,26 @@ public class AssistantOpenApiController {
|
||||
"运动馆智能助手API",
|
||||
"/api/assistant/sports-venue/query",
|
||||
"sportsVenueAssistantQuery",
|
||||
"根据用户输入进行语义识别,并按企业微信UserID权限查询客资、学员、企业微信客户运营或EC日报",
|
||||
true);
|
||||
"根据用户输入进行语义识别,并按企业微信调用上下文中的UserID权限查询客资、学员、企业微信客户运营或EC日报");
|
||||
}
|
||||
|
||||
private Map<String, Object> specification(
|
||||
String title,
|
||||
String path,
|
||||
String operationId,
|
||||
String description,
|
||||
boolean requireUserId
|
||||
String description
|
||||
) {
|
||||
Map<String, Object> properties = new LinkedHashMap<>();
|
||||
properties.put("query", Map.of(
|
||||
"type", "string",
|
||||
"description", "用户在企业微信中输入的完整问题或指令",
|
||||
"example", requireUserId ? "查看今日客资管理日报" : "测试表"));
|
||||
if (requireUserId) {
|
||||
properties.put("userId", Map.of(
|
||||
"type", "string",
|
||||
"description", "当前提问人的企业微信UserID,用于数据权限判断",
|
||||
"example", "YiNianYiQingJing"));
|
||||
}
|
||||
"example", operationId.equals("sportsVenueAssistantQuery")
|
||||
? "查看今日客资管理日报"
|
||||
: "测试表"));
|
||||
|
||||
Map<String, Object> requestSchema = new LinkedHashMap<>();
|
||||
requestSchema.put("type", "object");
|
||||
requestSchema.put("required", requireUserId ? List.of("query", "userId") : List.of("query"));
|
||||
requestSchema.put("required", List.of("query"));
|
||||
requestSchema.put("properties", properties);
|
||||
|
||||
Map<String, Object> attachmentProperties = Map.of(
|
||||
@ -86,11 +78,9 @@ public class AssistantOpenApiController {
|
||||
"description", "处理结果",
|
||||
"content", Map.of("application/json", Map.of("schema", responseSchema)))));
|
||||
|
||||
String server = ServletUriComponentsBuilder.fromCurrentContextPath().build().toUriString();
|
||||
return Map.of(
|
||||
"openapi", "3.0.1",
|
||||
"info", Map.of("title", title, "version", "1.0.0", "description", description),
|
||||
"servers", List.of(Map.of("url", server)),
|
||||
"paths", Map.of(path, Map.of("post", operation)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,7 +73,27 @@ class AssistantApiControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void sportsVenueApiRequiresUserIdForPermissionScope() throws Exception {
|
||||
void sportsVenueApiReadsUserIdFromWecomContextHeader() 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-UserId", "YiNianYiQingJing")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("{\"query\":\"查看今日客资日报\"}"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.success").value(true));
|
||||
|
||||
verify(semantic).dispatchWithAttachments("YiNianYiQingJing", "查看今日客资日报");
|
||||
}
|
||||
|
||||
@Test
|
||||
void sportsVenueApiRejectsRequestWhenWecomContextHasNoUserId() throws Exception {
|
||||
MockMvc mvc = mvc(
|
||||
mock(CozeWorkflowService.class),
|
||||
mock(SemanticDispatchService.class),
|
||||
@ -84,7 +104,7 @@ class AssistantApiControllerTest {
|
||||
.content("{\"query\":\"查看今日客资日报\"}"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.success").value(false))
|
||||
.andExpect(jsonPath("$.content").value(org.hamcrest.Matchers.containsString("userId")));
|
||||
.andExpect(jsonPath("$.content").value(org.hamcrest.Matchers.containsString("UserID")));
|
||||
}
|
||||
|
||||
private MockMvc mvc(
|
||||
|
||||
@ -31,7 +31,11 @@ class AssistantOpenApiControllerTest {
|
||||
.andExpect(jsonPath("$.paths['/api/assistant/sports-venue/query'].post.operationId")
|
||||
.value("sportsVenueAssistantQuery"))
|
||||
.andExpect(jsonPath(
|
||||
"$.paths['/api/assistant/sports-venue/query'].post.requestBody.content['application/json'].schema.required[1]")
|
||||
.value("userId"));
|
||||
"$.paths['/api/assistant/sports-venue/query'].post.requestBody.content['application/json'].schema.required.length()")
|
||||
.value(1))
|
||||
.andExpect(jsonPath(
|
||||
"$.paths['/api/assistant/sports-venue/query'].post.requestBody.content['application/json'].schema.properties.userId")
|
||||
.doesNotExist())
|
||||
.andExpect(jsonPath("$.servers").doesNotExist());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user