package com.zy.ai.service.impl;
|
|
import com.alibaba.fastjson.JSONObject;
|
import com.zy.ai.entity.ChatCompletionRequest;
|
import com.zy.ai.service.LlmChatService;
|
import com.zy.ai.service.MainProcessPseudocodeService;
|
import com.zy.common.utils.RedisUtil;
|
import com.zy.core.News;
|
import com.zy.core.enums.RedisKeyType;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.stereotype.Service;
|
|
import java.nio.charset.StandardCharsets;
|
import java.nio.file.Files;
|
import java.nio.file.Path;
|
import java.nio.file.Paths;
|
import java.util.ArrayList;
|
import java.util.List;
|
|
@Service("mainProcessPseudocodeService")
|
public class MainProcessPseudocodeServiceImpl implements MainProcessPseudocodeService {
|
|
private static final long SUCCESS_CACHE_SECONDS = 60 * 60 * 24;
|
private static final long FAILURE_CACHE_SECONDS = 60 * 10;
|
private static final String FAILURE_TEXT = "AI生成伪代码失败";
|
private static final String PSEUDOCODE_SYSTEM_PROMPT = """
|
你现在是一名高级 Java 架构师兼伪代码转换专家,专门负责把复杂的 Java 代码转换成结构清晰、适合大模型阅读与推理的伪代码。
|
|
请严格遵守以下要求工作:
|
|
核心目标
|
|
输入是一段或多段 Java 代码。
|
|
输出是一段人类可读、逻辑清晰、尽量语言中立的伪代码。
|
|
这份伪代码将被用作后续大模型提问的“参考描述”,所以要:
|
|
保留关键业务逻辑和判断条件;
|
|
弱化语言细节(如具体库、注解、框架细节);
|
|
用自然语言 + 简洁流程结构,帮助大模型快速理解代码意图。
|
|
风格要求
|
|
使用中文描述逻辑,但可以保留少量关键英文标识(例如类名、方法名、状态枚举)以便跟代码对应。
|
|
伪代码要分层分块,尽量按:
|
|
类职责说明
|
|
重要字段 / 全局变量说明
|
|
每个公开方法 / 核心私有方法的伪代码
|
|
逻辑上使用类似:
|
|
如果 ... 则 ...
|
|
否则如果 ...
|
|
循环遍历列表 ...
|
|
调用服务/方法: ...
|
|
返回 ...
|
|
不追求严格语法,只追求易懂和准确。
|
|
保留信息 & 抽象信息
|
|
必须保留:
|
|
关键业务含义(例如“生成入库任务”、“检查堆垛机任务是否完成”)
|
|
关键条件判断(状态字段、枚举、重要配置开关)
|
|
重要数据流向(从哪里读数据、写到哪里、调用了哪些服务)
|
|
与外部系统交互(如 HTTP 调用 WMS、写 Redis 锁、写数据库)
|
|
可以抽象或省略:
|
|
日志打印的具体格式,只保留“记录日志:xxx”即可;
|
|
具体框架注解(如 @Component, @Autowired 等);
|
|
泛型、异常栈细节、工具类内部实现;
|
|
结构模板(优先遵循)
|
|
对于一段较大的 Java 类,请按以下结构输出伪代码:
|
|
类整体说明
|
|
简要说明这个类的用途和在系统中的角色。
|
|
重要字段 / 配置说明
|
|
列出关键静态变量 / 配置项 / 状态缓存,并用一行解释它们的含义。
|
|
主流程方法(例如 run())
|
|
用有序列表或伪代码,按调用顺序描述主要步骤。
|
|
每个核心私有方法
|
|
对于每个关键方法:
|
|
先用一行中文总结功能;
|
|
再给出伪代码流程(条件、循环、关键调用);
|
|
与外部系统交互的说明
|
|
单独强调有哪些地方调用了外部服务(HTTP、消息队列、数据库、Redis 等)。
|
|
输出格式要求
|
|
使用 Markdown 结构,方便复制给其他大模型:
|
|
用 ## 标题区分“类说明”、“主流程伪代码”、“方法伪代码”等部分;
|
|
伪代码块可以使用缩进和项目符号,或用 pseudo 代码块 包裹;
|
|
不要直接逐行翻译代码,而是做抽象和整理;
|
|
不要输出无关文本,例如道歉、寒暄或与任务无关的解释。
|
|
伪代码示例风格(示意)
|
|
例如当输入一个 run() 方法时,期望你的输出风格类似:
|
|
函数 run():
|
读取配置 enableFake, fakeRealTaskRequestWms
|
如果 enableFake == "Y":
|
调用 checkInStationHasTask() 检测入库站并生成仿真站点数据
|
如果 fakeRealTaskRequestWms == "N":
|
调用 generateFakeInTask() 生成本地仿真入库任务
|
调用 generateFakeOutTask() 生成本地仿真出库任务
|
计算所有站点的停留时间 calcAllStationStayTime()
|
检查出库站点是否超时并重置 checkOutStationStayTimeOut()
|
检查入库站点货物是否已被堆垛机取走 checkInStationCrnTake()
|
如果 fakeRealTaskRequestWms == "Y":
|
调用 generateStoreWrkFile() 请求 WMS 生成真实任务
|
调用 crnOperateUtils.crnIoExecute() 执行堆垛机任务
|
调用 crnIoExecuteFinish() 处理堆垛机任务完成后的状态更新和仿真站点生成
|
调用 stationOperateProcessUtils.stationInExecute() 执行输送站入库任务
|
调用 stationOperateProcessUtils.stationOutExecute() 执行输送站出库任务
|
调用 stationOperateProcessUtils.stationOutExecuteFinish() 检查输送站出库任务完成
|
|
对输入的要求
|
|
如果用户给出的是多段代码或只给出片段:
|
|
先推断这段代码的职责;
|
|
再按你能理解到的范围进行伪代码转换;
|
|
如果存在明显缺失的类/方法,只需在伪代码中用“调用 XXX(具体逻辑略)”标记即可。
|
|
请始终以「让后续大模型能看懂这段代码逻辑并基于伪代码进行推理和提问」为最高优先级来组织你的输出。
|
""";
|
|
@Value("${mainProcessPlugin}")
|
private String mainProcessPlugin;
|
|
@Autowired
|
private LlmChatService llmChatService;
|
@Autowired
|
private RedisUtil redisUtil;
|
|
@Override
|
public JSONObject queryMainProcessPseudocode(boolean refresh) {
|
String cacheKey = RedisKeyType.MAIN_PROCESS_PSEUDOCODE.key;
|
String cached = trimToNull(redisUtil.get(cacheKey));
|
String pseudocode = cached;
|
String status = "cached";
|
String message = null;
|
boolean generatedFresh = false;
|
|
if (refresh || pseudocode == null) {
|
String generated = generateAndCachePseudocode();
|
if (generated != null) {
|
pseudocode = generated;
|
generatedFresh = true;
|
status = refresh ? "refreshed" : "generated";
|
} else if (cached != null) {
|
pseudocode = cached;
|
status = "fallback_cached";
|
message = "重新生成失败,返回缓存中的伪代码";
|
} else {
|
pseudocode = FAILURE_TEXT;
|
status = "failed";
|
message = FAILURE_TEXT;
|
redisUtil.set(cacheKey, FAILURE_TEXT, FAILURE_CACHE_SECONDS);
|
News.info(FAILURE_TEXT);
|
}
|
}
|
|
JSONObject result = new JSONObject();
|
result.put("mainProcessPlugin", resolvePlugin());
|
result.put("mainProcessPluginClass", resolvePluginClassName());
|
result.put("refreshRequested", refresh);
|
result.put("generatedFresh", generatedFresh);
|
result.put("cacheHit", !generatedFresh && ("cached".equals(status) || "fallback_cached".equals(status)));
|
result.put("status", status);
|
result.put("expireSeconds", redisUtil.getExpire(cacheKey));
|
result.put("message", message);
|
result.put("pseudocode", pseudocode);
|
return result;
|
}
|
|
private String generateAndCachePseudocode() {
|
String code = loadSourceBundle();
|
if (code == null || code.isEmpty()) {
|
return null;
|
}
|
|
List<ChatCompletionRequest.Message> messages = new ArrayList<>();
|
ChatCompletionRequest.Message system = new ChatCompletionRequest.Message();
|
system.setRole("system");
|
system.setContent(PSEUDOCODE_SYSTEM_PROMPT);
|
messages.add(system);
|
|
ChatCompletionRequest.Message user = new ChatCompletionRequest.Message();
|
user.setRole("user");
|
user.setContent("主流程插件类源代码:\n\n" + code);
|
messages.add(user);
|
|
try {
|
String result = trimToNull(llmChatService.chat(messages, 0.2, 2048));
|
if (result == null) {
|
return null;
|
}
|
redisUtil.set(RedisKeyType.MAIN_PROCESS_PSEUDOCODE.key, result, SUCCESS_CACHE_SECONDS);
|
News.info("AI生成伪代码成功");
|
return result;
|
} catch (Exception ignore) {
|
return null;
|
}
|
}
|
|
private String loadSourceBundle() {
|
StringBuilder code = new StringBuilder();
|
appendSource(code, resolvePluginClassName());
|
appendSource(code, "com.zy.core.utils.CrnOperateProcessUtils");
|
appendSource(code, "com.zy.core.utils.StationOperateProcessUtils");
|
return code.toString();
|
}
|
|
private void appendSource(StringBuilder code, String className) {
|
if (className == null || className.trim().isEmpty()) {
|
return;
|
}
|
String source = readJavaSource(className);
|
if (source == null || source.isEmpty()) {
|
return;
|
}
|
if (code.length() > 0) {
|
code.append("\n\n");
|
}
|
code.append("// ===== ").append(className).append(" =====\n");
|
code.append(source);
|
}
|
|
private String readJavaSource(String className) {
|
try {
|
String rel = className.replace('.', '/') + ".java";
|
Path path = Paths.get(System.getProperty("user.dir"), "src", "main", "java", rel);
|
if (!Files.exists(path)) {
|
return null;
|
}
|
return Files.readString(path, StandardCharsets.UTF_8);
|
} catch (Exception ignore) {
|
return null;
|
}
|
}
|
|
private String resolvePlugin() {
|
String plugin = trimToNull(mainProcessPlugin);
|
return plugin == null ? "NormalProcess" : plugin;
|
}
|
|
private String resolvePluginClassName() {
|
String plugin = resolvePlugin();
|
if (plugin.contains(".")) {
|
return plugin;
|
}
|
return "com.zy.core.plugin." + plugin;
|
}
|
|
private String trimToNull(Object value) {
|
if (value == null) {
|
return null;
|
}
|
String text = String.valueOf(value).trim();
|
return text.isEmpty() ? null : text;
|
}
|
}
|