You've already forked FrameTour-BE
- 新增小程序人脸聊天控制器 AppChatController,支持会话创建、消息收发、历史查询及会话关闭 - 集成智谱 GLM 模型客户端 GlmClient,支持流式文本生成与回调 - 新增聊天会话与消息实体类及 MyBatis 映射,实现数据持久化 - 提供 FaceChatService 接口及实现,封装聊天业务逻辑包括同步/流式消息发送 - 引入 zai-sdk 依赖以支持调用智谱 AI 大模型能力 - 支持基于人脸 ID 的唯一会话管理与用户权限校验 - 消息记录包含角色、内容、追踪 ID 及延迟信息,便于调试与分析
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
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<ChatConversationVO> 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<ChatSendMessageResp> 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<ChatMessagePageResp> 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<String> closeConversation(@PathVariable Long conversationId) {
|
||||
JwtInfo worker = JwtTokenUtil.getWorker();
|
||||
faceChatService.closeConversation(conversationId, worker.getUserId());
|
||||
return ApiResponse.success("OK");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user