新增ollama模型对话

This commit is contained in:
启航老齐 2026-03-07 14:57:43 +08:00
parent 1c936afe4c
commit 37c0f3feef
5 changed files with 79 additions and 232 deletions

View File

@ -141,6 +141,8 @@
<!-- <version>1.18.30</version>--> <!-- <version>1.18.30</version>-->
<!-- <scope>provided</scope>--> <!-- <scope>provided</scope>-->
<!-- </dependency>--> <!-- </dependency>-->
</dependencies> </dependencies>
<build> <build>

View File

@ -1,36 +0,0 @@
//package cn.qihangerp.erp.config;
//
//import cn.qihangerp.erp.filter.UrlTokenFilter;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.security.config.annotation.web.builders.HttpSecurity;
//import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
//import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
//import org.springframework.security.web.SecurityFilterChain;
//import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
//
///**
// * Spring Security配置
// *
// * @author qihang
// */
//@Configuration
//@EnableWebSecurity
//public class SecurityConfig {
// @Autowired
// private UrlTokenFilter urlTokenFilter;
//
// @Bean
// public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// http
// .csrf(AbstractHttpConfigurer::disable)
// .authorizeRequests(authorizeRequests ->
// authorizeRequests
// .anyRequest().permitAll()
// )
// .addFilterBefore(urlTokenFilter, UsernamePasswordAuthenticationFilter.class);
//
// return http.build();
// }
//}

View File

