diff --git a/pom.xml b/pom.xml index 479a22c..ae5f819 100644 --- a/pom.xml +++ b/pom.xml @@ -46,7 +46,12 @@ org.springframework.boot spring-boot-configuration-processor - + + + com.google.code.gson + gson + 2.8.9 + com.itextpdf diff --git a/src/main/java/cn/teammodel/ai/deepseek/DeepSeekClient.java b/src/main/java/cn/teammodel/ai/deepseek/DeepSeekClient.java new file mode 100644 index 0000000..1a3edd5 --- /dev/null +++ b/src/main/java/cn/teammodel/ai/deepseek/DeepSeekClient.java @@ -0,0 +1,178 @@ +package cn.teammodel.ai.deepseek; + +import cn.teammodel.common.ErrorCode; +import cn.teammodel.config.exception.ServiceException; +import cn.teammodel.model.dto.ai.deepseek.ChatRequestDto; +import cn.teammodel.model.dto.ai.deepseek.ChatResponseDto; +import cn.teammodel.model.dto.ai.deepseek.MessageDto; +import cn.teammodel.utils.JsonUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.google.gson.Gson; +import okhttp3.*; +import org.apache.http.HttpEntity; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import java.util.HashMap; +import java.util.Map; + +import java.io.IOException; +import java.io.InputStream; +import java.util.*; + +public class DeepSeekClient { + private static final String API_Key; + private static final String API_Url; + public static String API_Model; + /** + * 读取配置文件 读取key 和url + */ + static { + Properties props = new Properties(); + try { + InputStream is = DeepSeekClient.class.getResourceAsStream("/DeepSeekConfig.properties"); + props.load(is); + API_Key = props.getProperty("key"); + API_Url = props.getProperty("url"); + API_Model = props.getProperty("model"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * 提问题 测试使用 + * @param mssage + */ + private static Map ask(MessageDto mssage) + { + Map mapper = new HashMap<>(); + //创建消息列表 + List msg = new ArrayList<>(); + msg.add(mssage); + + //构建请求头 + ChatRequestDto requestBody = new ChatRequestDto(); + requestBody.setModel(API_Model); + requestBody.setMessages(msg); + requestBody.setTemperature(0); + requestBody.setMax_tokens(1024); + + long startTime = System.currentTimeMillis(); + //发起请求 + ChatResponseDto response = SendRequests(requestBody); + //Map response = SendRequest(requestBody); + Long endTime = System.currentTimeMillis(); + //思考耗时 秒 + long time = (endTime-startTime)/1000; + response.setWasteTime(time); + return mapper; + + } + + /*** + * OkHttpClient 方式请求 + * 返回请求结果 转 实体有问题 + * @param requestBody + * @return + */ + public static ChatResponseDto SendRequests(ChatRequestDto requestBody) + { + ChatResponseDto chatResponse = new ChatResponseDto(); + OkHttpClient client = new OkHttpClient().newBuilder().build(); + MediaType mediaType = MediaType.parse("application/json"); + //String content = "{\n \"messages\": [\n {\n \"content\": \"You are a helpful assistant\",\n \"role\": \"system\"\n },\n {\n \"content\": \"Hi\",\n \"role\": \"user\"\n }\n ],\n \"model\": \"deepseek-chat\",\n \"frequency_penalty\": 0,\n \"max_tokens\": 2048,\n \"presence_penalty\": 0,\n \"response_format\": {\n \"type\": \"text\"\n },\n \"stop\": null,\n \"stream\": false,\n \"stream_options\": null,\n \"temperature\": 1,\n \"top_p\": 1,\n \"tools\": null,\n \"tool_choice\": \"none\",\n \"logprobs\": false,\n \"top_logprobs\": null\n}"; + String content = JsonUtil.convertToJson(requestBody); + + RequestBody body = RequestBody.create(mediaType, content); + Request request = new Request.Builder() + .url(API_Url) + .method("POST", body) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json") + .addHeader("Authorization", "Bearer "+API_Key) + .build(); + + try(Response response = client.newCall(request).execute()) { + if (response.isSuccessful() && response.body() != null) { + String responseBody = response.body().string(); + // 使用 Gson 将 JSON 字符串转换为 MyEntity 对象 + Gson gson = new Gson(); + chatResponse = gson.fromJson(responseBody, ChatResponseDto.class); + // 确保关闭响应体以释放资源 + response.body().close(); + chatResponse.setCode(200); + chatResponse.setMsg("成功"); + } else { + chatResponse.setCode( response.code()); + chatResponse.setMsg(response.body().string()); + } + } catch (IOException e) { + if (e.getMessage().equals("Connection timed out") || e.getMessage().equals("timeout")) + { + chatResponse.setCode(408); + chatResponse.setMsg("请求DeepSeek服务器超时"); + }else { + chatResponse.setCode( 500); + chatResponse.setMsg(e.getMessage()); + } + } + return chatResponse; + } + + /*** + * HttpClient 方式请求 + * @param requestBody + * @return + */ + public static Map SendRequest(ChatRequestDto requestBody) { + Map mapper = new HashMap<>(); + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + // 创建HttpPost对象 + HttpPost httpPost = new HttpPost(API_Url); + //添加请求头 + httpPost.setHeader("Content-Type", "application/json"); + httpPost.setHeader("Accept", "application/json"); + httpPost.setHeader("Authorization", "Bearer " +API_Key); + + // 设置请求体 + String jsonContent = JsonUtil.convertToJson(requestBody); + httpPost.setEntity(new StringEntity(jsonContent, ContentType.create("application/json", "UTF-8"))); + + // 发送请求 + try (CloseableHttpResponse response = httpClient.execute(httpPost)) { + // 获取响应实体 + HttpEntity entity = response.getEntity(); + if (entity != null) { + // 解析响应内容 + String jsonString = EntityUtils.toString(entity); + ObjectMapper objectMapper = new ObjectMapper(); + mapper = objectMapper.readValue(jsonString, TypeFactory.defaultInstance().constructMapType(Map.class, String.class, Object.class)); + } + + // 检查响应状态码 + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode != 200) throw new RuntimeException("Failed : HTTP error code : " + statusCode); + } catch (IOException e) { + throw new ServiceException(ErrorCode.SYSTEM_ERROR.getCode(), "数据解析异常"); + } + + } catch (IOException e) { + throw new ServiceException(ErrorCode.SYSTEM_ERROR.getCode(), "请求头异常"+e.getMessage()); + } + //TODO 请求接口 + return mapper; + } + + + + +} diff --git a/src/main/java/cn/teammodel/common/PK.java b/src/main/java/cn/teammodel/common/PK.java index 246227a..9e48736 100644 --- a/src/main/java/cn/teammodel/common/PK.java +++ b/src/main/java/cn/teammodel/common/PK.java @@ -25,6 +25,10 @@ public interface PK { String STUDENT = "Base-%s"; String CLASS = "Class-%s"; String CHAT_SESSION = "ChatSession"; + /** + * DeepSeek问答会话 + */ + String DEEPSEEK_SESSION ="DeepSeekSession"; String WEEK_DUTY = "Duty"; String WEEK_DUTY_RECORD = "DutyRecord-%s"; String CHAT_APP = "ChatApp"; diff --git a/src/main/java/cn/teammodel/controller/frontend/AiController.java b/src/main/java/cn/teammodel/controller/frontend/AiController.java index 84162b4..4306e79 100644 --- a/src/main/java/cn/teammodel/controller/frontend/AiController.java +++ b/src/main/java/cn/teammodel/controller/frontend/AiController.java @@ -26,7 +26,7 @@ import java.util.concurrent.CompletableFuture; @RestController @RequestMapping("/ai") -@Api(tags = "AI 能力") +@Api(tags = "AI 科大讯飞 能力") public class AiController { @Resource private ChatSessionService chatSessionService; @@ -35,6 +35,7 @@ public class AiController { @Resource private ChatAppService chatAppService; + @PostMapping("api/chat/completion") @ApiOperation("与 spark 的流式对话") public SseEmitter chatCompletionToApi(@RequestBody @Valid ChatCompletionReqDto chatCompletionReqDto) { @@ -183,4 +184,6 @@ public class AiController { return chatMessageService.chatTeacherComment(chatTeacherCommentDto,userId, userName); } + + } \ No newline at end of file diff --git a/src/main/java/cn/teammodel/controller/frontend/AiDeepSeekController.java b/src/main/java/cn/teammodel/controller/frontend/AiDeepSeekController.java new file mode 100644 index 0000000..a966b38 --- /dev/null +++ b/src/main/java/cn/teammodel/controller/frontend/AiDeepSeekController.java @@ -0,0 +1,129 @@ +package cn.teammodel.controller.frontend; + +import cn.teammodel.common.IdRequest; +import cn.teammodel.model.dto.ai.UpdateSessionDto; +import cn.teammodel.model.dto.ai.deepseek.ChatResponseDto; +import cn.teammodel.model.dto.ai.deepseek.MessageDto; +import cn.teammodel.model.entity.TmdUserDetail; +import cn.teammodel.model.entity.ai.ChatSession; +import cn.teammodel.model.entity.ai.DeepSeekSession; +import cn.teammodel.model.entity.ai.DeepSeekSession.DeepSeekMessage; +import cn.teammodel.repository.ChatSessionRepository; +import cn.teammodel.repository.DeepSeekRepository; +import cn.teammodel.security.utils.SecurityUtil; +import cn.teammodel.service.DeepSeekService; +import cn.teammodel.common.R; +import cn.teammodel.service.DeepSeekSessionService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.*; + +@RestController +@RequestMapping("/aiDeepSeek") +@Api(tags = "AI DeepSeek 能力") +public class AiDeepSeekController { + + /** + * 访问DeepSeek方法 + */ + @Resource + private DeepSeekService deepSeekChatService; + + /** + * 会话管理 + */ + @Resource + private DeepSeekSessionService deepSeekSessionService; + + /** + * 创建默认会话 + * @return + */ + @PostMapping("session/create/default") + @ApiOperation("创建默认会话") + public R CreateDefaultSession(){ + String userId = ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getClaims().getSubject(); + String name = (String) ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getClaims().get("name"); + DeepSeekSession session = deepSeekSessionService.CreateDefaultSession(userId,name); + return R.success(session); + } + + /** + * 创建会话 按照结构传输 + * @param createSession + * @return + */ + @PostMapping("session/create") + @ApiOperation("创建会话(实体)") + public R CreateDefaultSession(@RequestBody @Valid DeepSeekSession createSession){ + DeepSeekSession session = deepSeekSessionService.CreateSession(createSession); + return R.success(session); + } + + /** + * 删除会话 + * @param idRequest + * @return + */ + @PostMapping("session/del") + @ApiOperation("删除会话") + public R removeSession(@RequestBody @Valid IdRequest idRequest) { + String userId = ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getClaims().getSubject(); + deepSeekSessionService.deleteSession(idRequest.getId(), userId); + return R.success("删除会话成功"); + } + + /** + * 更新会话 + * @param upSession + * @return + */ + @PostMapping("session/update") + @ApiOperation("更新聊天会话") + public R updateSession(@RequestBody @Valid DeepSeekSession upSession) { + String userId = ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getClaims().getSubject(); + DeepSeekSession session = deepSeekSessionService.updateSession(upSession, userId); + return R.success(session); + } + + /** + * 查询我的会话 + * @return + */ + @PostMapping("session/userid") + @ApiOperation("查询自己的会话列表") + public R> UserId(){ + String userId = SecurityUtil.getLoginUser().getId(); + List sessions = deepSeekSessionService.UserSessionList(userId); + return R.success(sessions); + } + + /** + * 查询某个会话的聊天记录 + * @param sessionId + * @return + */ + @GetMapping("session/history/{sessionId}") + @ApiOperation("查询我的聊天记录") + public R> getHistory(@PathVariable String sessionId) { + String userId = ((TmdUserDetail) SecurityUtil.getAuthentication().getPrincipal()).getClaims().getSubject(); + List history = deepSeekSessionService.listHistory(sessionId, userId); + return R.success(history); + } + + /** + *与deepseek的对话 + * @param messageDto + * @return + */ + @PostMapping("chat") + @ApiOperation("与deepseek的对话") + public R ChatCompletion(@RequestBody @Valid MessageDto messageDto) { + ChatResponseDto chatResponse = deepSeekChatService.ChatAsk(messageDto); + return R.success(chatResponse); + } +} diff --git a/src/main/java/cn/teammodel/model/dto/ai/deepseek/ChatRequestDto.java b/src/main/java/cn/teammodel/model/dto/ai/deepseek/ChatRequestDto.java new file mode 100644 index 0000000..308c721 --- /dev/null +++ b/src/main/java/cn/teammodel/model/dto/ai/deepseek/ChatRequestDto.java @@ -0,0 +1,44 @@ +package cn.teammodel.model.dto.ai.deepseek; + +import lombok.Data; + +import java.util.List; + +/*** + * 请求体的数据内部结构,用于构建请求 json + * { + * "messages": [ + * { + * "content": "You are a helpful assistant", + * "role": "system" + * }, + * { + * "content": "Hi", + * "role": "user" + * } + * ], + * "model": "deepseek-chat", + * "frequency_penalty": 0, + * "max_tokens": 2048, + * "presence_penalty": 0, + * "response_format": { + * "type": "text" + * }, + * "stop": null, + * "stream": false, + * "stream_options": null, + * "temperature": 1, + * "top_p": 1, + * "tools": null, + * "tool_choice": "none", + * "logprobs": false, + * "top_logprobs": null + * } + */ +@Data +public class ChatRequestDto { + private String model; + private List messages; + private int temperature; + private int max_tokens; +} diff --git a/src/main/java/cn/teammodel/model/dto/ai/deepseek/ChatResponseDto.java b/src/main/java/cn/teammodel/model/dto/ai/deepseek/ChatResponseDto.java new file mode 100644 index 0000000..a88ee88 --- /dev/null +++ b/src/main/java/cn/teammodel/model/dto/ai/deepseek/ChatResponseDto.java @@ -0,0 +1,47 @@ +package cn.teammodel.model.dto.ai.deepseek; + +import lombok.Data; + +import java.util.List; + +/** + * 接着响应的json 格式来设计响应体 用来响应体字符串 + */ +@Data +public class ChatResponseDto { + private int code; + private String msg; + private long wasteTime; + private String id; + private String object; + private long created; + private String model; + + private Usage usage; + + /** + * 返回内容 + */ + private List choices; + private String system_fingerprint; + @Data + public static class Choice { + private int index; + private MessageDto message; + private String logprobs; + private String finish_reason; + } + @Data + public static class Usage{ + private int prompt_tokens; + private int completion_tokens; + private int total_tokens; + private Prompt_Tokens_Details prompt_tokens_details; + private int prompt_cache_hit_tokens; + private int prompt_cache_miss_tokens; + } + @Data + public static class Prompt_Tokens_Details { + private int cached_tokens; + } +} diff --git a/src/main/java/cn/teammodel/model/dto/ai/deepseek/MessageDto.java b/src/main/java/cn/teammodel/model/dto/ai/deepseek/MessageDto.java new file mode 100644 index 0000000..19af4da --- /dev/null +++ b/src/main/java/cn/teammodel/model/dto/ai/deepseek/MessageDto.java @@ -0,0 +1,14 @@ +package cn.teammodel.model.dto.ai.deepseek; + +import lombok.Data; + +/** + * 内部类定义请求/响应结构 + */ +@Data +public class MessageDto { + private String role; + private String sessionId; + private String content; + +} diff --git a/src/main/java/cn/teammodel/model/entity/ai/DeepSeekSession.java b/src/main/java/cn/teammodel/model/entity/ai/DeepSeekSession.java new file mode 100644 index 0000000..b31f3e0 --- /dev/null +++ b/src/main/java/cn/teammodel/model/entity/ai/DeepSeekSession.java @@ -0,0 +1,103 @@ +package cn.teammodel.model.entity.ai; + +import cn.hutool.core.lang.UUID; +import cn.teammodel.model.entity.BaseItem; +import com.azure.spring.data.cosmos.core.mapping.Container; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.Instant; +import java.util.List; + +/** + * deepseek聊天会话 + * @Date: 2023/6/5 + */ +@EqualsAndHashCode(callSuper = true) +@Container(containerName = "Teacher") +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class DeepSeekSession extends BaseItem { + + /** + * 用户id + */ + public String userId; + + /** + * 会话标题 + */ + private String title; + + /** + * 生成该 completion 的模型名。 + */ + private String model; + + /** + * 创建时间 + */ + private long createTime; + + /** + * 产生对话即更新时间,按更新时间排序 + */ + private Long updateTime; + + /** + * 会话历史记录 + */ + private List history; + + @Data + public static class DeepSeekMessage { + /** + * 该对话的唯一标识符。 + */ + private String id; + + /** + * 生成这条消息的角色。 + * 角色 [user, assistant, system] + * user:用户。 + * assistant:模型。 + * system:模型和用户之间的对话。 + */ + private String role; + + /** + * 用户的提问文本 + */ + private String userText; + + /** + * 回答的文本 + */ + private String aiText; + + /** + * 停止原因 [stop, length, content_filter, tool_calls, insufficient_system_resource] 模型停止生成 token 的原因。 + * stop:模型自然停止生成,或遇到 stop 序列中列出的字符串。 + * length :输出长度达到了模型上下文长度限制,或达到了 max_tokens 的限制。 + * content_filter:输出内容因触发过滤策略而被过滤。 + * insufficient_system_resource:系统推理资源不足,生成被打断。 + */ + private String finish_reason; + + /** + * 创建聊天完成时的 Unix 时间戳 + */ + private Long createTime; + + public static DeepSeekMessage of(String userText, String aiText){ + DeepSeekMessage message = new DeepSeekMessage(); + message.setId(UUID.randomUUID().toString()); + message.setUserText(userText); + message.setAiText(aiText); + message.setCreateTime(Instant.now().toEpochMilli()); + return message; + } + } + +} diff --git a/src/main/java/cn/teammodel/repository/DeepSeekRepository.java b/src/main/java/cn/teammodel/repository/DeepSeekRepository.java new file mode 100644 index 0000000..ea94b36 --- /dev/null +++ b/src/main/java/cn/teammodel/repository/DeepSeekRepository.java @@ -0,0 +1,58 @@ +package cn.teammodel.repository; + +import cn.teammodel.model.entity.ai.ChatSession; +import cn.teammodel.model.entity.ai.DeepSeekSession; +import com.azure.spring.data.cosmos.repository.CosmosRepository; +import com.azure.spring.data.cosmos.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; +/** + * 数据库操作接口 + * Created by jiangjingming on 17/6/19. + */ +@Repository +public interface DeepSeekRepository extends CosmosRepository { + /** + * 根据id和code查询会话信息 + * @param id + * @param code + * @return + */ + DeepSeekSession findSessionByIdAndCode(String id, String code); + + /** + * 根据会话id查询会话信息 + * @param sessionId + * @return + */ + @Query("select * from c where c.code='DeepSeekSession' and c.sessionId = @sessionId") + List findBySessionId(String sessionId); + + /** + * 根据用户id查询会话信息 + * @param userId + * @return + */ + @Query("select c.id,c.userId,c.title,c.model,c.createTime,c.updateTime from c where c.code='DeepSeekSession' and c.userId = @userId") + List findByUserId(String userId); + + /** + * 根据会话id查询会话信息 + * @param sessionId + * @return + */ + @Query("SELECT value ARRAY_SLICE(c.history, -3) FROM c where c.id = @sessionId and c.code = 'DeepSeekSession'") + List findLatestMessage(String sessionId); + + /** + * 根据用户id查询会话信息 + * @param userId + * @return + */ + @Query("select * from c where c.code = 'DeepSeekSession' and c.id = @userId") + List findCommentsById(String userId); + + + +} diff --git a/src/main/java/cn/teammodel/service/DeepSeekService.java b/src/main/java/cn/teammodel/service/DeepSeekService.java new file mode 100644 index 0000000..4184d42 --- /dev/null +++ b/src/main/java/cn/teammodel/service/DeepSeekService.java @@ -0,0 +1,20 @@ +package cn.teammodel.service; + +import cn.teammodel.model.dto.ai.deepseek.ChatResponseDto; +import cn.teammodel.model.dto.ai.deepseek.MessageDto; +import cn.teammodel.model.entity.ai.DeepSeekSession; + +import java.util.List; + +/** + * 访问DeepSeek方法 + */ +public interface DeepSeekService { + + /** + * 获取AI的回答 + * @param message + * @return + */ + ChatResponseDto ChatAsk(MessageDto message); +} diff --git a/src/main/java/cn/teammodel/service/DeepSeekSessionService.java b/src/main/java/cn/teammodel/service/DeepSeekSessionService.java new file mode 100644 index 0000000..90ef875 --- /dev/null +++ b/src/main/java/cn/teammodel/service/DeepSeekSessionService.java @@ -0,0 +1,60 @@ +package cn.teammodel.service; + +import cn.teammodel.model.dto.ai.deepseek.ChatResponseDto; +import cn.teammodel.model.entity.ai.DeepSeekSession; +import cn.teammodel.model.entity.ai.DeepSeekSession.DeepSeekMessage; + +import java.util.List; + +/** + * 会话服务接口 + * Created by Jiayiwu on 16/5/26. + */ +public interface DeepSeekSessionService { + + /** + * 创建默认会话 + * @param userId + * @param name + * @return + */ + DeepSeekSession CreateDefaultSession(String userId, String name); + + /** + * 创建会话 + * @param session + * @return + */ + DeepSeekSession CreateSession(DeepSeekSession session); + + /** + * 更新会话 + * @param upSession + * @param userId + * @return + */ + DeepSeekSession updateSession(DeepSeekSession upSession, String userId); + + /** + * 删除会话 + * @param id + * @param userId + */ + void deleteSession (String id, String userId); + + /** + * 获取用户会话列表 + * @param userId + * @return + */ + List UserSessionList(String userId); + + /** + * 获取历史消息 + * @param sessionId + * @param userId + * @return + */ + List listHistory(String sessionId, String userId); + +} diff --git a/src/main/java/cn/teammodel/service/impl/DeepSeekServiceImpl.java b/src/main/java/cn/teammodel/service/impl/DeepSeekServiceImpl.java new file mode 100644 index 0000000..8628826 --- /dev/null +++ b/src/main/java/cn/teammodel/service/impl/DeepSeekServiceImpl.java @@ -0,0 +1,110 @@ +package cn.teammodel.service.impl; + +import cn.teammodel.ai.deepseek.DeepSeekClient; +import cn.teammodel.common.PK; +import cn.teammodel.model.dto.ai.deepseek.ChatRequestDto; +import cn.teammodel.model.dto.ai.deepseek.ChatResponseDto; +import cn.teammodel.model.dto.ai.deepseek.MessageDto; +import cn.teammodel.model.entity.ai.DeepSeekSession; +import cn.teammodel.model.entity.ai.DeepSeekSession.DeepSeekMessage; +import cn.teammodel.repository.DeepSeekRepository; +import cn.teammodel.security.utils.SecurityUtil; +import cn.teammodel.service.DeepSeekService; +import cn.teammodel.service.DeepSeekSessionService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.time.Instant; +import java.util.*; + + +/** + * 描述:访问DeepSeek方法 + */ +@Service +@Slf4j +public class DeepSeekServiceImpl implements DeepSeekService { + @Resource + private DeepSeekSessionService deepSeekService; + + @Resource + private DeepSeekRepository deepSeekRepository; + + /** + * 提问 + * @param message + * @return + */ + @Override + public ChatResponseDto ChatAsk(MessageDto message) { + //创建消息列表 + List msg = new ArrayList<>(); + msg.add(message); + + //构建请求头 + ChatRequestDto requestBody = new ChatRequestDto(); + requestBody.setModel(DeepSeekClient.API_Model); + requestBody.setMessages(msg); + requestBody.setTemperature(0); + requestBody.setMax_tokens(1024); + + //开始时间 + long startTime = System.currentTimeMillis(); + //发起请求 + ChatResponseDto response = DeepSeekClient.SendRequests(requestBody); + //Map response = DeepSeekClient.SendRequests(requestBody); + //Map response = SendRequest(requestBody); + //结束时间 + long endTime = System.currentTimeMillis(); + //思考耗时 秒 + response.setWasteTime((endTime-startTime)/1000); + + if (response.getCode() == 200){ + DeepSeekMessage savaMessage = new DeepSeekMessage(); + savaMessage.setId(response.getId()); + savaMessage.setUserText(message.getContent()); + savaMessage.setAiText(response.getChoices().get(0).getMessage().getContent()); + savaMessage.setRole(response.getChoices().get(0).getMessage().getRole()); + savaMessage.setCreateTime(response.getCreated()); + savaMessage.setFinish_reason(response.getChoices().get(0).getFinish_reason()); + DeepSeekSession session = new DeepSeekSession(); + if (message.getSessionId() != null){ + session = deepSeekRepository.findSessionByIdAndCode(message.getSessionId(), PK.CHAT_SESSION); + UpdateSession(message, session, savaMessage, response); + }else { + UpdateSession(message, session, savaMessage, response); + } + } + return response; + } + + //region 辅助方法 + /** + * 新增/更新会话 + * @param message + * @param session + * @param savaMessage + * @param response + */ + private void UpdateSession(MessageDto message, DeepSeekSession session, DeepSeekMessage savaMessage, ChatResponseDto response) { + if (session.getId() == null){ + List history = Collections.singletonList(savaMessage); + String userId = SecurityUtil.getLoginUser().getId(); + session.setId(UUID.randomUUID().toString()); + session.setCode(PK.DEEPSEEK_SESSION); + session.setTitle("新对话"); + session.setUserId(userId); + session.setModel(response.getModel()); + session.setCreateTime(Instant.now().toEpochMilli()); + session.setUpdateTime(Instant.now().toEpochMilli()); + session.setHistory(history); + session =deepSeekRepository.save(session); + }else { + session.getHistory().add(savaMessage); + deepSeekService.updateSession(session, message.getSessionId()); + } + } + //endregion + +} diff --git a/src/main/java/cn/teammodel/service/impl/DeepSeekSessionServiceImpl.java b/src/main/java/cn/teammodel/service/impl/DeepSeekSessionServiceImpl.java new file mode 100644 index 0000000..dac56eb --- /dev/null +++ b/src/main/java/cn/teammodel/service/impl/DeepSeekSessionServiceImpl.java @@ -0,0 +1,133 @@ +package cn.teammodel.service.impl; + +import cn.teammodel.common.ErrorCode; +import cn.teammodel.common.PK; +import cn.teammodel.config.exception.ServiceException; +import cn.teammodel.model.entity.ai.ChatSession; +import cn.teammodel.model.entity.ai.DeepSeekSession; +import cn.teammodel.model.entity.ai.DeepSeekSession.DeepSeekMessage; +import cn.teammodel.repository.DeepSeekRepository; +import cn.teammodel.service.DeepSeekSessionService; +import cn.teammodel.utils.RepositoryUtil; +import com.azure.cosmos.models.CosmosPatchOperations; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.time.Instant; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class DeepSeekSessionServiceImpl implements DeepSeekSessionService { + @Resource // 注入 + private DeepSeekRepository deepSeekRepository; + + /** + * 创建初始会话 + * @param userId + * @param name + * @return + */ + @Override + public DeepSeekSession CreateDefaultSession(String userId, String name) { + //初始化第一条欢迎语 + DeepSeekMessage message = DeepSeekMessage.of("你好"+name, "我是你的私人 DeepSeek AI 助手小豆,你可以问我任何包括但不仅限于教育的问题,我会尽力为您解答!"); + List history = Collections.singletonList(message); + DeepSeekSession session = new DeepSeekSession(); + session.setId(UUID.randomUUID().toString()); + session.setCode(PK.DEEPSEEK_SESSION); + session.setTitle("新对话"); + session.setUserId(userId); + session.setCreateTime(Instant.now().toEpochMilli()); + session.setUpdateTime(Instant.now().toEpochMilli()); + session.setHistory(history); + return deepSeekRepository.save(session); + } + + /** + * 创建新会话 + * @param session + * @return + */ + @Override + public DeepSeekSession CreateSession(DeepSeekSession session) { + return deepSeekRepository.save(session); + } + + /** + * 更新会话 + * @param upSession + * @param userId + * @return + */ + @Override + public DeepSeekSession updateSession (DeepSeekSession upSession, String userId) + { + String id = upSession.getId(); + String title = upSession.getTitle(); + DeepSeekSession session = RepositoryUtil.findOne(deepSeekRepository.findBySessionId(id), ""); + if (!session.getUserId().equals(userId)) { + throw new ServiceException(ErrorCode.NO_AUTH_ERROR); + } + CosmosPatchOperations options = CosmosPatchOperations.create() + .replace("/title", title); + + deepSeekRepository.save(id, PK.of(PK.CHAT_SESSION), DeepSeekSession.class, options); + return session; + } + + /** + * 删除会话 + * @param id + * @param userId + */ + @Override + public void deleteSession (String id, String userId) + { + DeepSeekSession session = RepositoryUtil.findOne(deepSeekRepository.findBySessionId(id), ""); + if (!session.getUserId().equals(userId)) { + throw new ServiceException(ErrorCode.NO_AUTH_ERROR); + } + deepSeekRepository.deleteById(id, PK.of(PK.CHAT_SESSION)); + } + + + /** + * 获取用户会话列表 + * @param userId + * @return + */ + @Override + public List UserSessionList(String userId) { + List sessions = deepSeekRepository.findByUserId(userId); + // 按更新时间排序 + if (ObjectUtils.isNotEmpty(sessions)) + { + sessions = sessions.stream().sorted(Comparator.comparing(DeepSeekSession::getUpdateTime)).collect(Collectors.toList()); + } + return sessions; + } + + /** + * 获取历史消息 + * @param sessionId + * @param userId + * @return + */ + @Override + public List listHistory(String sessionId, String userId) { + DeepSeekSession session = deepSeekRepository.findSessionByIdAndCode(sessionId, PK.DEEPSEEK_SESSION); + if (!userId.equals(session.getUserId())) { + throw new ServiceException(ErrorCode.NO_AUTH_ERROR); + } + return session.getHistory(); + } + + +} diff --git a/src/main/resources/DeepSeekConfig.properties b/src/main/resources/DeepSeekConfig.properties new file mode 100644 index 0000000..0ca02e2 --- /dev/null +++ b/src/main/resources/DeepSeekConfig.properties @@ -0,0 +1,3 @@ +key=sk-83b5b6dd85c745cbae2572ea01c54e44 +url=https://api.deepseek.com/chat/completions +model=deepseek-chat \ No newline at end of file