回调推送消息

This commit is contained in:
tanglong 2026-08-03 09:54:11 +08:00
parent 2e0936cb6b
commit 8fac71869f
2 changed files with 43 additions and 6 deletions

View File

@ -1,6 +1,7 @@
package com.company.agentfoundation.assistant.api; package com.company.agentfoundation.assistant.api;
import com.company.agentfoundation.integration.coze.service.CozeWorkflowService; import com.company.agentfoundation.integration.coze.service.CozeWorkflowService;
import com.company.agentfoundation.integration.wecom.client.WecomClient;
import com.company.agentfoundation.semantic.SemanticDispatchResult; import com.company.agentfoundation.semantic.SemanticDispatchResult;
import com.company.agentfoundation.semantic.SemanticDispatchService; import com.company.agentfoundation.semantic.SemanticDispatchService;
import com.company.agentfoundation.semantic.SemanticReplyAttachment; import com.company.agentfoundation.semantic.SemanticReplyAttachment;
@ -35,6 +36,7 @@ public class AssistantApiController {
private static final Logger log = LoggerFactory.getLogger(AssistantApiController.class); private static final Logger log = LoggerFactory.getLogger(AssistantApiController.class);
private final CozeWorkflowService cozeWorkflowService; private final CozeWorkflowService cozeWorkflowService;
private final WecomClient wecomClient;
private final SemanticDispatchService semanticDispatchService; private final SemanticDispatchService semanticDispatchService;
private final AssistantFileStore fileStore; private final AssistantFileStore fileStore;
private final Executor assistantTaskExecutor; private final Executor assistantTaskExecutor;
@ -43,11 +45,13 @@ public class AssistantApiController {
public AssistantApiController( public AssistantApiController(
CozeWorkflowService cozeWorkflowService, CozeWorkflowService cozeWorkflowService,
WecomClient wecomClient,
SemanticDispatchService semanticDispatchService, SemanticDispatchService semanticDispatchService,
AssistantFileStore fileStore, AssistantFileStore fileStore,
@Qualifier("wecomTaskExecutor") Executor assistantTaskExecutor @Qualifier("wecomTaskExecutor") Executor assistantTaskExecutor
) { ) {
this.cozeWorkflowService = cozeWorkflowService; this.cozeWorkflowService = cozeWorkflowService;
this.wecomClient = wecomClient;
this.semanticDispatchService = semanticDispatchService; this.semanticDispatchService = semanticDispatchService;
this.fileStore = fileStore; this.fileStore = fileStore;
this.assistantTaskExecutor = assistantTaskExecutor; this.assistantTaskExecutor = assistantTaskExecutor;
@ -57,11 +61,14 @@ public class AssistantApiController {
value = {"/general/query", "/general/query/", "/general/query/general"}, value = {"/general/query", "/general/query/", "/general/query/general"},
consumes = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE) produces = MediaType.APPLICATION_JSON_VALUE)
public AssistantQueryResponse generalQuery(@RequestBody AssistantQueryRequest request) { public AssistantQueryResponse generalQuery(
@RequestBody AssistantQueryRequest request,
HttpServletRequest httpRequest
) {
String query = request == null ? "" : request.normalizedQuery(); String query = request == null ? "" : request.normalizedQuery();
if (query.isEmpty()) return AssistantQueryResponse.failure("query不能为空"); if (query.isEmpty()) return AssistantQueryResponse.failure("query不能为空");
if (isCustomerCreation(query)) { if (isCustomerCreation(query)) {
return acceptCustomerCreation(query); return acceptCustomerCreation(query, resolveUserId(request, httpRequest));
} }
try { try {
return AssistantQueryResponse.success( return AssistantQueryResponse.success(
@ -72,7 +79,7 @@ public class AssistantApiController {
} }
} }
private AssistantQueryResponse acceptCustomerCreation(String query) { private AssistantQueryResponse acceptCustomerCreation(String query, String userId) {
String requestKey = query.toLowerCase(Locale.ROOT).replaceAll("\\s+", ""); String requestKey = query.toLowerCase(Locale.ROOT).replaceAll("\\s+", "");
if (!runningGeneralWrites.add(requestKey)) { if (!runningGeneralWrites.add(requestKey)) {
return AssistantQueryResponse.success( return AssistantQueryResponse.success(
@ -84,9 +91,11 @@ public class AssistantApiController {
String reply = toRobotPlainText(cozeWorkflowService.chatDefault(query)); String reply = toRobotPlainText(cozeWorkflowService.chatDefault(query));
log.info("assistant general async customer creation completed query={} reply={}", log.info("assistant general async customer creation completed query={} reply={}",
query, reply); query, reply);
notifyCustomerCreationResult(userId, true, reply);
} catch (RuntimeException exception) { } catch (RuntimeException exception) {
log.error("assistant general async customer creation failed query={} err={}", log.error("assistant general async customer creation failed query={} err={}",
query, exception.getMessage(), exception); query, exception.getMessage(), exception);
notifyCustomerCreationResult(userId, false, "客户新增失败,请稍后重试。");
} finally { } finally {
runningGeneralWrites.remove(requestKey); runningGeneralWrites.remove(requestKey);
} }
@ -101,6 +110,23 @@ public class AssistantApiController {
} }
} }
private void notifyCustomerCreationResult(String userId, boolean success, String result) {
if (userId == null || userId.isBlank()) {
log.warn("assistant general async result cannot be pushed because WeCom UserID is missing");
return;
}
String safeResult = result == null || result.isBlank()
? (success ? "客户新增处理完成。" : "客户新增失败,请稍后重试。")
: result;
String message = (success ? "✅ 客户新增完成\n" : "❌ 客户新增失败\n") + safeResult;
try {
wecomClient.sendText("general", userId, message);
} catch (RuntimeException exception) {
log.error("assistant general async result push failed userId={} err={}",
userId, exception.getMessage(), exception);
}
}
private boolean isCustomerCreation(String query) { private boolean isCustomerCreation(String query) {
String compact = query == null ? "" : query.replaceAll("\\s+", ""); String compact = query == null ? "" : query.replaceAll("\\s+", "");
boolean hasCreationVerb = compact.contains("新增") boolean hasCreationVerb = compact.contains("新增")

View File

@ -1,6 +1,7 @@
package com.company.agentfoundation.assistant.api; package com.company.agentfoundation.assistant.api;
import com.company.agentfoundation.integration.coze.service.CozeWorkflowService; import com.company.agentfoundation.integration.coze.service.CozeWorkflowService;
import com.company.agentfoundation.integration.wecom.client.WecomClient;
import com.company.agentfoundation.semantic.SemanticDispatchResult; import com.company.agentfoundation.semantic.SemanticDispatchResult;
import com.company.agentfoundation.semantic.SemanticDispatchService; import com.company.agentfoundation.semantic.SemanticDispatchService;
import com.company.agentfoundation.semantic.SemanticReplyAttachment; import com.company.agentfoundation.semantic.SemanticReplyAttachment;
@ -44,10 +45,15 @@ class AssistantApiControllerTest {
@Test @Test
void generalApiAcceptsCustomerCreationAsynchronously() throws Exception { void generalApiAcceptsCustomerCreationAsynchronously() throws Exception {
CozeWorkflowService coze = mock(CozeWorkflowService.class); CozeWorkflowService coze = mock(CozeWorkflowService.class);
when(coze.chatDefault("新增客户 名称 张三 电话 13632363654"))
.thenReturn("企业微信智能表格批量写入成功");
WecomClient wecom = mock(WecomClient.class);
CapturingExecutor executor = new CapturingExecutor(); CapturingExecutor executor = new CapturingExecutor();
MockMvc mvc = mvc(coze, mock(SemanticDispatchService.class), new AssistantFileStore(), executor); MockMvc mvc = mvc(
coze, wecom, mock(SemanticDispatchService.class), new AssistantFileStore(), executor);
mvc.perform(post("/api/assistant/general/query") mvc.perform(post("/api/assistant/general/query")
.header("X-WeCom-UserId", "YiNianYiQingJing")
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)
.content("{\"query\":\"新增客户 名称 张三 电话 13632363654\"}")) .content("{\"query\":\"新增客户 名称 张三 电话 13632363654\"}"))
.andExpect(status().isOk()) .andExpect(status().isOk())
@ -57,6 +63,10 @@ class AssistantApiControllerTest {
org.mockito.Mockito.verifyNoInteractions(coze); org.mockito.Mockito.verifyNoInteractions(coze);
executor.runCaptured(); executor.runCaptured();
verify(coze).chatDefault("新增客户 名称 张三 电话 13632363654"); verify(coze).chatDefault("新增客户 名称 张三 电话 13632363654");
verify(wecom).sendText(
"general",
"YiNianYiQingJing",
"✅ 客户新增完成\n企业微信智能表格批量写入成功");
} }
@Test @Test
@ -204,17 +214,18 @@ class AssistantApiControllerTest {
SemanticDispatchService semantic, SemanticDispatchService semantic,
AssistantFileStore files AssistantFileStore files
) { ) {
return mvc(coze, semantic, files, Runnable::run); return mvc(coze, mock(WecomClient.class), semantic, files, Runnable::run);
} }
private MockMvc mvc( private MockMvc mvc(
CozeWorkflowService coze, CozeWorkflowService coze,
WecomClient wecom,
SemanticDispatchService semantic, SemanticDispatchService semantic,
AssistantFileStore files, AssistantFileStore files,
Executor executor Executor executor
) { ) {
return MockMvcBuilders.standaloneSetup( return MockMvcBuilders.standaloneSetup(
new AssistantApiController(coze, semantic, files, executor)) new AssistantApiController(coze, wecom, semantic, files, executor))
.setMessageConverters( .setMessageConverters(
new MappingJackson2HttpMessageConverter(), new MappingJackson2HttpMessageConverter(),
new ByteArrayHttpMessageConverter()) new ByteArrayHttpMessageConverter())