diff --git a/api/ai-agent/pom.xml b/api/ai-agent/pom.xml index 74bd26a0..d4e9568d 100644 --- a/api/ai-agent/pom.xml +++ b/api/ai-agent/pom.xml @@ -141,6 +141,8 @@ + + diff --git a/api/ai-agent/src/main/java/cn/qihangerp/erp/config/SecurityConfig.java b/api/ai-agent/src/main/java/cn/qihangerp/erp/config/SecurityConfig.java deleted file mode 100644 index 5e77123e..00000000 --- a/api/ai-agent/src/main/java/cn/qihangerp/erp/config/SecurityConfig.java +++ /dev/null @@ -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(); -// } -//} diff --git a/api/ai-agent/src/main/java/cn/qihangerp/erp/controller/SseController.java b/api/ai-agent/src/main/java/cn/qihangerp/erp/controller/SseController.java index 5fcc19c6..0da6114e 100644 --- a/api/ai-agent/src/main/java/cn/qihangerp/erp/controller/SseController.java +++ b/api/ai-agent/src/main/java/cn/qihangerp/erp/controller/SseController.java @@ -1,8 +1,7 @@ package cn.qihangerp.erp.controller; -import com.alibaba.fastjson2.JSONArray; -import com.alibaba.fastjson2.JSONObject; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; 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.servlet.mvc.method.annotation.SseEmitter; +import cn.qihangerp.erp.serviceImpl.AiService; + 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.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; @@ -28,6 +25,9 @@ public class SseController { private static final Map emitters = new ConcurrentHashMap<>(); private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); + + @Autowired + private AiService aiService; @GetMapping(value = "/connect", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter connect(@RequestParam String clientId) { @@ -69,93 +69,22 @@ public class SseController { SseEmitter emitter = emitters.get(clientId); if (emitter != null) { try { - // 调用opencode接口获取回复 - String response = callOpencodeApi(message); + // 使用AiService处理消息 + String response = aiService.processMessage(message); emitter.send(SseEmitter.event() .name("message") .data(response)); + return "消息发送成功"; } catch (Exception e) { + log.error("消息处理失败: {}", e.getMessage()); emitters.remove(clientId); 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 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 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") public String disconnect(@RequestParam String clientId) { diff --git a/api/ai-agent/src/main/java/cn/qihangerp/erp/filter/UrlTokenFilter.java b/api/ai-agent/src/main/java/cn/qihangerp/erp/filter/UrlTokenFilter.java deleted file mode 100644 index 2af6d8fa..00000000 --- a/api/ai-agent/src/main/java/cn/qihangerp/erp/filter/UrlTokenFilter.java +++ /dev/null @@ -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(); -// } -// } -// } -//} diff --git a/api/ai-agent/src/main/java/cn/qihangerp/erp/serviceImpl/AiService.java b/api/ai-agent/src/main/java/cn/qihangerp/erp/serviceImpl/AiService.java new file mode 100644 index 00000000..af8998e4 --- /dev/null +++ b/api/ai-agent/src/main/java/cn/qihangerp/erp/serviceImpl/AiService.java @@ -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 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 "抱歉,我暂时无法处理您的请求,请稍后重试。"; + } + } +}