瀏覽代碼

strategy execute result

supeng 2 周之前
父節點
當前提交
3ea7df64ab

+ 393 - 63
supply-demand-engine-core/src/main/java/com/tzld/piaoquan/sde/integration/OpenRouterClient.java

@@ -3,20 +3,24 @@ package com.tzld.piaoquan.sde.integration;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
+import lombok.Builder;
+import lombok.Data;
 import lombok.extern.slf4j.Slf4j;
 import okhttp3.*;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
 
 import javax.annotation.PostConstruct;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
 /**
  * OpenRouter API Client
- * 支持通过 OpenRouter 调用 Gemini 和 ChatGPT 模型
+ * 支持通过 OpenRouter 调用 Gemini 和 ChatGPT 等多种模型
  *
  * @author supeng
  */
@@ -39,6 +43,12 @@ public class OpenRouterClient {
     @Value("${openrouter.timeout:60}")
     private int timeout;
 
+    @Value("${openrouter.default.gemini.model:google/gemini-2.0-flash-exp}")
+    private String defaultGeminiModel;
+
+    @Value("${openrouter.default.gpt.model:openai/gpt-4o-mini}")
+    private String defaultGptModel;
+
     private OkHttpClient httpClient;
 
     @PostConstruct
@@ -51,127 +61,447 @@ public class OpenRouterClient {
     }
 
     /**
-     * 调用 OpenRouter API
+     * 使用 ChatRequest 对象调用 API
      *
-     * @param model       模型名称,例如: "google/gemini-pro", "openai/gpt-4", "openai/gpt-3.5-turbo"
-     * @param messages    消息列表,每个消息包含 role 和 content
-     * @param temperature 温度参数,控制随机性 (0.0-2.0)
-     * @param maxTokens   最大生成 token 数
-     * @return API 响应内容
+     * @param request 聊天请求对象
+     * @return 聊天响应对象
      * @throws IOException 网络请求异常
      */
-    public String chat(String model, List<Map<String, String>> messages, Double temperature, Integer maxTokens) throws IOException {
-        JSONObject requestBody = buildRequestBody(model, messages, temperature, maxTokens);
+    public ChatResponse chat(ChatRequest request) throws IOException {
+        validateRequest(request);
 
-        Request request = new Request.Builder()
-                .url(url)
-                .addHeader("Authorization", "Bearer " + apiKey)
-                .addHeader("Content-Type", "application/json")
-                .addHeader("HTTP-Referer", "https://github.com/tzld")
-                .addHeader("X-Title", "Supply Demand Engine")
-                .post(RequestBody.create(JSON_MEDIA_TYPE, requestBody.toJSONString()))
-                .build();
+        JSONObject requestBody = buildRequestBody(request);
+        Request httpRequest = buildHttpRequest(requestBody);
+
+        log.info("Calling OpenRouter API with model: {}", request.getModel());
+        log.debug("Request body: {}", requestBody.toJSONString());
 
-        log.info("Calling OpenRouter API with model: {}", model);
+        try (Response response = httpClient.newCall(httpRequest).execute()) {
+            String responseBody = response.body() != null ? response.body().string() : "";
 
-        try (Response response = httpClient.newCall(request).execute()) {
             if (!response.isSuccessful()) {
-                String errorBody = response.body() != null ? response.body().string() : "No error body";
-                log.error("OpenRouter API call failed: {} - {}", response.code(), errorBody);
-                throw new IOException("OpenRouter API call failed: " + response.code() + " - " + errorBody);
+                log.error("OpenRouter API call failed: {} - {}", response.code(), responseBody);
+                throw new IOException("OpenRouter API call failed: " + response.code() + " - " + responseBody);
             }
 
-            String responseBody = response.body().string();
             log.debug("OpenRouter API response: {}", responseBody);
-
             return parseResponse(responseBody);
         }
     }
 
     /**
-     * 简化的调用方法,使用默认参数
+     * 简化调用 - 使用简单字符串
+     *
+     * @param model   模型名称
+     * @param content 用户消息内容
+     * @return 响应内容
+     * @throws IOException 网络请求异常
+     */
+    public String chat(String model, String content) throws IOException {
+        return chat(ChatRequest.builder()
+                .model(model)
+                .addUserMessage(content)
+                .build()).getContent();
+    }
+
+    /**
+     * 简化调用 - 使用简单字符串,带系统提示
+     *
+     * @param model        模型名称
+     * @param systemPrompt 系统提示
+     * @param userContent  用户消息内容
+     * @return 响应内容
+     * @throws IOException 网络请求异常
+     */
+    public String chat(String model, String systemPrompt, String userContent) throws IOException {
+        return chat(ChatRequest.builder()
+                .model(model)
+                .addSystemMessage(systemPrompt)
+                .addUserMessage(userContent)
+                .build()).getContent();
+    }
+
+    /**
+     * 简化调用 - 使用 Map 格式消息列表
      *
      * @param model    模型名称
      * @param messages 消息列表
-     * @return API 响应内容
+     * @return 响应内容
      * @throws IOException 网络请求异常
      */
     public String chat(String model, List<Map<String, String>> messages) throws IOException {
-        return chat(model, messages, 0.7, 2000);
+        ChatRequest request = ChatRequest.builder()
+                .model(model)
+                .messages(convertMessages(messages))
+                .build();
+        return chat(request).getContent();
     }
 
     /**
-     * 调用 Gemini 模型
+     * 简化调用 - 带参数
      *
-     * @param messages 消息列表
-     * @return API 响应内容
+     * @param model       模型名称
+     * @param messages    消息列表
+     * @param temperature 温度参数
+     * @param maxTokens   最大 token 数
+     * @return 响应内容
      * @throws IOException 网络请求异常
      */
-    public String chatWithGemini(List<Map<String, String>> messages) throws IOException {
-        return chat("google/gemini-pro", messages);
+    public String chat(String model, List<Map<String, String>> messages, Double temperature, Integer maxTokens) throws IOException {
+        ChatRequest request = ChatRequest.builder()
+                .model(model)
+                .messages(convertMessages(messages))
+                .temperature(temperature)
+                .maxTokens(maxTokens)
+                .build();
+        return chat(request).getContent();
     }
 
     /**
-     * 调用 ChatGPT 模型
+     * 调用 Gemini 模型 - 使用配置的默认模型
      *
-     * @param messages 消息列表
-     * @param useGpt4  是否使用 GPT-4,false 则使用 GPT-3.5-turbo
-     * @return API 响应内容
+     * @param content 用户消息内容
+     * @return 响应内容
      * @throws IOException 网络请求异常
      */
-    public String chatWithGPT(List<Map<String, String>> messages, boolean useGpt4) throws IOException {
-        String model = useGpt4 ? "openai/gpt-4" : "openai/gpt-3.5-turbo";
-        return chat(model, messages);
+    public String chatWithGemini(String content) throws IOException {
+        return chat(defaultGeminiModel, content);
+    }
+
+    /**
+     * 调用 Gemini 模型 - 指定具体模型
+     *
+     * @param model   Gemini 模型名称
+     * @param content 用户消息内容
+     * @return 响应内容
+     * @throws IOException 网络请求异常
+     */
+    public String chatWithGemini(String model, String content) throws IOException {
+        return chat(model, content);
+    }
+
+    /**
+     * 调用 ChatGPT 模型 - 使用配置的默认模型
+     *
+     * @param content 用户消息内容
+     * @return 响应内容
+     * @throws IOException 网络请求异常
+     */
+    public String chatWithGPT(String content) throws IOException {
+        return chat(defaultGptModel, content);
+    }
+
+    /**
+     * 调用 ChatGPT 模型 - 指定具体模型
+     *
+     * @param model   GPT 模型名称
+     * @param content 用户消息内容
+     * @return 响应内容
+     * @throws IOException 网络请求异常
+     */
+    public String chatWithGPT(String model, String content) throws IOException {
+        return chat(model, content);
+    }
+
+    /**
+     * 创建请求构建器
+     */
+    public ChatRequest.ChatRequestBuilder requestBuilder() {
+        return ChatRequest.builder();
+    }
+
+    /**
+     * 验证请求参数
+     */
+    private void validateRequest(ChatRequest request) {
+        if (!StringUtils.hasText(request.getModel())) {
+            throw new IllegalArgumentException("Model cannot be empty");
+        }
+        if (request.getMessages() == null || request.getMessages().isEmpty()) {
+            throw new IllegalArgumentException("Messages cannot be empty");
+        }
     }
 
     /**
      * 构建请求体
      */
-    private JSONObject buildRequestBody(String model, List<Map<String, String>> messages, Double temperature, Integer maxTokens) {
+    private JSONObject buildRequestBody(ChatRequest request) {
         JSONObject requestBody = new JSONObject();
-        requestBody.put("model", model);
+        requestBody.put("model", request.getModel());
 
         JSONArray messagesArray = new JSONArray();
-        for (Map<String, String> message : messages) {
+        for (Message message : request.getMessages()) {
             JSONObject messageNode = new JSONObject();
-            messageNode.put("role", message.get("role"));
-            messageNode.put("content", message.get("content"));
+            messageNode.put("role", message.getRole());
+            messageNode.put("content", message.getContent());
             messagesArray.add(messageNode);
         }
         requestBody.put("messages", messagesArray);
 
-        if (temperature != null) {
-            requestBody.put("temperature", temperature);
+        if (request.getTemperature() != null) {
+            requestBody.put("temperature", request.getTemperature());
+        }
+        if (request.getMaxTokens() != null) {
+            requestBody.put("max_tokens", request.getMaxTokens());
+        }
+        if (request.getTopP() != null) {
+            requestBody.put("top_p", request.getTopP());
         }
-        if (maxTokens != null) {
-            requestBody.put("max_tokens", maxTokens);
+        if (request.getTopK() != null) {
+            requestBody.put("top_k", request.getTopK());
+        }
+        if (request.getFrequencyPenalty() != null) {
+            requestBody.put("frequency_penalty", request.getFrequencyPenalty());
+        }
+        if (request.getPresencePenalty() != null) {
+            requestBody.put("presence_penalty", request.getPresencePenalty());
+        }
+        if (request.getStop() != null && !request.getStop().isEmpty()) {
+            requestBody.put("stop", request.getStop());
         }
 
         return requestBody;
     }
 
     /**
-     * 解析响应,提取生成的内容
+     * 构建 HTTP 请求
+     */
+    private Request buildHttpRequest(JSONObject requestBody) {
+        return new Request.Builder()
+                .url(url)
+                .addHeader("Authorization", "Bearer " + apiKey)
+                .addHeader("Content-Type", "application/json")
+                .addHeader("HTTP-Referer", "https://github.com/tzld")
+                .addHeader("X-Title", "Supply Demand Engine")
+                .post(RequestBody.create(JSON_MEDIA_TYPE, requestBody.toJSONString()))
+                .build();
+    }
+
+    /**
+     * 解析响应
      */
-    private String parseResponse(String responseBody) throws IOException {
+    private ChatResponse parseResponse(String responseBody) throws IOException {
         try {
             JSONObject root = JSON.parseObject(responseBody);
             JSONArray choices = root.getJSONArray("choices");
-            if (choices != null && !choices.isEmpty()) {
-                JSONObject firstChoice = choices.getJSONObject(0);
-                JSONObject message = firstChoice.getJSONObject("message");
-                if (message != null) {
-                    String content = message.getString("content");
-                    if (content != null) {
-                        return content;
-                    }
-                }
+
+            if (choices == null || choices.isEmpty()) {
+                log.warn("No choices in response, returning empty response");
+                return ChatResponse.builder()
+                        .content("")
+                        .rawResponse(responseBody)
+                        .build();
             }
-            log.warn("Unable to parse content from response, returning full response");
-            return responseBody;
+
+            JSONObject firstChoice = choices.getJSONObject(0);
+            JSONObject message = firstChoice.getJSONObject("message");
+
+            String content = "";
+            String role = "assistant";
+            if (message != null) {
+                content = message.getString("content");
+                role = message.getString("role");
+            }
+
+            // 提取使用信息
+            JSONObject usage = root.getJSONObject("usage");
+            Integer promptTokens = null;
+            Integer completionTokens = null;
+            Integer totalTokens = null;
+
+            if (usage != null) {
+                promptTokens = usage.getInteger("prompt_tokens");
+                completionTokens = usage.getInteger("completion_tokens");
+                totalTokens = usage.getInteger("total_tokens");
+            }
+
+            return ChatResponse.builder()
+                    .content(content)
+                    .role(role)
+                    .promptTokens(promptTokens)
+                    .completionTokens(completionTokens)
+                    .totalTokens(totalTokens)
+                    .rawResponse(responseBody)
+                    .build();
+
         } catch (Exception e) {
-            log.error("Error parsing response: {}", e.getMessage());
-            return responseBody;
+            log.error("Error parsing response: {}", e.getMessage(), e);
+            throw new IOException("Failed to parse response: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 转换消息格式
+     */
+    private List<Message> convertMessages(List<Map<String, String>> messages) {
+        List<Message> result = new ArrayList<>();
+        for (Map<String, String> msg : messages) {
+            result.add(Message.builder()
+                    .role(msg.get("role"))
+                    .content(msg.get("content"))
+                    .build());
         }
+        return result;
+    }
+
+    /**
+     * 聊天请求对象
+     */
+    @Data
+    @Builder
+    public static class ChatRequest {
+        /**
+         * 模型名称,例如: "google/gemini-pro", "openai/gpt-4", "openai/gpt-3.5-turbo"
+         */
+        private String model;
+
+        /**
+         * 消息列表
+         */
+        private List<Message> messages;
+
+        /**
+         * 温度参数,控制随机性 (0.0-2.0)
+         */
+        private Double temperature;
+
+        /**
+         * 最大生成 token 数
+         */
+        private Integer maxTokens;
+
+        /**
+         * Top-p 采样参数 (0.0-1.0)
+         */
+        private Double topP;
+
+        /**
+         * Top-k 采样参数
+         */
+        private Integer topK;
+
+        /**
+         * 频率惩罚 (-2.0 到 2.0)
+         */
+        private Double frequencyPenalty;
+
+        /**
+         * 存在惩罚 (-2.0 到 2.0)
+         */
+        private Double presencePenalty;
+
+        /**
+         * 停止序列
+         */
+        private List<String> stop;
+
+        /**
+         * 添加消息的便捷方法
+         */
+        public static class ChatRequestBuilder {
+            public ChatRequestBuilder addMessage(String role, String content) {
+                if (this.messages == null) {
+                    this.messages = new ArrayList<>();
+                }
+                this.messages.add(Message.builder().role(role).content(content).build());
+                return this;
+            }
+
+            public ChatRequestBuilder addUserMessage(String content) {
+                return addMessage("user", content);
+            }
+
+            public ChatRequestBuilder addSystemMessage(String content) {
+                return addMessage("system", content);
+            }
+
+            public ChatRequestBuilder addAssistantMessage(String content) {
+                return addMessage("assistant", content);
+            }
+        }
+    }
+
+    /**
+     * 消息对象
+     */
+    @Data
+    @Builder
+    public static class Message {
+        /**
+         * 角色: system, user, assistant
+         */
+        private String role;
+
+        /**
+         * 消息内容
+         */
+        private String content;
+
+        public static Message user(String content) {
+            return Message.builder().role("user").content(content).build();
+        }
+
+        public static Message system(String content) {
+            return Message.builder().role("system").content(content).build();
+        }
+
+        public static Message assistant(String content) {
+            return Message.builder().role("assistant").content(content).build();
+        }
+    }
+
+    /**
+     * 聊天响应对象
+     */
+    @Data
+    @Builder
+    public static class ChatResponse {
+        /**
+         * 生成的内容
+         */
+        private String content;
+
+        /**
+         * 角色
+         */
+        private String role;
+
+        /**
+         * 提示词使用的 token 数
+         */
+        private Integer promptTokens;
+
+        /**
+         * 生成内容使用的 token 数
+         */
+        private Integer completionTokens;
+
+        /**
+         * 总 token 数
+         */
+        private Integer totalTokens;
+
+        /**
+         * 原始响应
+         */
+        private String rawResponse;
+    }
+
+    /**
+     * 常用模型常量
+     */
+    public static class Models {
+        public static final String GEMINI_3_PRO_PREVIEW = "google/gemini-3-pro-preview";
+        public static final String GEMINI_25_PRO = "google/gemini-2.5-pro";
+        public static final String GEMINI_3_FLASH_PREVIEW = "google/gemini-3-flash-preview";
+        public static final String GEMINI_25_FLASH = "google/gemini-2.5-flash";
+        public static final String GEMINI_2_FLASH = "google/gemini-2.0-flash-001";
+        public static final String GPT_4O_MINI = "openai/gpt-4o-mini";
+        public static final String GPT_5_MINI = "openai/gpt-5-mini";
+        public static final String GPT_52 = "openai/gpt-5.2";
+        public static final String GPT_5 = "openai/gpt-5";
+        public static final String CLAUDE_OPUS_45 = "anthropic/claude-opus-4.5";
+        public static final String CLAUDE_SONNET_45 = "anthropic/claude-sonnet-4.5";
+        public static final String CLAUDE_HAIKU_45 = "anthropic/claude-haiku-4.5";
     }
 }

+ 17 - 0
supply-demand-engine-core/src/main/java/com/tzld/piaoquan/sde/model/dto/StrategyConfigDTO.java

@@ -0,0 +1,17 @@
+package com.tzld.piaoquan.sde.model.dto;
+
+import lombok.Data;
+
+/**
+ * 策略配置
+ *
+ * @author supeng
+ */
+@Data
+public class StrategyConfigDTO {
+    private String model;
+    private Double temperature;
+    private Integer maxTokens;
+    private Double topP;
+    private Double topK;
+}

+ 17 - 0
supply-demand-engine-core/src/main/java/com/tzld/piaoquan/sde/model/dto/StrategyResultDTO.java

@@ -0,0 +1,17 @@
+package com.tzld.piaoquan.sde.model.dto;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * 策略执行结果
+ *
+ * @author supeng
+ */
+@Data
+@Builder
+public class StrategyResultDTO {
+    private boolean isSuccess;
+    private String result;
+    private String msg;
+}

+ 26 - 2
supply-demand-engine-core/src/main/java/com/tzld/piaoquan/sde/service/impl/TaskServiceImpl.java

@@ -9,6 +9,7 @@ import com.tzld.piaoquan.sde.common.enums.*;
 import com.tzld.piaoquan.sde.common.exception.BizException;
 import com.tzld.piaoquan.sde.integration.OpenRouterClient;
 import com.tzld.piaoquan.sde.mapper.*;
+import com.tzld.piaoquan.sde.model.dto.StrategyResultDTO;
 import com.tzld.piaoquan.sde.model.entity.*;
 import com.tzld.piaoquan.sde.model.request.TaskCreateParam;
 import com.tzld.piaoquan.sde.model.request.TaskGetParam;
@@ -56,6 +57,8 @@ public class TaskServiceImpl implements TaskService {
     private OpenRouterClient openRouterClient;
     @Autowired
     private Map<Integer, DeconstructStrategy> strategyMap;
+    @Autowired
+    private SdTaskResultMapper sdTaskResultMapper;
 
     @Override
     public void create(CommonRequest<TaskCreateParam> request) {
@@ -282,8 +285,29 @@ public class TaskServiceImpl implements TaskService {
                     return;
                 }
                 DemandExtractionStrategy demandExtractionStrategy = strategyMap.get(sdTask.getTaskType());
-                demandExtractionStrategy.execute(sdTask, strategy);
-
+                StrategyResultDTO strategyResultDTO = demandExtractionStrategy.execute(sdTask, strategy);
+                if (Objects.isNull(strategyResultDTO)) {
+                    SdTask updateTask = new SdTask();
+                    updateTask.setId(sdTask.getId());
+                    updateTask.setTaskStatus(TaskStatusEnum.FAILED.getValue());
+                    updateTask.setErrorMsg("");
+                    int updateRows = sdTaskMapper.updateById(updateTask);
+                    log.info("");
+                    continue;
+                }
+                //成功
+                SdTaskResult sdTaskResult = new SdTaskResult();
+                sdTaskResult.setTaskId(sdTask.getId());
+                sdTaskResult.setResult(strategyResultDTO.getResult());
+                int rows = sdTaskResultMapper.insert(sdTaskResult);
+                log.info("taskResult insert rows:{}", rows);
+                if (rows > 0) {
+                    SdTask updateTask = new SdTask();
+                    updateTask.setId(sdTask.getId());
+                    updateTask.setTaskStatus(TaskStatusEnum.SUCCESS.getValue());
+                    int updateRows = sdTaskMapper.updateById(updateTask);
+                    log.info("task success update rows:{}", updateRows);
+                }
             } catch (Exception e) {
                 log.error("taskExecuteHandler error", e);
             }

+ 5 - 3
supply-demand-engine-core/src/main/java/com/tzld/piaoquan/sde/service/strategy/ClusterStrategy.java

@@ -1,6 +1,7 @@
 package com.tzld.piaoquan.sde.service.strategy;
 
 import com.tzld.piaoquan.sde.common.enums.TaskTypeEnum;
+import com.tzld.piaoquan.sde.model.dto.StrategyResultDTO;
 import com.tzld.piaoquan.sde.model.entity.SdStrategy;
 import com.tzld.piaoquan.sde.model.entity.SdTask;
 import lombok.extern.slf4j.Slf4j;
@@ -21,11 +22,12 @@ public class ClusterStrategy implements DemandExtractionStrategy {
     }
 
     @Override
-    public void execute(SdTask task, SdStrategy strategy) {
-        log.info("执行聚类任务: taskId={}, taskNo={}", task.getId(), task.getTaskNo());
+    public StrategyResultDTO execute(SdTask task, SdStrategy strategy) {
+        log.info("Executing cluster task: taskId={}, taskNo={}", task.getId(), task.getTaskNo());
 
         // TODO: 实现聚类任务逻辑
 
-        log.info("聚类任务执行完成: taskId={}", task.getId());
+        log.info("Cluster task execution completed: taskId={}", task.getId());
+        return StrategyResultDTO.builder().build();
     }
 }

+ 37 - 14
supply-demand-engine-core/src/main/java/com/tzld/piaoquan/sde/service/strategy/DeconstructStrategy.java

@@ -1,5 +1,6 @@
 package com.tzld.piaoquan.sde.service.strategy;
 
+import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.tzld.piaoquan.sde.common.enums.IsDeleteEnum;
@@ -9,11 +10,14 @@ import com.tzld.piaoquan.sde.integration.OpenRouterClient;
 import com.tzld.piaoquan.sde.mapper.SdPromptTemplateMapper;
 import com.tzld.piaoquan.sde.mapper.SdSubTaskMapper;
 import com.tzld.piaoquan.sde.mapper.SdSubTaskResultItemMapper;
+import com.tzld.piaoquan.sde.model.dto.StrategyConfigDTO;
+import com.tzld.piaoquan.sde.model.dto.StrategyResultDTO;
 import com.tzld.piaoquan.sde.model.entity.*;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import java.io.IOException;
 import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
@@ -45,13 +49,16 @@ public class DeconstructStrategy implements DemandExtractionStrategy {
     }
 
     @Override
-    public void execute(SdTask task, SdStrategy strategy) {
-        log.info("执行解构任务: taskId={}, taskNo={}", task.getId(), task.getTaskNo());
+    public StrategyResultDTO execute(SdTask task, SdStrategy strategy) {
+        log.info("Executing deconstruct task: taskId={}, taskNo={}", task.getId(), task.getTaskNo());
         // 1. 获取Prompt模板
         SdPromptTemplate promptTemplate = sdPromptTemplateMapper.selectById(strategy.getPromptTemplateId());
         if (Objects.isNull(promptTemplate)) {
-            log.error("Prompt模板不存在: promptTemplateId={}", strategy.getPromptTemplateId());
-            return;
+            log.error("Prompt template not found: promptTemplateId={}", strategy.getPromptTemplateId());
+            return StrategyResultDTO.builder()
+                    .isSuccess(false)
+                    .msg("Prompt template not found")
+                    .build();
         }
 
         // 2. 查询成功的子任务
@@ -63,8 +70,11 @@ public class DeconstructStrategy implements DemandExtractionStrategy {
                 .select(SdSubTask::getId);
         List<SdSubTask> subTasks = sdSubTaskMapper.selectList(subTaskWrapper);
         if (Objects.isNull(subTasks) || subTasks.isEmpty()) {
-            log.warn("没有成功的子任务: taskId={}", task.getId());
-            return;
+            log.warn("No successful subtasks found: taskId={}", task.getId());
+            return StrategyResultDTO.builder()
+                    .isSuccess(false)
+                    .msg("No successful subtasks found")
+                    .build();
         }
 
         // 3. 查询子任务结果项
@@ -78,8 +88,11 @@ public class DeconstructStrategy implements DemandExtractionStrategy {
                 .select(SdSubTaskResultItem::getItemContent);
         List<SdSubTaskResultItem> resultItems = sdSubTaskResultItemMapper.selectList(resultItemWrapper);
         if (Objects.isNull(resultItems) || resultItems.isEmpty()) {
-            log.warn("没有子任务结果项: taskId=", task.getId());
-            return;
+            log.warn("No subtask result items found: taskId={}", task.getId());
+            return StrategyResultDTO.builder()
+                    .isSuccess(false)
+                    .msg("No subtask result items found")
+                    .build();
         }
 
         // 4. 构建输入内容
@@ -93,11 +106,21 @@ public class DeconstructStrategy implements DemandExtractionStrategy {
         String finalPrompt = prompt.replace("{{input}}", input);
 
         // 6. 调用AI服务 (TODO: 实现具体调用逻辑)
-        log.info("准备调用AI服务: taskId={}, promptLength={}", task.getId(), finalPrompt.length());
-        // String result = openRouterClient.chat(finalPrompt, strategy.getConfig());
-
-        // 7. 保存结果 (TODO: 实现结果保存逻辑)
-
-        log.info("解构任务执行完成: taskId={}", task.getId());
+        log.info("Preparing to call AI service: taskId={}, promptLength={}", task.getId(), finalPrompt.length());
+        StrategyConfigDTO strategyConfigDTO = JSON.parseObject(strategy.getConfig(), StrategyConfigDTO.class);
+        try {
+            String result = openRouterClient.chat(strategyConfigDTO.getModel(), finalPrompt);
+            return StrategyResultDTO.builder()
+                    .isSuccess(true)
+                    .result(result)
+                    .build();
+        } catch (IOException e) {
+            log.error("open router client error", e);
+        }
+        log.info("Deconstruct task execution completed: taskId={}", task.getId());
+        return StrategyResultDTO.builder()
+                .isSuccess(false)
+                .msg("execute Deconstruct strategy failure")
+                .build();
     }
 }

+ 2 - 2
supply-demand-engine-core/src/main/java/com/tzld/piaoquan/sde/service/strategy/DemandExtractionStrategy.java

@@ -1,6 +1,6 @@
 package com.tzld.piaoquan.sde.service.strategy;
 
-import com.tzld.piaoquan.sde.common.enums.TaskTypeEnum;
+import com.tzld.piaoquan.sde.model.dto.StrategyResultDTO;
 import com.tzld.piaoquan.sde.model.entity.SdStrategy;
 import com.tzld.piaoquan.sde.model.entity.SdTask;
 
@@ -19,5 +19,5 @@ public interface DemandExtractionStrategy {
     /**
      * 执行需求提取
      */
-    void execute(SdTask task, SdStrategy strategy);
+    StrategyResultDTO execute(SdTask task, SdStrategy strategy);
 }