package com.ycwl.basic.controller.mobile; import com.ycwl.basic.model.jwt.JwtInfo; import com.ycwl.basic.model.mobile.chat.*; import com.ycwl.basic.service.mobile.FaceChatService; import com.ycwl.basic.utils.ApiResponse; import com.ycwl.basic.utils.JwtTokenUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter; import java.util.concurrent.CompletableFuture; /** * 小程序人脸智能聊天接口。 */ @Slf4j @RestController @RequestMapping("/api/mobile/chat/v1") @RequiredArgsConstructor public class AppChatController { private final FaceChatService faceChatService; /** * 获取或创建会话(同一人脸只保留一条)。 */ @PostMapping("/faces/{faceId}/conversation") public ApiResponse createConversation(@PathVariable Long faceId) { JwtInfo worker = JwtTokenUtil.getWorker(); ChatConversationVO vo = faceChatService.getOrCreateConversation(faceId, worker.getUserId()); return ApiResponse.success(vo); } /** * 同步发送消息,适用于短回复或前端自行轮询。 */ @PostMapping("/conversations/{conversationId}/messages") public ApiResponse sendMessage(@PathVariable Long conversationId, @RequestBody ChatSendMessageReq req) { JwtInfo worker = JwtTokenUtil.getWorker(); ChatSendMessageResp resp = faceChatService.sendMessage(conversationId, worker.getUserId(), req.getContent(), req.getTraceId()); return ApiResponse.success(resp); } /** * 流式返回,使用 HTTP chunked。小程序侧用 wx.request 的 onChunkReceived 消费。 */ @PostMapping(value = "/conversations/{conversationId}/messages/stream", produces = "text/plain;charset=UTF-8") public ResponseBodyEmitter streamMessage(@PathVariable Long conversationId, @RequestBody ChatSendMessageReq req) { JwtInfo worker = JwtTokenUtil.getWorker(); ResponseBodyEmitter emitter = new ResponseBodyEmitter(30_000L); CompletableFuture.runAsync(() -> { try { faceChatService.sendMessageStream( conversationId, worker.getUserId(), req.getContent(), req.getTraceId(), chunk -> { try { emitter.send(chunk, new MediaType("text", "plain", java.nio.charset.StandardCharsets.UTF_8)); } catch (Exception e) { emitter.completeWithError(e); } }); emitter.complete(); } catch (Exception e) { log.error("streamMessage error", e); emitter.completeWithError(e); } }); return emitter; } /** * 查询历史消息,cursor 为最后一条 seq,limit 为条数。 */ @GetMapping("/conversations/{conversationId}/messages") public ApiResponse listMessages(@PathVariable Long conversationId, @RequestParam(value = "cursor", required = false) Integer cursor, @RequestParam(value = "limit", required = false) Integer limit) { JwtInfo worker = JwtTokenUtil.getWorker(); ChatMessagePageResp resp = faceChatService.listMessages(conversationId, cursor, limit, worker.getUserId()); return ApiResponse.success(resp); } @PostMapping("/conversations/{conversationId}/close") public ApiResponse closeConversation(@PathVariable Long conversationId) { JwtInfo worker = JwtTokenUtil.getWorker(); faceChatService.closeConversation(conversationId, worker.getUserId()); return ApiResponse.success("OK"); } }