feat(oa): 添加 AI 对话管理功能

- 新增 AI配置、对话历史、对话消息等相关的实体类、Mapper、Service 和 Controller
- 实现 AI 对话管理的基础功能,包括创建对话、发送消息、结束对话等- 集成 DeepSeek AI 服务,实现与 AI 模型的交互
- 添加 token消耗和费用计算相关逻辑
This commit is contained in:
2025-08-04 14:24:18 +08:00
parent 2fa67cb3c1
commit c93959e351
21 changed files with 1808 additions and 0 deletions

View File

@@ -0,0 +1,125 @@
package com.ruoyi.oa.utils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.oa.domain.vo.SysOaAiConfigVo;
import com.ruoyi.oa.domain.vo.SysOaAiMessageVo;
import com.ruoyi.oa.service.ISysOaAiConfigService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.util.*;
/**
* AI服务工具类
*
* @author ruoyi
* @date 2024-12-19
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class AiServiceUtil {
private final ISysOaAiConfigService configService;
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
/**
* 调用DeepSeek AI服务单次对话
*
* @param message 用户消息
* @return AI回复
*/
public String callDeepSeek(String message) {
return callDeepSeekWithHistory(message, null);
}
/**
* 调用DeepSeek AI服务连续对话
*
* @param message 用户消息
* @param conversationHistory 对话历史
* @return AI回复
*/
public String callDeepSeekWithHistory(String message, List<SysOaAiMessageVo> conversationHistory) {
try {
// 获取AI配置
SysOaAiConfigVo config = configService.getActiveConfig();
if (config == null) {
throw new RuntimeException("未找到可用的AI配置");
}
// 构建请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " + config.getApiKey());
// 构建请求体
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("model", config.getModelName());
requestBody.put("temperature", config.getTemperature());
requestBody.put("max_tokens", config.getMaxTokens());
List<Map<String, String>> messages = new ArrayList<>();
// 添加对话历史
if (conversationHistory != null && !conversationHistory.isEmpty()) {
for (SysOaAiMessageVo historyMessage : conversationHistory) {
Map<String, String> historyMsg = new HashMap<>();
historyMsg.put("role", historyMessage.getRole());
historyMsg.put("content", historyMessage.getContent());
messages.add(historyMsg);
}
}
// 添加当前用户消息
Map<String, String> userMessage = new HashMap<>();
userMessage.put("role", "user");
userMessage.put("content", message);
messages.add(userMessage);
requestBody.put("messages", messages);
// 发送请求
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
String url = config.getBaseUrl() + "/chat/completions";
log.info("调用DeepSeek API: {}, 消息数量: {}", url, messages.size());
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
if (response.getStatusCode() == HttpStatus.OK) {
JsonNode jsonNode = objectMapper.readTree(response.getBody());
String aiResponse = jsonNode.path("choices").path(0).path("message").path("content").asText();
// 记录token使用情况
int totalTokens = jsonNode.path("usage").path("total_tokens").asInt();
log.info("AI回复成功消耗token: {}", totalTokens);
return aiResponse;
} else {
log.error("DeepSeek API调用失败: {}", response.getStatusCode());
throw new RuntimeException("AI服务调用失败");
}
} catch (Exception e) {
log.error("调用DeepSeek服务异常", e);
throw new RuntimeException("AI服务调用异常: " + e.getMessage());
}
}
/**
* 计算费用示例每1000个token 0.002美元)
*
* @param tokens token数量
* @return 费用
*/
public BigDecimal calculateCost(int tokens) {
// 这里可以根据实际的定价策略计算费用
BigDecimal costPerToken = new BigDecimal("0.000002"); // 每token 0.000002美元
return costPerToken.multiply(new BigDecimal(tokens));
}
}