refactor(ai): 重构AI命令服务实现类
- 重命名变量名 log 为 commandLog 以提高代码可读性
This commit is contained in:
@@ -32,17 +32,17 @@ public class AiCommandLogServiceImpl extends ServiceImpl<AiCommandLogMapper, AiC
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void rollbackCommand(String logId) {
|
public void rollbackCommand(String logId) {
|
||||||
AiCommandLog log = this.getById(logId);
|
AiCommandLog commandLog = this.getById(logId);
|
||||||
if (log == null) {
|
if (commandLog == null) {
|
||||||
throw new RuntimeException("命令记录不存在");
|
throw new RuntimeException("命令记录不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log.getExecuteStatus() == null || log.getExecuteStatus() != 1) {
|
if (commandLog.getExecuteStatus() == null || commandLog.getExecuteStatus() != 1) {
|
||||||
throw new RuntimeException("只能撤销成功执行的命令");
|
throw new RuntimeException("只能撤销成功执行的命令");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 实现具体的回滚逻辑
|
// TODO: 实现具体的回滚逻辑
|
||||||
log.info("撤销命令执行: logId={}, function={}", logId, log.getFunctionName());
|
log.info("撤销命令执行: logId={}, function={}", logId, commandLog.getFunctionName());
|
||||||
throw new UnsupportedOperationException("回滚功能尚未实现");
|
throw new UnsupportedOperationException("回滚功能尚未实现");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,288 +37,288 @@ import java.util.Optional;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class AiCommandServiceImpl implements AiCommandService {
|
public class AiCommandServiceImpl implements AiCommandService {
|
||||||
|
|
||||||
private static final String SYSTEM_PROMPT = """
|
private static final String SYSTEM_PROMPT = """
|
||||||
你是一个智能的企业操作助手,需要将用户的自然语言命令解析成标准的函数调用。
|
你是一个智能的企业操作助手,需要将用户的自然语言命令解析成标准的函数调用。
|
||||||
请返回严格的 JSON 格式,包含字段:
|
请返回严格的 JSON 格式,包含字段:
|
||||||
- success: boolean
|
- success: boolean
|
||||||
- explanation: string
|
- explanation: string
|
||||||
- confidence: number (0-1)
|
- confidence: number (0-1)
|
||||||
- error: string
|
- error: string
|
||||||
- provider: string
|
- provider: string
|
||||||
- model: string
|
- model: string
|
||||||
- functionCalls: 数组,每个元素包含 name、description、arguments(对象)
|
- functionCalls: 数组,每个元素包含 name、description、arguments(对象)
|
||||||
当无法识别命令时,success=false,并给出 error。
|
当无法识别命令时,success=false,并给出 error。
|
||||||
""";
|
""";
|
||||||
|
|
||||||
private final AiCommandLogService logService;
|
private final AiCommandLogService logService;
|
||||||
private final UserTools userTools;
|
private final UserTools userTools;
|
||||||
private final ChatClient chatClient;
|
private final ChatClient chatClient;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AiParseResponseDTO parseCommand(AiParseRequestDTO request, HttpServletRequest httpRequest) {
|
public AiParseResponseDTO parseCommand(AiParseRequestDTO request, HttpServletRequest httpRequest) {
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
String command = Optional.ofNullable(request.getCommand()).orElse("").trim();
|
String command = Optional.ofNullable(request.getCommand()).orElse("").trim();
|
||||||
|
|
||||||
if (StrUtil.isBlank(command)) {
|
if (StrUtil.isBlank(command)) {
|
||||||
return AiParseResponseDTO.builder()
|
return AiParseResponseDTO.builder()
|
||||||
.success(false)
|
.success(false)
|
||||||
.error("命令不能为空")
|
.error("命令不能为空")
|
||||||
.functionCalls(Collections.emptyList())
|
.functionCalls(Collections.emptyList())
|
||||||
.build();
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
Long userId = SecurityUtils.getUserId();
|
||||||
|
String username = SecurityUtils.getUsername();
|
||||||
|
String ipAddress = JakartaServletUtil.getClientIP(httpRequest);
|
||||||
|
|
||||||
|
AiCommandLog commandLog = new AiCommandLog();
|
||||||
|
commandLog.setUserId(userId);
|
||||||
|
commandLog.setUsername(username);
|
||||||
|
commandLog.setOriginalCommand(command);
|
||||||
|
commandLog.setIpAddress(ipAddress);
|
||||||
|
commandLog.setAiProvider("spring-ai");
|
||||||
|
commandLog.setAiModel("auto");
|
||||||
|
|
||||||
|
String systemPrompt = buildSystemPrompt();
|
||||||
|
String userPrompt = buildUserPrompt(request);
|
||||||
|
|
||||||
|
try {
|
||||||
|
log.info("📤 发送命令至 AI 模型: {}", command);
|
||||||
|
ChatResponse chatResponse = chatClient.prompt()
|
||||||
|
.system(systemPrompt)
|
||||||
|
.user(userPrompt)
|
||||||
|
.call().chatResponse();
|
||||||
|
|
||||||
|
String rawContent = Optional.ofNullable(chatResponse.getResult())
|
||||||
|
.map(result -> result.getOutput().getText())
|
||||||
|
.orElse("");
|
||||||
|
|
||||||
|
ParseResult parseResult = parseAiResponse(rawContent);
|
||||||
|
|
||||||
|
commandLog.setAiProvider(StrUtil.emptyToDefault(parseResult.provider(), "spring-ai"));
|
||||||
|
commandLog.setAiModel(StrUtil.emptyToDefault(parseResult.model(), "auto"));
|
||||||
|
commandLog.setParseStatus(parseResult.success() ? 1 : 0);
|
||||||
|
commandLog.setExplanation(parseResult.explanation());
|
||||||
|
commandLog.setFunctionCalls(JSONUtil.toJsonStr(parseResult.functionCalls()));
|
||||||
|
commandLog.setConfidence(parseResult.confidence() != null ? BigDecimal.valueOf(parseResult.confidence()) : null);
|
||||||
|
commandLog.setParseErrorMessage(parseResult.success() ? null : StrUtil.emptyToDefault(parseResult.error(), "解析失败"));
|
||||||
|
long duration = System.currentTimeMillis() - startTime;
|
||||||
|
commandLog.setParseDurationMs((int) duration);
|
||||||
|
|
||||||
|
logService.save(commandLog);
|
||||||
|
|
||||||
|
AiParseResponseDTO response = AiParseResponseDTO.builder()
|
||||||
|
.parseLogId(commandLog.getId())
|
||||||
|
.success(parseResult.success())
|
||||||
|
.functionCalls(parseResult.functionCalls())
|
||||||
|
.explanation(parseResult.explanation())
|
||||||
|
.confidence(parseResult.confidence())
|
||||||
|
.error(parseResult.error())
|
||||||
|
.rawResponse(rawContent)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
if (!parseResult.success()) {
|
||||||
|
log.warn("❗️ AI 未能解析命令: {}", parseResult.error());
|
||||||
|
} else {
|
||||||
|
log.info("✅ 解析成功,审计记录ID: {}", commandLog.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (Exception e) {
|
||||||
|
long duration = System.currentTimeMillis() - startTime;
|
||||||
|
commandLog.setParseStatus(0);
|
||||||
|
commandLog.setFunctionCalls(JSONUtil.toJsonStr(Collections.emptyList()));
|
||||||
|
commandLog.setParseErrorMessage(e.getMessage());
|
||||||
|
commandLog.setParseDurationMs((int) duration);
|
||||||
|
logService.save(commandLog);
|
||||||
|
|
||||||
|
log.error("❌ 解析命令失败: {}", e.getMessage(), e);
|
||||||
|
throw new RuntimeException("解析命令失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Long userId = SecurityUtils.getUserId();
|
private String buildSystemPrompt() {
|
||||||
String username = SecurityUtils.getUsername();
|
return SYSTEM_PROMPT;
|
||||||
String ipAddress = JakartaServletUtil.getClientIP(httpRequest);
|
|
||||||
|
|
||||||
AiCommandLog log = new AiCommandLog();
|
|
||||||
log.setUserId(userId);
|
|
||||||
log.setUsername(username);
|
|
||||||
log.setOriginalCommand(command);
|
|
||||||
log.setIpAddress(ipAddress);
|
|
||||||
log.setAiProvider("spring-ai");
|
|
||||||
log.setAiModel("auto");
|
|
||||||
|
|
||||||
String systemPrompt = buildSystemPrompt();
|
|
||||||
String userPrompt = buildUserPrompt(request);
|
|
||||||
|
|
||||||
try {
|
|
||||||
log.info("📤 发送命令至 AI 模型: {}", command);
|
|
||||||
ChatResponse chatResponse = chatClient.prompt()
|
|
||||||
.system(systemPrompt)
|
|
||||||
.user(userPrompt)
|
|
||||||
.call().chatResponse();
|
|
||||||
|
|
||||||
String rawContent = Optional.ofNullable(chatResponse.getResult())
|
|
||||||
.map(result -> result.getOutput().getText())
|
|
||||||
.orElse("");
|
|
||||||
|
|
||||||
ParseResult parseResult = parseAiResponse(rawContent);
|
|
||||||
|
|
||||||
log.setAiProvider(StrUtil.emptyToDefault(parseResult.provider(), "spring-ai"));
|
|
||||||
log.setAiModel(StrUtil.emptyToDefault(parseResult.model(), "auto"));
|
|
||||||
log.setParseStatus(parseResult.success() ? 1 : 0);
|
|
||||||
log.setExplanation(parseResult.explanation());
|
|
||||||
log.setFunctionCalls(JSONUtil.toJsonStr(parseResult.functionCalls()));
|
|
||||||
log.setConfidence(parseResult.confidence() != null ? BigDecimal.valueOf(parseResult.confidence()) : null);
|
|
||||||
log.setParseErrorMessage(parseResult.success() ? null : StrUtil.emptyToDefault(parseResult.error(), "解析失败"));
|
|
||||||
long duration = System.currentTimeMillis() - startTime;
|
|
||||||
log.setParseDurationMs((int) duration);
|
|
||||||
|
|
||||||
logService.save(log);
|
|
||||||
|
|
||||||
AiParseResponseDTO response = AiParseResponseDTO.builder()
|
|
||||||
.parseLogId(log.getId())
|
|
||||||
.success(parseResult.success())
|
|
||||||
.functionCalls(parseResult.functionCalls())
|
|
||||||
.explanation(parseResult.explanation())
|
|
||||||
.confidence(parseResult.confidence())
|
|
||||||
.error(parseResult.error())
|
|
||||||
.rawResponse(rawContent)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
if (!parseResult.success()) {
|
|
||||||
log.warn("❗️ AI 未能解析命令: {}", parseResult.error());
|
|
||||||
} else {
|
|
||||||
log.info("✅ 解析成功,审计记录ID: {}", log.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
} catch (Exception e) {
|
|
||||||
long duration = System.currentTimeMillis() - startTime;
|
|
||||||
log.setParseStatus(0);
|
|
||||||
log.setFunctionCalls(JSONUtil.toJsonStr(Collections.emptyList()));
|
|
||||||
log.setParseErrorMessage(e.getMessage());
|
|
||||||
log.setParseDurationMs((int) duration);
|
|
||||||
logService.save(log);
|
|
||||||
|
|
||||||
log.error("❌ 解析命令失败: {}", e.getMessage(), e);
|
|
||||||
throw new RuntimeException("解析命令失败: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildSystemPrompt() {
|
|
||||||
return SYSTEM_PROMPT;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildUserPrompt(AiParseRequestDTO request) {
|
|
||||||
JSONObject payload = JSONUtil.createObj()
|
|
||||||
.set("command", request.getCommand())
|
|
||||||
.set("currentRoute", request.getCurrentRoute())
|
|
||||||
.set("currentComponent", request.getCurrentComponent())
|
|
||||||
.set("context", Optional.ofNullable(request.getContext()).orElse(Collections.emptyMap()))
|
|
||||||
.set("availableFunctions", availableFunctions());
|
|
||||||
|
|
||||||
return StrUtil.format("""
|
|
||||||
请根据以下上下文识别用户意图,并输出符合系统提示要求的 JSON:
|
|
||||||
{}
|
|
||||||
""", JSONUtil.toJsonPrettyStr(payload));
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Map<String, Object>> availableFunctions() {
|
|
||||||
return List.of(
|
|
||||||
Map.of(
|
|
||||||
"name", "updateUserNickname",
|
|
||||||
"description", "根据用户名更新用户昵称",
|
|
||||||
"requiredParameters", List.of("username", "nickname")
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ParseResult parseAiResponse(String rawContent) {
|
|
||||||
if (StrUtil.isBlank(rawContent)) {
|
|
||||||
throw new IllegalStateException("AI 返回内容为空");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
private String buildUserPrompt(AiParseRequestDTO request) {
|
||||||
JSONObject jsonObject = JSONUtil.parseObj(rawContent);
|
JSONObject payload = JSONUtil.createObj()
|
||||||
boolean success = jsonObject.getBool("success", false);
|
.set("command", request.getCommand())
|
||||||
String explanation = jsonObject.getStr("explanation");
|
.set("currentRoute", request.getCurrentRoute())
|
||||||
Double confidence = jsonObject.containsKey("confidence") ? jsonObject.getDouble("confidence") : null;
|
.set("currentComponent", request.getCurrentComponent())
|
||||||
String error = jsonObject.getStr("error");
|
.set("context", Optional.ofNullable(request.getContext()).orElse(Collections.emptyMap()))
|
||||||
String provider = jsonObject.getStr("provider");
|
.set("availableFunctions", availableFunctions());
|
||||||
String model = jsonObject.getStr("model");
|
|
||||||
|
|
||||||
List<AiFunctionCallDTO> functionCalls = toFunctionCallList(jsonObject.getJSONArray("functionCalls"));
|
return StrUtil.format("""
|
||||||
|
请根据以下上下文识别用户意图,并输出符合系统提示要求的 JSON:
|
||||||
return new ParseResult(success, explanation, confidence, error, provider, model, functionCalls);
|
{}
|
||||||
} catch (Exception ex) {
|
""", JSONUtil.toJsonPrettyStr(payload));
|
||||||
throw new IllegalStateException("无法解析 AI 响应: " + ex.getMessage(), ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<AiFunctionCallDTO> toFunctionCallList(JSONArray array) {
|
|
||||||
if (array == null || array.isEmpty()) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<AiFunctionCallDTO> result = new ArrayList<>();
|
private List<Map<String, Object>> availableFunctions() {
|
||||||
for (Object element : array) {
|
return List.of(
|
||||||
JSONObject functionJson = JSONUtil.parseObj(element);
|
Map.of(
|
||||||
Map<String, Object> arguments = Optional.ofNullable(functionJson.getJSONObject("arguments"))
|
"name", "updateUserNickname",
|
||||||
.map(obj -> obj.toBean(new TypeReference<Map<String, Object>>() {
|
"description", "根据用户名更新用户昵称",
|
||||||
}))
|
"requiredParameters", List.of("username", "nickname")
|
||||||
.orElse(Collections.emptyMap());
|
)
|
||||||
|
);
|
||||||
result.add(AiFunctionCallDTO.builder()
|
|
||||||
.name(functionJson.getStr("name"))
|
|
||||||
.description(functionJson.getStr("description"))
|
|
||||||
.arguments(arguments)
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private record ParseResult(
|
|
||||||
boolean success,
|
|
||||||
String explanation,
|
|
||||||
Double confidence,
|
|
||||||
String error,
|
|
||||||
String provider,
|
|
||||||
String model,
|
|
||||||
List<AiFunctionCallDTO> functionCalls
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object executeCommand(AiExecuteRequestDTO request, HttpServletRequest httpRequest) throws Exception {
|
|
||||||
long startTime = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// 获取用户信息
|
|
||||||
Long userId = SecurityUtils.getUserId();
|
|
||||||
String username = SecurityUtils.getUsername();
|
|
||||||
String ipAddress = JakartaServletUtil.getClientIP(httpRequest);
|
|
||||||
|
|
||||||
AiFunctionCallDTO functionCall = request.getFunctionCall();
|
|
||||||
|
|
||||||
// 根据解析日志ID获取审计记录,如果不存在则创建新记录
|
|
||||||
AiCommandLog log;
|
|
||||||
if (StrUtil.isNotBlank(request.getParseLogId())) {
|
|
||||||
// 更新已存在的审计记录(解析阶段已创建)
|
|
||||||
log = logService.getById(request.getParseLogId());
|
|
||||||
if (log == null) {
|
|
||||||
throw new IllegalStateException("未找到对应的解析记录,ID: " + request.getParseLogId());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 如果没有解析日志ID,创建新记录(兼容直接执行的情况)
|
|
||||||
log = new AiCommandLog();
|
|
||||||
log.setUserId(userId);
|
|
||||||
log.setUsername(username);
|
|
||||||
log.setOriginalCommand(request.getOriginalCommand());
|
|
||||||
log.setIpAddress(ipAddress);
|
|
||||||
logService.save(log);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新执行相关字段
|
private ParseResult parseAiResponse(String rawContent) {
|
||||||
log.setFunctionName(functionCall.getName());
|
if (StrUtil.isBlank(rawContent)) {
|
||||||
log.setFunctionArguments(JSONUtil.toJsonStr(functionCall.getArguments()));
|
throw new IllegalStateException("AI 返回内容为空");
|
||||||
log.setExecuteStatus(0); // 0-待执行
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 🎯 执行具体的函数调用
|
JSONObject jsonObject = JSONUtil.parseObj(rawContent);
|
||||||
Object result = executeFunctionCall(functionCall);
|
boolean success = jsonObject.getBool("success", false);
|
||||||
|
String explanation = jsonObject.getStr("explanation");
|
||||||
|
Double confidence = jsonObject.containsKey("confidence") ? jsonObject.getDouble("confidence") : null;
|
||||||
|
String error = jsonObject.getStr("error");
|
||||||
|
String provider = jsonObject.getStr("provider");
|
||||||
|
String model = jsonObject.getStr("model");
|
||||||
|
|
||||||
// 更新执行成功
|
List<AiFunctionCallDTO> functionCalls = toFunctionCallList(jsonObject.getJSONArray("functionCalls"));
|
||||||
log.setExecuteStatus(1); // 1-成功
|
|
||||||
log.setExecuteErrorMessage(null);
|
|
||||||
|
|
||||||
// 更新审计记录
|
return new ParseResult(success, explanation, confidence, error, provider, model, functionCalls);
|
||||||
logService.updateById(log);
|
} catch (Exception ex) {
|
||||||
|
throw new IllegalStateException("无法解析 AI 响应: " + ex.getMessage(), ex);
|
||||||
log.info("✅ 命令执行成功,审计记录ID: {}", log.getId());
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
// 更新执行失败
|
|
||||||
log.setExecuteStatus(-1); // -1-失败
|
|
||||||
log.setExecuteErrorMessage(e.getMessage());
|
|
||||||
|
|
||||||
// 更新审计记录
|
|
||||||
logService.updateById(log);
|
|
||||||
|
|
||||||
log.error("❌ 命令执行失败,审计记录ID: {}", log.getId(), e);
|
|
||||||
|
|
||||||
// 抛出异常,由 Controller 统一处理
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行具体的函数调用
|
|
||||||
*/
|
|
||||||
private Object executeFunctionCall(AiFunctionCallDTO functionCall) {
|
|
||||||
String functionName = functionCall.getName();
|
|
||||||
Map<String, Object> arguments = functionCall.getArguments();
|
|
||||||
|
|
||||||
log.info("🎯 执行函数: {}, 参数: {}", functionName, arguments);
|
|
||||||
|
|
||||||
// 根据函数名称路由到不同的处理器
|
|
||||||
switch (functionName) {
|
|
||||||
case "updateUserNickname":
|
|
||||||
return executeUpdateUserNickname(arguments);
|
|
||||||
default:
|
|
||||||
throw new UnsupportedOperationException("不支持的函数: " + functionName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用 Tool: 根据用户名更新用户昵称
|
|
||||||
*/
|
|
||||||
private Object executeUpdateUserNickname(Map<String, Object> arguments) {
|
|
||||||
String username = (String) arguments.get("username");
|
|
||||||
String nickname = (String) arguments.get("nickname");
|
|
||||||
|
|
||||||
log.info("🔧 [Tool] 更新用户昵称: username={}, nickname={}", username, nickname);
|
|
||||||
String resultMsg = userTools.updateUserNickname(username, nickname);
|
|
||||||
|
|
||||||
boolean success = resultMsg != null && resultMsg.contains("成功");
|
|
||||||
if (!success) {
|
|
||||||
throw new RuntimeException(resultMsg != null ? resultMsg : "更新用户昵称失败");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Map.of("username", username, "nickname", nickname, "message", resultMsg);
|
private List<AiFunctionCallDTO> toFunctionCallList(JSONArray array) {
|
||||||
}
|
if (array == null || array.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<AiFunctionCallDTO> result = new ArrayList<>();
|
||||||
|
for (Object element : array) {
|
||||||
|
JSONObject functionJson = JSONUtil.parseObj(element);
|
||||||
|
Map<String, Object> arguments = Optional.ofNullable(functionJson.getJSONObject("arguments"))
|
||||||
|
.map(obj -> obj.toBean(new TypeReference<Map<String, Object>>() {
|
||||||
|
}))
|
||||||
|
.orElse(Collections.emptyMap());
|
||||||
|
|
||||||
|
result.add(AiFunctionCallDTO.builder()
|
||||||
|
.name(functionJson.getStr("name"))
|
||||||
|
.description(functionJson.getStr("description"))
|
||||||
|
.arguments(arguments)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private record ParseResult(
|
||||||
|
boolean success,
|
||||||
|
String explanation,
|
||||||
|
Double confidence,
|
||||||
|
String error,
|
||||||
|
String provider,
|
||||||
|
String model,
|
||||||
|
List<AiFunctionCallDTO> functionCalls
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object executeCommand(AiExecuteRequestDTO request, HttpServletRequest httpRequest) throws Exception {
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
Long userId = SecurityUtils.getUserId();
|
||||||
|
String username = SecurityUtils.getUsername();
|
||||||
|
String ipAddress = JakartaServletUtil.getClientIP(httpRequest);
|
||||||
|
|
||||||
|
AiFunctionCallDTO functionCall = request.getFunctionCall();
|
||||||
|
|
||||||
|
// 根据解析日志ID获取审计记录,如果不存在则创建新记录
|
||||||
|
AiCommandLog commandLog;
|
||||||
|
if (StrUtil.isNotBlank(request.getParseLogId())) {
|
||||||
|
// 更新已存在的审计记录(解析阶段已创建)
|
||||||
|
commandLog = logService.getById(request.getParseLogId());
|
||||||
|
if (commandLog == null) {
|
||||||
|
throw new IllegalStateException("未找到对应的解析记录,ID: " + request.getParseLogId());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果没有解析日志ID,创建新记录(兼容直接执行的情况)
|
||||||
|
commandLog = new AiCommandLog();
|
||||||
|
commandLog.setUserId(userId);
|
||||||
|
commandLog.setUsername(username);
|
||||||
|
commandLog.setOriginalCommand(request.getOriginalCommand());
|
||||||
|
commandLog.setIpAddress(ipAddress);
|
||||||
|
logService.save(commandLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新执行相关字段
|
||||||
|
commandLog.setFunctionName(functionCall.getName());
|
||||||
|
commandLog.setFunctionArguments(JSONUtil.toJsonStr(functionCall.getArguments()));
|
||||||
|
commandLog.setExecuteStatus(0); // 0-待执行
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 🎯 执行具体的函数调用
|
||||||
|
Object result = executeFunctionCall(functionCall);
|
||||||
|
|
||||||
|
// 更新执行成功
|
||||||
|
commandLog.setExecuteStatus(1); // 1-成功
|
||||||
|
commandLog.setExecuteErrorMessage(null);
|
||||||
|
|
||||||
|
// 更新审计记录
|
||||||
|
logService.updateById(commandLog);
|
||||||
|
|
||||||
|
log.info("✅ 命令执行成功,审计记录ID: {}", commandLog.getId());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 更新执行失败
|
||||||
|
commandLog.setExecuteStatus(-1); // -1-失败
|
||||||
|
commandLog.setExecuteErrorMessage(e.getMessage());
|
||||||
|
|
||||||
|
// 更新审计记录
|
||||||
|
logService.updateById(commandLog);
|
||||||
|
|
||||||
|
log.error("❌ 命令执行失败,审计记录ID: {}", commandLog.getId(), e);
|
||||||
|
|
||||||
|
// 抛出异常,由 Controller 统一处理
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行具体的函数调用
|
||||||
|
*/
|
||||||
|
private Object executeFunctionCall(AiFunctionCallDTO functionCall) {
|
||||||
|
String functionName = functionCall.getName();
|
||||||
|
Map<String, Object> arguments = functionCall.getArguments();
|
||||||
|
|
||||||
|
log.info("🎯 执行函数: {}, 参数: {}", functionName, arguments);
|
||||||
|
|
||||||
|
// 根据函数名称路由到不同的处理器
|
||||||
|
switch (functionName) {
|
||||||
|
case "updateUserNickname":
|
||||||
|
return executeUpdateUserNickname(arguments);
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException("不支持的函数: " + functionName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用 Tool: 根据用户名更新用户昵称
|
||||||
|
*/
|
||||||
|
private Object executeUpdateUserNickname(Map<String, Object> arguments) {
|
||||||
|
String username = (String) arguments.get("username");
|
||||||
|
String nickname = (String) arguments.get("nickname");
|
||||||
|
|
||||||
|
log.info("🔧 [Tool] 更新用户昵称: username={}, nickname={}", username, nickname);
|
||||||
|
String resultMsg = userTools.updateUserNickname(username, nickname);
|
||||||
|
|
||||||
|
boolean success = resultMsg != null && resultMsg.contains("成功");
|
||||||
|
if (!success) {
|
||||||
|
throw new RuntimeException(resultMsg != null ? resultMsg : "更新用户昵称失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Map.of("username", username, "nickname", nickname, "message", resultMsg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user