实现SuperAgent特殊入口结果处理

This commit is contained in:
andy
2026-07-10 12:05:57 +08:00
parent 9de4f0e7b6
commit 74e429a2cb
29 changed files with 1121 additions and 97 deletions

View File

@@ -4,10 +4,12 @@ import cn.nianxx.thhotel.integrations.ai.superagent.common.request.SuperAgentTas
import cn.nianxx.thhotel.integrations.ai.superagent.service.SuperAgentTaskResultSecurityService;
import cn.nianxx.thhotel.integrations.ai.superagent.service.impl.SuperAgentTaskResultException;
import cn.nianxx.thhotel.integrations.ai.superagent.service.impl.SuperAgentTaskResultProperties;
import cn.nianxx.thhotel.integrations.messaging.agentbus.adapter.AgentBusProperties;
import cn.nianxx.thhotel.workflows.reservation.common.result.SuperAgentTaskResultResponse;
import cn.nianxx.thhotel.workflows.reservation.service.ReservationAiTaskIntakeService;
import java.nio.charset.StandardCharsets;
import org.springframework.http.HttpStatus;
import org.springframework.http.InvalidMediaTypeException;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
@@ -27,6 +29,7 @@ public class SuperAgentTaskResultController {
private final SuperAgentTaskResultSecurityService securityService;
private final SuperAgentTaskResultProperties properties;
private final AgentBusProperties agentBusProperties;
private final ReservationAiTaskIntakeService intakeService;
/**
@@ -35,18 +38,21 @@ public class SuperAgentTaskResultController {
public SuperAgentTaskResultController(
SuperAgentTaskResultSecurityService securityService,
SuperAgentTaskResultProperties properties,
AgentBusProperties agentBusProperties,
ReservationAiTaskIntakeService intakeService) {
this.securityService = securityService;
this.properties = properties;
this.agentBusProperties = agentBusProperties;
this.intakeService = intakeService;
}
/**
* 接收 SuperAgent AI 任务结果,先限制请求体大小,再完成 HMAC 鉴权,最后委托业务服务创建 CP1-3 数据
* 接收 SuperAgent AI 任务结果,支持 JSON 和 S000/S999 文本结果HMAC 始终基于原始请求体校验
*/
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<SuperAgentTaskResultResponse> accept(
@RequestBody(required = false) String rawBody,
@RequestHeader(name = "Content-Type", required = false) String contentType,
@RequestHeader(name = "X-TH-Hotel-SuperAgent-Client-Id", required = false) String clientId,
@RequestHeader(name = "X-TH-Hotel-SuperAgent-Timestamp", required = false) String timestamp,
@RequestHeader(name = "X-TH-Hotel-SuperAgent-Nonce", required = false) String nonce,
@@ -54,6 +60,7 @@ public class SuperAgentTaskResultController {
@RequestHeader(name = "X-TH-Hotel-Request-Id", required = false) String requestId) {
String requestBody = rawBody == null ? "" : rawBody;
rejectBodyWhenTooLarge(requestBody);
rejectUnsupportedContentType(contentType);
securityService.verify(new SuperAgentTaskResultSecurityRequest(
"POST",
REQUEST_PATH,
@@ -63,11 +70,45 @@ public class SuperAgentTaskResultController {
signature,
requestBody
));
SuperAgentTaskResultResponse response = intakeService.accept(requestBody, clientId, requestId);
SuperAgentTaskResultResponse response = intakeService.accept(
requestBody,
clientId,
requestId,
agentBusProperties.getCapture().getDefaultHotelId());
HttpStatus status = response.idempotentReplay() ? HttpStatus.OK : HttpStatus.CREATED;
return ResponseEntity.status(status).body(response);
}
/**
* 仅允许 JSON 结构化任务或 text/plain 入口结果文本,避免其他媒体类型误入业务解析。
*/
private void rejectUnsupportedContentType(String contentType) {
String rawContentType = contentType == null ? "" : contentType.trim();
if (rawContentType.isEmpty()) {
throw unsupportedContentType();
}
MediaType mediaType;
try {
mediaType = MediaType.parseMediaType(rawContentType);
} catch (InvalidMediaTypeException exception) {
throw unsupportedContentType();
}
if (!MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)
&& !MediaType.TEXT_PLAIN.isCompatibleWith(mediaType)) {
throw unsupportedContentType();
}
}
/**
* 构造统一的 Content-Type 不支持错误,避免把外部原始 Header 写入响应。
*/
private SuperAgentTaskResultException unsupportedContentType() {
return new SuperAgentTaskResultException(
HttpStatus.UNSUPPORTED_MEDIA_TYPE,
"REQUEST_CONTENT_TYPE_UNSUPPORTED",
"Content-Type 仅支持 application/json 或 text/plain。");
}
/**
* 在解析 JSON 和校验 Header 之前限制请求体大小,避免超限请求进入后续处理。
*/