@ -1,8 +1,7 @@
package cn.qihangerp.erp.controller; package cn.qihangerp.erp.controller;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@ -10,11 +9,9 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import cn.qihangerp.erp.serviceImpl.AiService;
import java.io.IOException; import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -28,6 +25,9 @@ public class SseController {
private static final Map<String, SseEmitter> emitters = new ConcurrentHashMap<>(); private static final Map<String, SseEmitter> emitters = new ConcurrentHashMap<>();
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
@Autowired
private AiService aiService;
@GetMapping(value = "/connect", produces = MediaType.TEXT_EVENT_STREAM_VALUE) @GetMapping(value = "/connect", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter connect(@RequestParam String clientId) { public SseEmitter connect(@RequestParam String clientId) {
@ -69,93 +69,22 @@ public class SseController {
SseEmitter emitter = emitters.get(clientId); SseEmitter emitter = emitters.get(clientId);
if (emitter != null) { if (emitter != null) {
try { try {
// 调用opencode接口获取回复 // 使用AiService处理消息
String response = callOpencodeApi(message); String response = aiService.processMessage(message);
emitter.send(SseEmitter.event() emitter.send(SseEmitter.event()
.name("message") .name("message")
.data(response)); .data(response));
return "消息发送成功"; return "消息发送成功";
} catch (Exception e) { } catch (Exception e) {
log.error("消息处理失败: {}", e.getMessage());
emitters.remove(clientId); emitters.remove(clientId);
return "消息发送失败"; return "消息发送失败";
} }
} }
return "客户端不存在"; return "客户端不存在";
} }
private String callOpencodeApi(String message) throws Exception {
// 创建HTTP客户端
HttpClient client = HttpClient.newHttpClient();
// 1. 创建新会话
HttpRequest createSessionRequest = HttpRequest.newBuilder()
.uri(URI.create("http://localhost:14967/session"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{}"))
.build();
HttpResponse<String> createSessionResponse = client.send(createSessionRequest, HttpResponse.BodyHandlers.ofString());
String sessionId = parseSessionId(createSessionResponse.body());
// 2. 构建消息请求体
JSONObject requestBody = new JSONObject();
JSONArray parts = new JSONArray();
JSONObject part = new JSONObject();
part.put("type", "text");
part.put("text", message);
parts.add(part);
requestBody.put("parts", parts);
// 3. 向会话发送消息
HttpRequest sendMessageRequest = HttpRequest.newBuilder()
.uri(URI.create("http://localhost:14967/session/" + sessionId + "/message"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody.toJSONString()))
.build();
// 发送请求并获取响应
HttpResponse<String> response = client.send(sendMessageRequest, HttpResponse.BodyHandlers.ofString());
// 解析响应提取AI回复
return parseAIResponse(response.body());
}
private String parseSessionId(String responseBody) {
// 简单解析JSON提取sessionId
// 实际项目中建议使用JSON库
int idIndex = responseBody.indexOf("\"id\":\"");
if (idIndex != -1) {
int start = idIndex + 6;
int end = responseBody.indexOf("\"", start);
if (end != -1) {
return responseBody.substring(start, end);
}
}
return "";
}
private String parseAIResponse(String responseBody) {
log.info("=================AI回复==========");
log.info(responseBody);
try {
// 解析响应提取AI回复
JSONObject jsonObject = JSONObject.parseObject(responseBody);
if (jsonObject.containsKey("info")) {
JSONArray parts = jsonObject.getJSONArray("parts");
for (int i = 0; i < parts.size(); i++) {
JSONObject part = parts.getJSONObject(i);
if (part.containsKey("text")) {
return part.getString("text");
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return "无法获取AI回复";
}
@GetMapping("/disconnect") @GetMapping("/disconnect")
public String disconnect(@RequestParam String clientId) { public String disconnect(@RequestParam String clientId) {

View File

@ -1,115 +0,0 @@
//package cn.qihangerp.erp.filter;
//
//import cn.qihangerp.common.AjaxResult;
//import cn.qihangerp.common.enums.HttpStatus;
//import cn.qihangerp.security.LoginUser;
//import cn.qihangerp.security.TokenService;
//import com.alibaba.fastjson2.JSON;
//import jakarta.servlet.FilterChain;
//import jakarta.servlet.ServletException;
//import jakarta.servlet.http.HttpServletRequest;
//import jakarta.servlet.http.HttpServletRequestWrapper;
//import jakarta.servlet.http.HttpServletResponse;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.http.MediaType;
//import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
//import org.springframework.security.core.context.SecurityContextHolder;
//import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
//import org.springframework.stereotype.Component;
//import org.springframework.web.filter.OncePerRequestFilter;
//
//import java.io.IOException;
//import java.io.PrintWriter;
//
///**
// * token过滤器 从URL参数中获取token并验证有效性
// *
// * @author qihang
// */
//@Component
//public class UrlTokenFilter extends OncePerRequestFilter {
// @Autowired
// private TokenService tokenService;
// private Logger log = LoggerFactory.getLogger(getClass());
//
// @Override
// protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
// throws ServletException, IOException {
// // 从URL参数中获取token
// String token = request.getParameter("token");
// String url = request.getRequestURI();
//
// // 跳过登录等不需要token的请求
// if (url.contains("/login") || url.contains("/captchaImage")) {
// chain.doFilter(request, response);
// return;
// }
//
// // 如果URL参数中没有token尝试从header中获取保持兼容性
// if (token == null || token.isEmpty()) {
// token = request.getHeader("Authorization");
// }
//
// // 验证token
// if (token != null && !token.isEmpty()) {
// // 移除Bearer前缀
// if (token.startsWith("Bearer ")) {
// token = token.substring(7);
// }
//
// // 将token声明为final以便内部类可以引用
// final String finalToken = token;
//
// // 将token设置到请求的header中以便TokenService能够正常工作
// final HttpServletRequest modifiedRequest = new HttpServletRequestWrapper(request) {
// @Override
// public String getHeader(String name) {
// if ("Authorization".equals(name)) {
// return "Bearer " + finalToken;
// }
// return super.getHeader(name);
// }
// };
//
// // 验证token并设置用户信息
// try {
// LoginUser loginUser = tokenService.getLoginUser(modifiedRequest);
// if (loginUser != null) {
// tokenService.verifyToken(loginUser);
// UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
// authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(modifiedRequest));
// SecurityContextHolder.getContext().setAuthentication(authenticationToken);
// chain.doFilter(modifiedRequest, response);
// return;
// }
// } catch (Exception e) {
// log.error("Token validation failed: {}", e.getMessage());
// }
// }
//
// // token无效或不存在
// fallback("授权过期!", response);
// }
//
// private void fallback(String message, HttpServletResponse response) {
// response.setCharacterEncoding("UTF-8");
// response.setContentType(MediaType.APPLICATION_JSON_VALUE);
// PrintWriter writer = null;
// try {
// if (message == null) {
// message = "401 Forbidden";
// }
// AjaxResult res = AjaxResult.error(HttpStatus.UNAUTHORIZED, message);
// writer = response.getWriter();
// writer.append(JSON.toJSONString(res));
// } catch (IOException e) {
// log.error(e.getMessage());
// } finally {
// if (writer != null) {
// writer.close();
// }
// }
// }
//}

View File

@ -0,0 +1,67 @@
package cn.qihangerp.erp.serviceImpl;
import cn.qihangerp.common.ResultVo;
import com.alibaba.fastjson2.JSONObject;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
/**
* AI服务类直接调用Ollama API处理聊天内容
*/
@Service
public class AiService {
private final HttpClient httpClient;
private final String ollamaUrl;
public AiService() {
this.httpClient = HttpClient.newHttpClient();
this.ollamaUrl = "http://localhost:11434/api/generate";
}
/**
* 处理聊天消息
* @param message 用户消息
* @return AI回复
*/
public String processMessage(String message) {
try {
// 构建请求体
JSONObject requestBody = new JSONObject();
requestBody.put("model", "llama3");
requestBody.put("prompt", message);
requestBody.put("stream", false);
requestBody.put("temperature", 0.7);
// 创建HTTP请求
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(ollamaUrl))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody.toJSONString()))
.build();
// 发送请求并获取响应
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// 解析响应
JSONObject responseBody = JSONObject.parseObject(response.body());
// 检查是否有错误
if (responseBody.containsKey("error")) {
String errorMessage = responseBody.getString("error");
return "错误: " + errorMessage;
}
return responseBody.getString("response");
} catch (IOException | InterruptedException e) {
e.printStackTrace();
return "抱歉,我暂时无法处理您的请求,请稍后重试。";
}
}
}