Merge branch 'develop-business-css' of http://121.37.111.42:3000/ThbTech/JavaProjectRepo into develop-business-css
This commit is contained in:
commit
66a0c83554
@ -134,6 +134,79 @@
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- frontend-maven-plugin 插件:用于在 Maven 构建时自动编译前端并拷贝到 static 目录 -->
|
||||
<plugin>
|
||||
<groupId>com.github.eirslett</groupId>
|
||||
<artifactId>frontend-maven-plugin</artifactId>
|
||||
<version>1.15.0</version>
|
||||
<!-- 前端代码所在的目录 -->
|
||||
<configuration>
|
||||
<workingDirectory>frontend</workingDirectory>
|
||||
</configuration>
|
||||
<executions>
|
||||
<!-- 1. 安装 Node 和 npm -->
|
||||
<execution>
|
||||
<id>install node and npm</id>
|
||||
<goals>
|
||||
<goal>install-node-and-npm</goal>
|
||||
</goals>
|
||||
<phase>generate-resources</phase>
|
||||
<configuration>
|
||||
<!-- 可以根据您机器上的实际 node 版本修改 -->
|
||||
<nodeVersion>v18.17.0</nodeVersion>
|
||||
<npmVersion>9.6.7</npmVersion>
|
||||
</configuration>
|
||||
</execution>
|
||||
<!-- 2. 安装依赖 (npm install) -->
|
||||
<execution>
|
||||
<id>npm install</id>
|
||||
<goals>
|
||||
<goal>npm</goal>
|
||||
</goals>
|
||||
<phase>generate-resources</phase>
|
||||
<configuration>
|
||||
<arguments>install</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
<!-- 3. 执行前端构建命令 (npm run build:mvn) -->
|
||||
<execution>
|
||||
<id>npm run build</id>
|
||||
<goals>
|
||||
<goal>npm</goal>
|
||||
</goals>
|
||||
<phase>generate-resources</phase>
|
||||
<configuration>
|
||||
<arguments>run build:mvn</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- maven-resources-plugin:将前端构建的 dist 目录内容拷贝到 target/classes/static -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-frontend-dist</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.outputDirectory}/static</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>frontend/dist</directory>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
package com.yfd.business.css.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
|
||||
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSocketMessageBroker
|
||||
public class WebSocketBrokerConfig implements WebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
// 前端连接的端点,支持跨域
|
||||
registry.addEndpoint("/ws/train")
|
||||
.setAllowedOriginPatterns("*")
|
||||
.withSockJS();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureMessageBroker(MessageBrokerRegistry registry) {
|
||||
// 客户端订阅的路径前缀
|
||||
registry.enableSimpleBroker("/topic");
|
||||
// 客户端发送消息的路径前缀
|
||||
registry.setApplicationDestinationPrefixes("/app");
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.yfd.business.css.domain.ModelTrainTask;
|
||||
import com.yfd.business.css.service.ModelTrainService;
|
||||
import com.yfd.business.css.service.TrainWebSocketService;
|
||||
import com.yfd.platform.config.ResponseResult;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@ -21,9 +22,34 @@ public class ModelTrainController {
|
||||
@Autowired
|
||||
private ModelTrainService modelTrainService;
|
||||
|
||||
@Autowired
|
||||
private TrainWebSocketService trainWebSocketService;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* 接收 Python 端的训练状态回调
|
||||
*/
|
||||
@PostMapping("/internal/callback")
|
||||
public ResponseResult handleTrainCallback(@RequestBody Map<String, Object> callbackData) {
|
||||
System.out.println("====== 收到 Python 端训练回调 ======");
|
||||
System.out.println("回调数据: " + callbackData);
|
||||
|
||||
String taskId = (String) callbackData.get("taskId");
|
||||
if (taskId == null) {
|
||||
return ResponseResult.error("taskId不能为空");
|
||||
}
|
||||
|
||||
// 1. 更新数据库任务状态
|
||||
modelTrainService.updateTaskStatusFromCallback(taskId, callbackData);
|
||||
|
||||
// 2. 触发 WebSocket 推送给前端
|
||||
trainWebSocketService.sendTrainStatus(taskId, callbackData);
|
||||
|
||||
return ResponseResult.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传数据集
|
||||
*/
|
||||
|
||||
@ -26,6 +26,13 @@ public interface ModelTrainService extends IService<ModelTrainTask> {
|
||||
*/
|
||||
ModelTrainTask syncTaskStatus(String taskId);
|
||||
|
||||
/**
|
||||
* 从回调中更新任务状态
|
||||
* @param taskId 任务ID
|
||||
* @param callbackData 回调数据
|
||||
*/
|
||||
void updateTaskStatusFromCallback(String taskId, java.util.Map<String, Object> callbackData);
|
||||
|
||||
/**
|
||||
* 发布模型
|
||||
* @param taskId 任务ID
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
package com.yfd.business.css.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class TrainWebSocketService {
|
||||
|
||||
@Autowired
|
||||
private SimpMessagingTemplate messagingTemplate;
|
||||
|
||||
/**
|
||||
* 向前端推送模型训练状态
|
||||
*
|
||||
* @param taskId 任务ID
|
||||
* @param data 状态数据
|
||||
*/
|
||||
public void sendTrainStatus(String taskId, Map<String, Object> data) {
|
||||
// 1. 细粒度推送(供详情页使用)
|
||||
String specificDestination = "/topic/train-status/" + taskId;
|
||||
messagingTemplate.convertAndSend(specificDestination, data);
|
||||
|
||||
// 2. 全局广播推送(供列表页使用)
|
||||
String globalDestination = "/topic/train-status/all";
|
||||
log.info("全局广播训练状态到 {}, 数据: {}", globalDestination, data);
|
||||
messagingTemplate.convertAndSend(globalDestination, data);
|
||||
}
|
||||
}
|
||||
@ -206,92 +206,67 @@ public class ModelTrainServiceImpl extends ServiceImpl<ModelTrainTaskMapper, Mod
|
||||
if (task == null) {
|
||||
throw new BizException("任务不存在");
|
||||
}
|
||||
|
||||
// 只有在 Training 或 Pending 状态才去查询 Python 服务
|
||||
if ("Training".equalsIgnoreCase(task.getStatus()) || "Pending".equalsIgnoreCase(task.getStatus())) {
|
||||
try {
|
||||
String url = pythonApiUrl + "/v1/train/status/" + taskId;
|
||||
ResponseEntity<Map> response = restTemplate.getForEntity(url, Map.class);
|
||||
|
||||
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
|
||||
Map<String, Object> body = response.getBody();
|
||||
Object codeObj = body.get("code");
|
||||
Integer code = null;
|
||||
if (codeObj instanceof Number) {
|
||||
code = ((Number) codeObj).intValue();
|
||||
} else if (codeObj instanceof String) {
|
||||
try {
|
||||
code = Integer.parseInt((String) codeObj);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
// 由于改为 WebSocket 异步推送,这里简化为直接查库返回
|
||||
return task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTaskStatusFromCallback(String taskId, Map<String, Object> callbackData) {
|
||||
ModelTrainTask task = this.getById(taskId);
|
||||
if (task == null) {
|
||||
log.warn("回调通知的任务不存在, taskId: {}", taskId);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String status = (String) callbackData.get("status");
|
||||
if (status != null) {
|
||||
// 转换状态为首字母大写
|
||||
if (status.equalsIgnoreCase("Success")) {
|
||||
status = "Success";
|
||||
} else if (status.equalsIgnoreCase("Failed")) {
|
||||
status = "Failed";
|
||||
} else if (status.equalsIgnoreCase("Training")) {
|
||||
status = "Training";
|
||||
} else if (status.equalsIgnoreCase("Pending")) {
|
||||
status = "Pending";
|
||||
}
|
||||
|
||||
task.setStatus(status);
|
||||
|
||||
if ("Success".equals(status)) {
|
||||
String modelPathRaw = firstNonBlank(
|
||||
(String) callbackData.get("model_path"),
|
||||
(String) callbackData.get("model_relative_path")
|
||||
);
|
||||
task.setModelOutputPath(normalizeModelPath(modelPathRaw));
|
||||
|
||||
// Map -> JSON String
|
||||
if (callbackData.get("metrics") != null) {
|
||||
task.setMetrics(objectMapper.writeValueAsString(callbackData.get("metrics")));
|
||||
}
|
||||
if (code != null && code != 0) {
|
||||
task.setStatus("Failed");
|
||||
task.setErrorLog(String.valueOf(body.get("msg")));
|
||||
this.updateById(task);
|
||||
return task;
|
||||
if (callbackData.get("feature_map") != null) {
|
||||
task.setFeatureMapSnapshot(objectMapper.writeValueAsString(callbackData.get("feature_map")));
|
||||
}
|
||||
|
||||
Object dataObj = body.get("data");
|
||||
if (!(dataObj instanceof Map)) {
|
||||
return task;
|
||||
}
|
||||
Map<String, Object> data = (Map<String, Object>) dataObj;
|
||||
String status = (String) data.get("status");
|
||||
|
||||
if (status != null) {
|
||||
// 转换状态为首字母大写
|
||||
if (status.equalsIgnoreCase("Success")) {
|
||||
status = "Success";
|
||||
} else if (status.equalsIgnoreCase("Failed")) {
|
||||
status = "Failed";
|
||||
} else if (status.equalsIgnoreCase("Training")) {
|
||||
status = "Training";
|
||||
} else if (status.equalsIgnoreCase("Pending")) {
|
||||
status = "Pending";
|
||||
}
|
||||
|
||||
task.setStatus(status);
|
||||
|
||||
if ("Success".equals(status)) {
|
||||
String modelPathRaw = firstNonBlank(
|
||||
(String) data.get("model_relative_path"),
|
||||
(String) data.get("model_path_rel_project"),
|
||||
(String) data.get("model_path")
|
||||
);
|
||||
task.setModelOutputPath(normalizeModelPath(modelPathRaw));
|
||||
|
||||
// Map -> JSON String
|
||||
if (data.get("metrics") != null) {
|
||||
task.setMetrics(objectMapper.writeValueAsString(data.get("metrics")));
|
||||
}
|
||||
if (data.get("feature_map") != null) {
|
||||
task.setFeatureMapSnapshot(objectMapper.writeValueAsString(data.get("feature_map")));
|
||||
}
|
||||
|
||||
String metricsPathRaw = firstNonBlank(
|
||||
(String) data.get("metrics_image_relative_path"),
|
||||
(String) data.get("metrics_image_rel_project"),
|
||||
(String) data.get("metrics_image")
|
||||
);
|
||||
task.setMetricsImagePath(normalizeModelPath(metricsPathRaw));
|
||||
} else if ("Failed".equals(status)) {
|
||||
task.setErrorLog((String) data.get("error"));
|
||||
} else if ("Training".equals(status)) {
|
||||
if (data.get("metrics") != null) {
|
||||
task.setMetrics(objectMapper.writeValueAsString(data.get("metrics")));
|
||||
}
|
||||
}
|
||||
|
||||
this.updateById(task);
|
||||
String metricsPathRaw = firstNonBlank(
|
||||
(String) callbackData.get("metrics_image"),
|
||||
(String) callbackData.get("metrics_image_relative_path")
|
||||
);
|
||||
task.setMetricsImagePath(normalizeModelPath(metricsPathRaw));
|
||||
} else if ("Failed".equals(status)) {
|
||||
task.setErrorLog((String) callbackData.get("message"));
|
||||
} else if ("Training".equals(status)) {
|
||||
if (callbackData.get("metrics") != null) {
|
||||
task.setMetrics(objectMapper.writeValueAsString(callbackData.get("metrics")));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("同步任务状态失败: {}", e.getMessage(), e);
|
||||
|
||||
this.updateById(task);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("从回调更新任务状态失败: {}", e.getMessage(), e);
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -81,7 +81,7 @@ public class ProjectServiceImpl
|
||||
Sheet sheet = wb.createSheet("projects");
|
||||
int r = 0;
|
||||
Row header = sheet.createRow(r++);
|
||||
String[] cols = {"project_id","code","name","description","topology","created_at","updated_at","modifier"};
|
||||
String[] cols = {"项目id","项目编号","项目名称","项目描述","项目建模拓扑","创建时间","修改时间","创建人"};
|
||||
for (int i = 0; i < cols.length; i++) header.createCell(i).setCellValue(cols[i]);
|
||||
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
for (Project p : list) {
|
||||
|
||||
@ -64,6 +64,7 @@ public class SecurityConfig {
|
||||
.requestMatchers(HttpMethod.GET,
|
||||
"/*.html",
|
||||
"/webSocket/**",
|
||||
"/ws/**",
|
||||
"/assets/**",
|
||||
"/icon/**").permitAll()
|
||||
.requestMatchers(
|
||||
@ -80,6 +81,7 @@ public class SecurityConfig {
|
||||
"/pageimage/**",
|
||||
"/avatar/**",
|
||||
"/systemurl/**",
|
||||
"/train/internal/callback",
|
||||
"/api/imageserver/upload").permitAll()
|
||||
.anyRequest().authenticated();
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user