异步返回
This commit is contained in:
parent
1e61d35d5c
commit
2e0936cb6b
@ -7,6 +7,7 @@ import com.company.agentfoundation.semantic.SemanticReplyAttachment;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.http.ContentDisposition;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
@ -26,6 +27,7 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/assistant")
|
||||
@ -35,16 +37,20 @@ public class AssistantApiController {
|
||||
private final CozeWorkflowService cozeWorkflowService;
|
||||
private final SemanticDispatchService semanticDispatchService;
|
||||
private final AssistantFileStore fileStore;
|
||||
private final Executor assistantTaskExecutor;
|
||||
private final Set<String> runningGeneralWrites = ConcurrentHashMap.newKeySet();
|
||||
private final Set<String> runningUsers = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public AssistantApiController(
|
||||
CozeWorkflowService cozeWorkflowService,
|
||||
SemanticDispatchService semanticDispatchService,
|
||||
AssistantFileStore fileStore
|
||||
AssistantFileStore fileStore,
|
||||
@Qualifier("wecomTaskExecutor") Executor assistantTaskExecutor
|
||||
) {
|
||||
this.cozeWorkflowService = cozeWorkflowService;
|
||||
this.semanticDispatchService = semanticDispatchService;
|
||||
this.fileStore = fileStore;
|
||||
this.assistantTaskExecutor = assistantTaskExecutor;
|
||||
}
|
||||
|
||||
@PostMapping(
|
||||
@ -54,6 +60,9 @@ public class AssistantApiController {
|
||||
public AssistantQueryResponse generalQuery(@RequestBody AssistantQueryRequest request) {
|
||||
String query = request == null ? "" : request.normalizedQuery();
|
||||
if (query.isEmpty()) return AssistantQueryResponse.failure("query不能为空");
|
||||
if (isCustomerCreation(query)) {
|
||||
return acceptCustomerCreation(query);
|
||||
}
|
||||
try {
|
||||
return AssistantQueryResponse.success(
|
||||
toRobotPlainText(cozeWorkflowService.chatDefault(query)), List.of());
|
||||
@ -63,6 +72,46 @@ public class AssistantApiController {
|
||||
}
|
||||
}
|
||||
|
||||
private AssistantQueryResponse acceptCustomerCreation(String query) {
|
||||
String requestKey = query.toLowerCase(Locale.ROOT).replaceAll("\\s+", "");
|
||||
if (!runningGeneralWrites.add(requestKey)) {
|
||||
return AssistantQueryResponse.success(
|
||||
"相同客户新增请求正在处理中,请勿重复提交。", List.of());
|
||||
}
|
||||
try {
|
||||
assistantTaskExecutor.execute(() -> {
|
||||
try {
|
||||
String reply = toRobotPlainText(cozeWorkflowService.chatDefault(query));
|
||||
log.info("assistant general async customer creation completed query={} reply={}",
|
||||
query, reply);
|
||||
} catch (RuntimeException exception) {
|
||||
log.error("assistant general async customer creation failed query={} err={}",
|
||||
query, exception.getMessage(), exception);
|
||||
} finally {
|
||||
runningGeneralWrites.remove(requestKey);
|
||||
}
|
||||
});
|
||||
return AssistantQueryResponse.success(
|
||||
"客户新增请求已受理,正在后台处理,请勿重复提交。", List.of());
|
||||
} catch (RuntimeException exception) {
|
||||
runningGeneralWrites.remove(requestKey);
|
||||
log.error("assistant general async customer creation rejected query={} err={}",
|
||||
query, exception.getMessage(), exception);
|
||||
return AssistantQueryResponse.failure("客户新增请求提交失败,请稍后重试。");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCustomerCreation(String query) {
|
||||
String compact = query == null ? "" : query.replaceAll("\\s+", "");
|
||||
boolean hasCreationVerb = compact.contains("新增")
|
||||
|| compact.contains("新建")
|
||||
|| compact.contains("添加")
|
||||
|| compact.contains("创建")
|
||||
|| compact.contains("录入");
|
||||
boolean hasCustomerObject = compact.contains("客户") || compact.contains("客资");
|
||||
return hasCreationVerb && hasCustomerObject;
|
||||
}
|
||||
|
||||
@PostMapping(
|
||||
value = {
|
||||
"/sports-venue/query",
|
||||
|
||||
@ -13,6 +13,7 @@ import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@ -40,6 +41,24 @@ class AssistantApiControllerTest {
|
||||
verify(coze).chatDefault("测试表");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generalApiAcceptsCustomerCreationAsynchronously() throws Exception {
|
||||
CozeWorkflowService coze = mock(CozeWorkflowService.class);
|
||||
CapturingExecutor executor = new CapturingExecutor();
|
||||
MockMvc mvc = mvc(coze, mock(SemanticDispatchService.class), new AssistantFileStore(), executor);
|
||||
|
||||
mvc.perform(post("/api/assistant/general/query")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("{\"query\":\"新增客户 名称 张三 电话 13632363654\"}"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.success").value(true))
|
||||
.andExpect(jsonPath("$.content").value(org.hamcrest.Matchers.containsString("已受理")));
|
||||
|
||||
org.mockito.Mockito.verifyNoInteractions(coze);
|
||||
executor.runCaptured();
|
||||
verify(coze).chatDefault("新增客户 名称 张三 电话 13632363654");
|
||||
}
|
||||
|
||||
@Test
|
||||
void sportsVenueApiRunsSemanticRoutingAndReturnsDownloadableExcel() throws Exception {
|
||||
SemanticDispatchService semantic = mock(SemanticDispatchService.class);
|
||||
@ -185,10 +204,34 @@ class AssistantApiControllerTest {
|
||||
SemanticDispatchService semantic,
|
||||
AssistantFileStore files
|
||||
) {
|
||||
return MockMvcBuilders.standaloneSetup(new AssistantApiController(coze, semantic, files))
|
||||
return mvc(coze, semantic, files, Runnable::run);
|
||||
}
|
||||
|
||||
private MockMvc mvc(
|
||||
CozeWorkflowService coze,
|
||||
SemanticDispatchService semantic,
|
||||
AssistantFileStore files,
|
||||
Executor executor
|
||||
) {
|
||||
return MockMvcBuilders.standaloneSetup(
|
||||
new AssistantApiController(coze, semantic, files, executor))
|
||||
.setMessageConverters(
|
||||
new MappingJackson2HttpMessageConverter(),
|
||||
new ByteArrayHttpMessageConverter())
|
||||
.build();
|
||||
}
|
||||
|
||||
private static final class CapturingExecutor implements Executor {
|
||||
private Runnable captured;
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
captured = command;
|
||||
}
|
||||
|
||||
void runCaptured() {
|
||||
if (captured == null) throw new AssertionError("No asynchronous task was submitted");
|
||||
captured.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user