diff --git a/core/pom.xml b/core/pom.xml
index b90f9ba..94afd03 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -149,7 +149,20 @@
1.3.0-91
compile
+
+
+ com.microsoft.onnxruntime
+ onnxruntime
+ 1.16.1
+
+
+
+ org.openpnp
+ opencv
+ 4.7.0-0
+
+
diff --git a/core/src/main/java/com/dite/znpt/config/WebMvcConfig.java b/core/src/main/java/com/dite/znpt/config/WebMvcConfig.java
index 7f8d651..c09ff82 100644
--- a/core/src/main/java/com/dite/znpt/config/WebMvcConfig.java
+++ b/core/src/main/java/com/dite/znpt/config/WebMvcConfig.java
@@ -1,15 +1,12 @@
package com.dite.znpt.config;
import cn.hutool.core.collection.ListUtil;
-import com.dite.znpt.constant.Constants;
import com.dite.znpt.enums.FilePathEnum;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
/**
@@ -27,7 +24,7 @@ public class WebMvcConfig implements WebMvcConfigurer {
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("/swagger-ui/**").addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
for (FilePathEnum pathEnum : FilePathEnum.values()) {
- registry.addResourceHandler(pathEnum.getUrlPath() + "**").addResourceLocations("file:" + pathEnum.getFileAbsolutePath());
+ registry.addResourceHandler(pathEnum.getUrlPath() + "**").addResourceLocations("file:" + pathEnum.getFileAbsolutePathPrefix());
}
}
diff --git a/core/src/main/java/com/dite/znpt/config/YoloModelConfig.java b/core/src/main/java/com/dite/znpt/config/YoloModelConfig.java
new file mode 100644
index 0000000..7e92b49
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/config/YoloModelConfig.java
@@ -0,0 +1,45 @@
+package com.dite.znpt.config;
+
+import ai.onnxruntime.OrtEnvironment;
+import ai.onnxruntime.OrtException;
+import ai.onnxruntime.OrtSession;
+import lombok.Getter;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.PostConstruct;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Getter
+@Configuration
+public class YoloModelConfig {
+
+ private OrtEnvironment env;
+ private OrtSession session;
+ private String[] labels;
+ private List colors;
+
+ @PostConstruct
+ public void init() throws OrtException {
+ env = OrtEnvironment.getEnvironment();
+ OrtSession.SessionOptions options = new OrtSession.SessionOptions();
+ session = env.createSession("d:\\tmp\\best.onnx", options);
+
+ // 解析 labels
+ String names = session.getMetadata().getCustomMetadata().get("names");
+ Pattern pattern = Pattern.compile("'([^']*)'");
+ Matcher matcher = pattern.matcher(names);
+ List labelList = new ArrayList<>();
+ colors = new ArrayList<>();
+
+ Random random = new Random();
+ while (matcher.find()) {
+ labelList.add(matcher.group(1));
+ colors.add(new double[]{random.nextDouble()*256, random.nextDouble()*256, random.nextDouble()*256});
+ }
+ labels = labelList.toArray(new String[0]);
+ }
+}
diff --git a/core/src/main/java/com/dite/znpt/config/YoloModelRegistry.java b/core/src/main/java/com/dite/znpt/config/YoloModelRegistry.java
new file mode 100644
index 0000000..8d3584c
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/config/YoloModelRegistry.java
@@ -0,0 +1,112 @@
+package com.dite.znpt.config;
+
+import ai.onnxruntime.OrtEnvironment;
+import ai.onnxruntime.OrtException;
+import ai.onnxruntime.OrtSession;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.dite.znpt.domain.entity.ModelConfigEntity;
+import com.dite.znpt.enums.FilePathEnum;
+import com.dite.znpt.mapper.ModelConfigMapper;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class YoloModelRegistry {
+
+ private final ModelConfigMapper modelConfigMapper;
+
+ private final Map sessionMap = new ConcurrentHashMap<>();
+ private final Map metaMap = new ConcurrentHashMap<>();
+ private final Map modelParamMap = new ConcurrentHashMap<>();
+ @Getter
+ private final OrtEnvironment environment = OrtEnvironment.getEnvironment();
+
+ static {
+ // 加载opencv动态库
+ nu.pattern.OpenCV.loadLocally();
+ }
+
+ @PostConstruct
+ public void loadModelsOnStartup() throws OrtException {
+ List configs = modelConfigMapper.selectList(Wrappers.emptyWrapper());
+ for (ModelConfigEntity config : configs) {
+ loadModel(config);
+ }
+ }
+
+ public void loadModel(ModelConfigEntity config) throws OrtException {
+ OrtSession.SessionOptions opts = new OrtSession.SessionOptions();
+ // 使用gpu,需要本机安装过cuda,并修改pom.xml,不安装也能运行本程序
+ // sessionOptions.addCUDA(0);
+ OrtSession session = environment.createSession(FilePathEnum.ATTACH.getFileAbsolutePath(config.getModelPath()), opts);
+ String labelStr = session.getMetadata().getCustomMetadata().get("names");
+
+ // label解析
+ List labels = new ArrayList<>();
+ List colors = new ArrayList<>();
+ Pattern pattern = Pattern.compile("'([^']*)'");
+ Matcher matcher = pattern.matcher(labelStr);
+ Random random = new Random();
+ while (matcher.find()) {
+ labels.add(matcher.group(1));
+ colors.add(new double[]{random.nextDouble() * 256, random.nextDouble() * 256, random.nextDouble() * 256});
+ }
+
+ sessionMap.put(config.getModelId(), session);
+ metaMap.put(config.getModelId(), new ModelMetadata(labels.toArray(new String[0]), colors, config));
+ modelParamMap.put(config.getModelId(), config);
+ }
+
+ public OrtSession getSession(String modelId) {
+ return sessionMap.get(modelId);
+ }
+
+ public ModelMetadata getMetadata(String modelId) {
+ return metaMap.get(modelId);
+ }
+
+ public ModelConfigEntity getModelConfig(String modelId) {
+ return modelParamMap.get(modelId);
+ }
+
+ public void unloadModel(String modelId) {
+ OrtSession session = sessionMap.remove(modelId);
+ if (session != null) {
+ try {
+ session.close(); // 释放 ONNX runtime 资源
+ } catch (OrtException e) {
+ log.warn("模型 {} 卸载时发生错误: {}", modelId, e.getMessage());
+ }
+ }
+ metaMap.remove(modelId);
+ }
+
+ public void reloadModel(ModelConfigEntity modelConfig) throws OrtException {
+ unloadModel(modelConfig.getModelId());
+ loadModel(modelConfig);
+ }
+
+ @Data
+ @AllArgsConstructor
+ public static class ModelMetadata {
+ private String[] labels;
+ private List colors;
+ private ModelConfigEntity config;
+ }
+}
+
diff --git a/core/src/main/java/com/dite/znpt/domain/bo/Detection.java b/core/src/main/java/com/dite/znpt/domain/bo/Detection.java
new file mode 100644
index 0000000..68e942c
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/domain/bo/Detection.java
@@ -0,0 +1,21 @@
+package com.dite.znpt.domain.bo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class Detection{
+
+ @ApiModelProperty("标签")
+ public String label;
+ @ApiModelProperty("分类id")
+ private Integer clsId;
+ @ApiModelProperty("位置")
+ private float[] bbox;
+ @ApiModelProperty("置信度")
+ private float confidence;
+}
diff --git a/core/src/main/java/com/dite/znpt/domain/bo/Letterbox.java b/core/src/main/java/com/dite/znpt/domain/bo/Letterbox.java
new file mode 100644
index 0000000..b3fa640
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/domain/bo/Letterbox.java
@@ -0,0 +1,73 @@
+package com.dite.znpt.domain.bo;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.opencv.core.Core;
+import org.opencv.core.Mat;
+import org.opencv.core.Size;
+import org.opencv.imgproc.Imgproc;
+
+public class Letterbox {
+
+ @Setter
+ private Size newShape ;
+ private final double[] color = new double[]{114,114,114};
+ private final Boolean auto = false;
+ private final Boolean scaleUp = true;
+ @Setter
+ private Integer stride = 32;
+
+ @Getter
+ private double ratio;
+ @Getter
+ private double dw;
+ @Getter
+ private double dh;
+
+ public Letterbox(int w,int h) {
+ this.newShape = new Size(w, h);
+ }
+
+ public Letterbox() {
+ this.newShape = new Size(640, 640);
+ }
+
+
+ public Integer getWidth() {
+ return (int) this.newShape.width;
+ }
+
+ public Integer getHeight() {
+ return (int) this.newShape.height;
+ }
+
+ public Mat letterbox(Mat im) { // 调整图像大小和填充图像,使满足步长约束,并记录参数
+
+ int[] shape = {im.rows(), im.cols()}; // 当前形状 [height, width]
+ // Scale ratio (new / old)
+ double r = Math.min(this.newShape.height / shape[0], this.newShape.width / shape[1]);
+ if (!this.scaleUp) { // 仅缩小,不扩大(一且为了mAP)
+ r = Math.min(r, 1.0);
+ }
+ // Compute padding
+ Size newUnpad = new Size(Math.round(shape[1] * r), Math.round(shape[0] * r));
+ double dw = this.newShape.width - newUnpad.width, dh = this.newShape.height - newUnpad.height; // wh 填充
+ if (this.auto) { // 最小矩形
+ dw = dw % this.stride;
+ dh = dh % this.stride;
+ }
+ dw /= 2; // 填充的时候两边都填充一半,使图像居于中心
+ dh /= 2;
+ if (shape[1] != newUnpad.width || shape[0] != newUnpad.height) { // resize
+ Imgproc.resize(im, im, newUnpad, 0, 0, Imgproc.INTER_LINEAR);
+ }
+ int top = (int) Math.round(dh - 0.1), bottom = (int) Math.round(dh + 0.1);
+ int left = (int) Math.round(dw - 0.1), right = (int) Math.round(dw + 0.1);
+ // 将图像填充为正方形
+ Core.copyMakeBorder(im, im, top, bottom, left, right, Core.BORDER_CONSTANT, new org.opencv.core.Scalar(this.color));
+ this.ratio = r;
+ this.dh = dh;
+ this.dw = dw;
+ return im;
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/dite/znpt/domain/bo/ODConfig.java b/core/src/main/java/com/dite/znpt/domain/bo/ODConfig.java
new file mode 100644
index 0000000..689a7bd
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/domain/bo/ODConfig.java
@@ -0,0 +1,55 @@
+package com.dite.znpt.domain.bo;
+
+import java.util.*;
+
+public final class ODConfig {
+
+
+ public static final Integer lineThicknessRatio = 333;
+ public static final Double fontSizeRatio = 1080.0;
+
+ private static final List default_names = new ArrayList<>(Arrays.asList(
+ "person", "bicycle", "car", "motorcycle", "airplane", "bus", "train",
+ "truck", "boat", "traffic light", "fire hydrant", "stop sign", "parking meter",
+ "bench", "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear",
+ "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase",
+ "frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat",
+ "baseball glove", "skateboard", "surfboard", "tennis racket", "bottle",
+ "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
+ "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut",
+ "cake", "chair", "couch", "potted plant", "bed", "dining table", "toilet",
+ "tv", "laptop", "mouse", "remote", "keyboard", "cell phone", "microwave",
+ "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors",
+ "teddy bear", "hair drier", "toothbrush"));
+
+
+ private static final List names = new ArrayList<>(Arrays.asList(
+ "no_helmet", "helmet"));
+
+ private final Map colors;
+
+ public ODConfig() {
+ this.colors = new HashMap<>();
+ default_names.forEach(name->{
+ Random random = new Random();
+ double[] color = {random.nextDouble()*256, random.nextDouble()*256, random.nextDouble()*256};
+ colors.put(name, color);
+ });
+ }
+
+ public String getName(int clsId) {
+ return names.get(clsId);
+ }
+
+ public double[] getColor(int clsId) {
+ return colors.get(getName(clsId));
+ }
+
+ public double[] getNameColor(String Name){
+ return colors.get(Name);
+ }
+
+ public double[] getOtherColor(int clsId) {
+ return colors.get(default_names.get(clsId));
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/dite/znpt/domain/entity/ModelConfigEntity.java b/core/src/main/java/com/dite/znpt/domain/entity/ModelConfigEntity.java
new file mode 100644
index 0000000..549b9c8
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/domain/entity/ModelConfigEntity.java
@@ -0,0 +1,56 @@
+package com.dite.znpt.domain.entity;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.dite.znpt.domain.AuditableEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @author huise23
+ * @date 2025/07/02 20:57
+ * @Description: 模型配置表实体类
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@TableName("model_config")
+@ApiModel(value="ModelConfigEntity对象", description="模型配置表")
+public class ModelConfigEntity extends AuditableEntity implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = -73052440757340126L;
+
+ @ExcelProperty("模型id")
+ @ApiModelProperty("模型id")
+ @TableId(value = "model_id", type = IdType.ASSIGN_ID)
+ private String modelId;
+
+ @ExcelProperty("模型名称")
+ @ApiModelProperty("模型名称")
+ @TableField("model_name")
+ private String modelName;
+
+ @ExcelProperty("模型路径")
+ @ApiModelProperty("模型路径")
+ @TableField("model_path")
+ private String modelPath;
+
+ @ExcelProperty("模型置信度")
+ @ApiModelProperty("模型置信度")
+ @TableField("conf_threshold")
+ private Float confThreshold;
+
+ @ExcelProperty("非极大抑制")
+ @ApiModelProperty("非极大抑制")
+ @TableField("nms_threshold")
+ private Float nmsThreshold;
+}
+
diff --git a/core/src/main/java/com/dite/znpt/domain/entity/PartEntity.java b/core/src/main/java/com/dite/znpt/domain/entity/PartEntity.java
index f602cbf..d29345d 100644
--- a/core/src/main/java/com/dite/znpt/domain/entity/PartEntity.java
+++ b/core/src/main/java/com/dite/znpt/domain/entity/PartEntity.java
@@ -1,24 +1,27 @@
package com.dite.znpt.domain.entity;
-import java.io.Serializable;
-
-import com.baomidou.mybatisplus.annotation.*;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
import com.dite.znpt.domain.AuditableEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
-import com.alibaba.excel.annotation.ExcelProperty;
+
+import java.io.Serializable;
/**
* @author huise23
* @date 2025/04/11 23:17
- * @Description: 表实体类
+ * @Description: 部件表实体类
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("part")
-@ApiModel(value="PartEntity对象", description="表")
+@ApiModel(value="PartEntity对象", description="部件表")
public class PartEntity extends AuditableEntity implements Serializable {
private static final long serialVersionUID = -53853862365306266L;
diff --git a/core/src/main/java/com/dite/znpt/domain/vo/ModelConfigListReq.java b/core/src/main/java/com/dite/znpt/domain/vo/ModelConfigListReq.java
new file mode 100644
index 0000000..8944150
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/domain/vo/ModelConfigListReq.java
@@ -0,0 +1,41 @@
+package com.dite.znpt.domain.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @author huise23
+ * @date 2025/07/02 20:57
+ * @Description: 请求实体
+ */
+@Data
+@ApiModel("列表请求实体")
+public class ModelConfigListReq implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = -41204426418525667L;
+
+ @ApiModelProperty("查询关键字")
+ private String keyword;
+
+ @ApiModelProperty("Id")
+ private String modelId;
+
+ @ApiModelProperty("模型名称")
+ private String modelName;
+
+ @ApiModelProperty("模型路径")
+ private String modelPath;
+
+ @ApiModelProperty("模型置信度")
+ private Float confThreshold;
+
+ @ApiModelProperty("非极大抑制")
+ private Float nmsThreshold;
+
+}
+
diff --git a/core/src/main/java/com/dite/znpt/domain/vo/ModelConfigReq.java b/core/src/main/java/com/dite/znpt/domain/vo/ModelConfigReq.java
new file mode 100644
index 0000000..73a3742
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/domain/vo/ModelConfigReq.java
@@ -0,0 +1,50 @@
+package com.dite.znpt.domain.vo;
+
+import com.dite.znpt.util.ValidationGroup;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.*;
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @author huise23
+ * @date 2025/07/02 20:57
+ * @Description: 模型配置表请求类
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@ApiModel(value="ModelConfig请求对象", description="模型配置表")
+public class ModelConfigReq implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 930798215980875267L;
+
+ @ApiModelProperty("模型id")
+ private String modelId;
+
+ @NotBlank(groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, message = "模型名称不能为空")
+ @Size(max = 50, groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, message = "模型名称长度不能超过50")
+ @ApiModelProperty("模型名称")
+ private String modelName;
+
+ @NotEmpty(groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, message = "模型附件不能为空")
+ @ApiModelProperty("模型附件id")
+ private String attachId;
+
+ @NotNull(groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, message = "模型置信度不能为空")
+ @Min(value = 0, groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, message = "模型置信度只能在0-100之间")
+ @Max(value = 100, groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, message = "模型置信度只能在0-100之间")
+ @ApiModelProperty("模型置信度")
+ private Float confThreshold;
+
+ @NotNull(groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, message = "nms不能为空")
+ @Min(value = 0, groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, message = "nms只能在0-1之间")
+ @Max(value = 1, groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, message = "nms只能在0-1之间")
+ @ApiModelProperty("nms")
+ private Float nmsThreshold;
+}
+
diff --git a/core/src/main/java/com/dite/znpt/domain/vo/ModelConfigResp.java b/core/src/main/java/com/dite/znpt/domain/vo/ModelConfigResp.java
new file mode 100644
index 0000000..7b7d588
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/domain/vo/ModelConfigResp.java
@@ -0,0 +1,18 @@
+package com.dite.znpt.domain.vo;
+
+import com.dite.znpt.domain.entity.ModelConfigEntity;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * @author huise23
+ * @date 2025/07/02 20:57
+ * @Description: 响应实体
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ApiModel("响应实体")
+public class ModelConfigResp extends ModelConfigEntity {
+}
+
diff --git a/core/src/main/java/com/dite/znpt/domain/vo/ProjectTaskImportReq.java b/core/src/main/java/com/dite/znpt/domain/vo/ProjectTaskImportReq.java
index c2b6103..bc56499 100644
--- a/core/src/main/java/com/dite/znpt/domain/vo/ProjectTaskImportReq.java
+++ b/core/src/main/java/com/dite/znpt/domain/vo/ProjectTaskImportReq.java
@@ -1,21 +1,13 @@
package com.dite.znpt.domain.vo;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.io.Serial;
-import java.io.Serializable;
-
-import com.baomidou.mybatisplus.annotation.*;
+import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
-import com.alibaba.excel.annotation.ExcelProperty;
-import com.dite.znpt.util.ValidationGroup;
-import javax.validation.constraints.NotBlank;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Size;
+import java.io.Serial;
+import java.io.Serializable;
+import java.time.LocalDate;
/**
* @author huise23
* @date 2025/06/27 14:21
@@ -60,11 +52,11 @@ public class ProjectTaskImportReq implements Serializable {
@ExcelProperty(value = "实际结束时间")
private LocalDate actualEndDate;
- @ExcelProperty(value = "任务状态,0未开始,1进行中,2已结束")
- private Integer status;
+ @ExcelProperty(value = "任务状态(未开始,进行中,已结束)")
+ private String status;
- @ExcelProperty(value = "是否逾期,默认未逾期")
- private Integer overdueStatus;
+ @ExcelProperty(value = "是否逾期(已逾期,未逾期)")
+ private String overdueStatus;
@ExcelProperty(value = "备注")
private String remark;
diff --git a/core/src/main/java/com/dite/znpt/enums/AttachBusinessTypeEnum.java b/core/src/main/java/com/dite/znpt/enums/AttachBusinessTypeEnum.java
index c5c0909..502a022 100644
--- a/core/src/main/java/com/dite/znpt/enums/AttachBusinessTypeEnum.java
+++ b/core/src/main/java/com/dite/znpt/enums/AttachBusinessTypeEnum.java
@@ -15,6 +15,7 @@ import java.util.List;
public enum AttachBusinessTypeEnum {
PROJECT_TASK("PROJECT_TASK", "项目任务"),
INSURANCE_FILE("insurance", "保险文件"),
+ MODEL_FILE("model", "模型文件"),
;
private final String code;
private final String desc;
diff --git a/core/src/main/java/com/dite/znpt/enums/FilePathEnum.java b/core/src/main/java/com/dite/znpt/enums/FilePathEnum.java
index 596613a..702b30f 100644
--- a/core/src/main/java/com/dite/znpt/enums/FilePathEnum.java
+++ b/core/src/main/java/com/dite/znpt/enums/FilePathEnum.java
@@ -22,26 +22,37 @@ public enum FilePathEnum {
private final String fileRelativePath;
/**
- * 功能描述:获取文件绝对路径
+ * 功能描述:获取文件绝对路径前缀
*
* @return {@link String }
* @author cuizhibin
* @date 2025/06/23 14:46
**/
- public String getFileAbsolutePath() {
+ public String getFileAbsolutePathPrefix() {
return SpringUtil.getBean(Environment.class).getProperty("upload.save-path") + fileRelativePath + FileUtil.FILE_SEPARATOR;
}
/**
- * 功能描述:获取图像下载路径
+ * 功能描述:获取文件绝对路径全路径
+ *
+ * @return {@link String }
+ * @author cuizhibin
+ * @date 2025/06/23 14:46
+ **/
+ public String getFileAbsolutePath(String fileDownPath) {
+ return FileUtil.normalize(getFileAbsolutePathPrefix() + StrUtil.removePrefix(fileDownPath, urlPath));
+ }
+
+ /**
+ * 功能描述:获取下载路径
*
* @param absolutePath
* @return {@link String }
* @author cuizhibin
* @date 2025/06/06 09:07
**/
- public String getImageDownPath(String absolutePath) {
- String relativePath = StrUtil.removePrefix(absolutePath, getFileAbsolutePath());
+ public String getFileDownPath(String absolutePath) {
+ String relativePath = StrUtil.removePrefix(absolutePath, getFileAbsolutePathPrefix());
return StrUtil.replace(urlPath.concat(relativePath), FileUtil.FILE_SEPARATOR, StrUtil.SLASH);
}
diff --git a/core/src/main/java/com/dite/znpt/mapper/ModelConfigMapper.java b/core/src/main/java/com/dite/znpt/mapper/ModelConfigMapper.java
new file mode 100644
index 0000000..37906a4
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/mapper/ModelConfigMapper.java
@@ -0,0 +1,18 @@
+package com.dite.znpt.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.dite.znpt.domain.entity.ModelConfigEntity;
+import com.dite.znpt.domain.vo.ModelConfigListReq;
+import com.dite.znpt.domain.vo.ModelConfigResp;
+
+import java.util.List;
+
+/**
+ * @author huise23
+ * @date 2025/07/02 20:57
+ * @Description: 模型配置表数据库访问层
+ */
+public interface ModelConfigMapper extends BaseMapper {
+ List queryBySelective(ModelConfigListReq modelConfigReq);
+}
+
diff --git a/core/src/main/java/com/dite/znpt/mapper/PartMapper.java b/core/src/main/java/com/dite/znpt/mapper/PartMapper.java
index efcb3fa..2ec4e0d 100644
--- a/core/src/main/java/com/dite/znpt/mapper/PartMapper.java
+++ b/core/src/main/java/com/dite/znpt/mapper/PartMapper.java
@@ -4,15 +4,13 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dite.znpt.domain.entity.PartEntity;
import com.dite.znpt.domain.vo.PartListReq;
import com.dite.znpt.domain.vo.PartListResp;
-import com.dite.znpt.domain.vo.PartResp;
-import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author huise23
* @date 2025/04/11 23:17
- * @Description: 表数据库访问层
+ * @Description: 部件表数据库访问层
*/
public interface PartMapper extends BaseMapper {
List queryBySelective(PartListReq partReq);
diff --git a/core/src/main/java/com/dite/znpt/service/AttachInfoService.java b/core/src/main/java/com/dite/znpt/service/AttachInfoService.java
index f11a8df..3c4987f 100644
--- a/core/src/main/java/com/dite/znpt/service/AttachInfoService.java
+++ b/core/src/main/java/com/dite/znpt/service/AttachInfoService.java
@@ -7,7 +7,6 @@ import com.dite.znpt.enums.AttachBusinessTypeEnum;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
-import java.rmi.ServerException;
import java.util.List;
/**
diff --git a/core/src/main/java/com/dite/znpt/service/ModelConfigService.java b/core/src/main/java/com/dite/znpt/service/ModelConfigService.java
new file mode 100644
index 0000000..052ef99
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/service/ModelConfigService.java
@@ -0,0 +1,65 @@
+package com.dite.znpt.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.dite.znpt.domain.entity.ModelConfigEntity;
+import com.dite.znpt.domain.vo.ModelConfigListReq;
+import com.dite.znpt.domain.vo.ModelConfigReq;
+import com.dite.znpt.domain.vo.ModelConfigResp;
+
+import java.util.List;
+
+/**
+ * @author huise23
+ * @date 2025/07/02 20:57
+ * @Description: 模型配置表服务接口
+ */
+public interface ModelConfigService extends IService {
+
+ /**
+ * 功能描述:查询列表
+ *
+ * @param modelConfigReq
+ * @return {@link List }<{@link ModelConfigResp }>
+ * @author huise23
+ * @date 2025/07/02 20:57
+ **/
+ List selectList(ModelConfigListReq modelConfigReq);
+
+ /**
+ * 功能描述:查询单条
+ *
+ * @param modelId Id
+ * @return {@link ModelConfigResp }
+ * @author huise23
+ * @date 2025/07/02 20:57
+ **/
+ ModelConfigResp selectById(String modelId);
+
+ /**
+ * 功能描述:新增
+ *
+ * @param modelConfigReq
+ * @author huise23
+ * @date 2025/07/02 20:57
+ **/
+ void saveData(ModelConfigReq modelConfigReq);
+
+ /**
+ * 功能描述:更新
+ *
+ * @param modelConfigReq
+ * @author huise23
+ * @date 2025/07/02 20:57
+ **/
+ void updateData(ModelConfigReq modelConfigReq);
+
+ /**
+ * 功能描述:删除
+ *
+ * @param modelId Id
+ * @author huise23
+ * @date 2025/07/02 20:57
+ **/
+ void deleteById(String modelId);
+}
+
diff --git a/core/src/main/java/com/dite/znpt/service/PartService.java b/core/src/main/java/com/dite/znpt/service/PartService.java
index 1eba964..1901493 100644
--- a/core/src/main/java/com/dite/znpt/service/PartService.java
+++ b/core/src/main/java/com/dite/znpt/service/PartService.java
@@ -13,7 +13,7 @@ import java.util.List;
/**
* @author huise23
* @date 2025/04/11 23:17
- * @Description: 表服务接口
+ * @Description: 部件表服务接口
*/
public interface PartService extends IService {
diff --git a/core/src/main/java/com/dite/znpt/service/impl/AttachInfoServiceImpl.java b/core/src/main/java/com/dite/znpt/service/impl/AttachInfoServiceImpl.java
index 2762f34..93c77dd 100644
--- a/core/src/main/java/com/dite/znpt/service/impl/AttachInfoServiceImpl.java
+++ b/core/src/main/java/com/dite/znpt/service/impl/AttachInfoServiceImpl.java
@@ -10,17 +10,15 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dite.znpt.constant.Constants;
import com.dite.znpt.constant.Message;
import com.dite.znpt.domain.entity.AttachInfoEntity;
-import com.dite.znpt.domain.entity.InsuranceInfoEntity;
import com.dite.znpt.domain.vo.AttachInfoReq;
import com.dite.znpt.enums.AttachBusinessTypeEnum;
import com.dite.znpt.enums.FilePathEnum;
-import com.dite.znpt.service.AttachInfoService;
import com.dite.znpt.mapper.FileInfoMapper;
+import com.dite.znpt.service.AttachInfoService;
+import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
-import lombok.RequiredArgsConstructor;
-import com.dite.znpt.util.PageUtil;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
@@ -69,7 +67,7 @@ public class AttachInfoServiceImpl extends ServiceImpl saveData(String businessType, AttachInfoReq infoReq, MultipartFile[] files) {
- String temPathPrefix = FilePathEnum.ATTACH.getFileAbsolutePath().concat(businessType).concat(FileUtil.FILE_SEPARATOR);
+ String temPathPrefix = FilePathEnum.ATTACH.getFileAbsolutePathPrefix().concat(businessType).concat(FileUtil.FILE_SEPARATOR);
if(StrUtil.isNotBlank(infoReq.getUserDefinedPath())){
temPathPrefix = temPathPrefix.concat(infoReq.getUserDefinedPath()).concat(FileUtil.FILE_SEPARATOR);
}
@@ -84,7 +82,7 @@ public class AttachInfoServiceImpl extends ServiceImpl list = new ArrayList<>();
for (MultipartFile file : files) {
AudioFileInfoEntity audio = new AudioFileInfoEntity();
@@ -113,7 +113,7 @@ public class AudioFileInfoServiceImpl extends ServiceImpl impleme
dictReq.setDictId(dictId);
List list = selectList(dictReq);
- return list.isEmpty() ? CollUtil.getFirst(list) : new DictResp();
+ return CollUtil.isNotEmpty(list) ? CollUtil.getFirst(list) : new DictResp();
}
/**
diff --git a/core/src/main/java/com/dite/znpt/service/impl/ImageCollectServiceImpl.java b/core/src/main/java/com/dite/znpt/service/impl/ImageCollectServiceImpl.java
index dd87b06..d786566 100644
--- a/core/src/main/java/com/dite/znpt/service/impl/ImageCollectServiceImpl.java
+++ b/core/src/main/java/com/dite/znpt/service/impl/ImageCollectServiceImpl.java
@@ -52,8 +52,8 @@ public class ImageCollectServiceImpl extends ServiceImpl imageList = Converts.INSTANCE.toImageEntity(req.getImageList());
- String permPathPrefix = FilePathEnum.IMAGE.getFileAbsolutePath().concat(ImageSourceEnum.COLLECT.getCode()).concat(FileUtil.FILE_SEPARATOR).concat(partId).concat(FileUtil.FILE_SEPARATOR).concat(dateStr).concat(FileUtil.FILE_SEPARATOR);
- String temPathPrefix = FilePathEnum.IMAGE_TEMP.getFileAbsolutePath().concat(ImageSourceEnum.COLLECT.getCode()).concat(FileUtil.FILE_SEPARATOR).concat(partId).concat(FileUtil.FILE_SEPARATOR);
+ String permPathPrefix = FilePathEnum.IMAGE.getFileAbsolutePathPrefix().concat(ImageSourceEnum.COLLECT.getCode()).concat(FileUtil.FILE_SEPARATOR).concat(partId).concat(FileUtil.FILE_SEPARATOR).concat(dateStr).concat(FileUtil.FILE_SEPARATOR);
+ String temPathPrefix = FilePathEnum.IMAGE_TEMP.getFileAbsolutePathPrefix().concat(ImageSourceEnum.COLLECT.getCode()).concat(FileUtil.FILE_SEPARATOR).concat(partId).concat(FileUtil.FILE_SEPARATOR);
imageList.forEach(image -> {
image.setPartId(partId);
image.setCollectId(imageCollect.getCollectId());
@@ -62,7 +62,7 @@ public class ImageCollectServiceImpl extends ServiceImpl impl
Map partIdMap= partService.listByIds(partIds).stream().collect(Collectors.toMap(PartEntity::getPartId, Function.identity()));
list.forEach(req -> {
if(partIdMap.containsKey(req.getPartId())){
- String path_prefix = FilePathEnum.IMAGE.getFileAbsolutePath().concat(StrUtil.BACKSLASH).concat(req.getImageSource()).concat(StrUtil.BACKSLASH).concat(req.getPartId()).concat(StrUtil.BACKSLASH);
- String temp_path_prefix = FilePathEnum.IMAGE_TEMP.getFileAbsolutePath().concat(StrUtil.BACKSLASH).concat(req.getImageSource()).concat(StrUtil.BACKSLASH).concat(req.getPartId()).concat(StrUtil.BACKSLASH);
+ String path_prefix = FilePathEnum.IMAGE.getFileAbsolutePathPrefix().concat(StrUtil.BACKSLASH).concat(req.getImageSource()).concat(StrUtil.BACKSLASH).concat(req.getPartId()).concat(StrUtil.BACKSLASH);
+ String temp_path_prefix = FilePathEnum.IMAGE_TEMP.getFileAbsolutePathPrefix().concat(StrUtil.BACKSLASH).concat(req.getImageSource()).concat(StrUtil.BACKSLASH).concat(req.getPartId()).concat(StrUtil.BACKSLASH);
File file = new File(req.getImagePath());
if(file.exists() && req.getImagePath().contains(temp_path_prefix)){
ImageEntity entity = new ImageEntity();
@@ -146,12 +142,12 @@ public class ImageServiceImpl extends ServiceImpl impl
throw new ServiceException(Message.IMAGE_IS_EMPTY);
}
List list = new ArrayList<>(files.length);
- File temCategory = new File(FilePathEnum.IMAGE_TEMP.getFileAbsolutePath());
+ File temCategory = new File(FilePathEnum.IMAGE_TEMP.getFileAbsolutePathPrefix());
if (!temCategory.exists()) {
// 创建完整的目录
temCategory.mkdirs();
}
- String temPathPrefix = FilePathEnum.IMAGE_TEMP.getFileAbsolutePath().concat(imageSource).concat(FileUtil.FILE_SEPARATOR).concat(partId).concat(FileUtil.FILE_SEPARATOR);
+ String temPathPrefix = FilePathEnum.IMAGE_TEMP.getFileAbsolutePathPrefix().concat(imageSource).concat(FileUtil.FILE_SEPARATOR).concat(partId).concat(FileUtil.FILE_SEPARATOR);
for (MultipartFile file : files) {
if (!file.isEmpty()) {
try {
@@ -173,7 +169,7 @@ public class ImageServiceImpl extends ServiceImpl impl
throw new ServiceException(Message.IMAGE_IS_EMPTY);
}
String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
- String path_prefix = FilePathEnum.IMAGE.getFileAbsolutePath().concat(imageSource).concat(FileUtil.FILE_SEPARATOR).concat(dateStr).concat(FileUtil.FILE_SEPARATOR);
+ String path_prefix = FilePathEnum.IMAGE.getFileAbsolutePathPrefix().concat(imageSource).concat(FileUtil.FILE_SEPARATOR).concat(dateStr).concat(FileUtil.FILE_SEPARATOR);
if (Objects.nonNull(imageWorkReq)) {
path_prefix = path_prefix.concat(StrUtil.emptyToDefault(imageWorkReq.getUploadUser(), "默认用户")).concat(FileUtil.FILE_SEPARATOR)
.concat(StrUtil.emptyToDefault(imageWorkReq.getLongitude(), "0"))
@@ -188,7 +184,7 @@ public class ImageServiceImpl extends ServiceImpl impl
for (MultipartFile multipartFile : files) {
String path = path_prefix + multipartFile.getOriginalFilename();
FileUtil.writeBytes(multipartFile.getBytes(),path);
- result.add(FilePathEnum.IMAGE.getImageDownPath(path));
+ result.add(FilePathEnum.IMAGE.getFileDownPath(path));
}
String partId = imageWorkReq.getPartId();
if (StrUtil.isNotEmpty(partId)) {
@@ -202,7 +198,7 @@ public class ImageServiceImpl extends ServiceImpl impl
List imageList = new ArrayList<>();
result.forEach(path -> {
ImageEntity imageEntity = new ImageEntity();
- String absolutePath = FilePathEnum.IMAGE.getFileAbsolutePath() + StrUtil.removePrefix(path, FilePathEnum.IMAGE.getUrlPath());
+ String absolutePath = FilePathEnum.IMAGE.getFileAbsolutePathPrefix() + StrUtil.removePrefix(path, FilePathEnum.IMAGE.getUrlPath());
try {
ImageReq imageReq = imageRespBuilder(absolutePath);
BeanUtil.copyProperties(imageReq, imageEntity);
@@ -251,7 +247,7 @@ public class ImageServiceImpl extends ServiceImpl impl
req.setCameraManufacturer(obj.getStr("Make"));
req.setCameraModel(obj.getStr("Model"));
req.setImageName(obj.getStr("File Name"));
- req.setImagePath(FilePathEnum.IMAGE_TEMP.getImageDownPath(path));
+ req.setImagePath(FilePathEnum.IMAGE_TEMP.getFileDownPath(path));
BigDecimal imageSize = new BigDecimal(extractDigit(obj.getStr("File Size"))).divide(new BigDecimal(1024*1024), 4, RoundingMode.HALF_UP);
req.setImageSize(imageSize.toString().concat("M"));
req.setImageWidth(extractDigit(obj.getStr("Image Width")));
@@ -308,11 +304,11 @@ public class ImageServiceImpl extends ServiceImpl impl
@Override
public List listAppUploadImages() {
List filePaths = new ArrayList<>();
- PathUtil.walkFiles(Path.of(FilePathEnum.IMAGE.getFileAbsolutePath()), new SimpleFileVisitor<>() {
+ PathUtil.walkFiles(Path.of(FilePathEnum.IMAGE.getFileAbsolutePathPrefix()), new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
if (path.toFile().isFile()) {
- String imageDownPath = FilePathEnum.IMAGE.getImageDownPath(path.toFile().getAbsolutePath());
+ String imageDownPath = FilePathEnum.IMAGE.getFileDownPath(path.toFile().getAbsolutePath());
List split = StrUtil.split(imageDownPath, StrUtil.SLASH, true, true);
// /static/image/source/date
try {
@@ -405,7 +401,7 @@ public class ImageServiceImpl extends ServiceImpl impl
List newImageList = new ArrayList<>();
partReq.getImagePaths().forEach(path -> {
ImageEntity imageEntity = new ImageEntity();
- String absolutePath = FilePathEnum.IMAGE.getFileAbsolutePath() + StrUtil.removePrefix(path, FilePathEnum.IMAGE.getUrlPath());
+ String absolutePath = FilePathEnum.IMAGE.getFileAbsolutePathPrefix() + StrUtil.removePrefix(path, FilePathEnum.IMAGE.getUrlPath());
try {
ImageReq imageReq = imageRespBuilder(absolutePath);
BeanUtil.copyProperties(imageReq, imageEntity);
diff --git a/core/src/main/java/com/dite/znpt/service/impl/ModelConfigServiceImpl.java b/core/src/main/java/com/dite/znpt/service/impl/ModelConfigServiceImpl.java
new file mode 100644
index 0000000..b940600
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/service/impl/ModelConfigServiceImpl.java
@@ -0,0 +1,123 @@
+package com.dite.znpt.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.ListUtil;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.dite.znpt.config.YoloModelRegistry;
+import com.dite.znpt.domain.entity.AttachInfoEntity;
+import com.dite.znpt.domain.entity.ModelConfigEntity;
+import com.dite.znpt.domain.vo.ModelConfigListReq;
+import com.dite.znpt.domain.vo.ModelConfigReq;
+import com.dite.znpt.domain.vo.ModelConfigResp;
+import com.dite.znpt.enums.AttachBusinessTypeEnum;
+import com.dite.znpt.mapper.ModelConfigMapper;
+import com.dite.znpt.service.AttachInfoService;
+import com.dite.znpt.service.ModelConfigService;
+import com.dite.znpt.util.PageUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * @author huise23
+ * @date 2025/07/02 20:57
+ * @Description: 模型配置表服务实现类
+ */
+@Service
+@RequiredArgsConstructor
+public class ModelConfigServiceImpl extends ServiceImpl implements ModelConfigService {
+
+ private final YoloModelRegistry modelRegistry;
+ private final AttachInfoService attachInfoService;
+
+ /**
+ * 功能描述:查询列表
+ *
+ * @param modelConfigReq 信息
+ * @return {@link List }<{@link ModelConfigResp }>
+ * @author huise23
+ * @date 2025/07/02 20:57
+ **/
+ @Override
+ public List selectList(ModelConfigListReq modelConfigReq) {
+ PageUtil.startPage();
+ List modelConfigList= this.baseMapper.queryBySelective(modelConfigReq);
+ modelConfigList.forEach(resp -> {
+
+ });
+ return modelConfigList;
+ }
+
+ /**
+ * 功能描述:查询单条
+ *
+ * @param modelId Id
+ * @return {@link ModelConfigResp }
+ * @author huise23
+ * @date 2025/07/02 20:57
+ **/
+ @Override
+ public ModelConfigResp selectById(String modelId) {
+ ModelConfigListReq modelConfigReq = new ModelConfigListReq();
+ modelConfigReq.setModelId(modelId);
+
+ List list = selectList(modelConfigReq);
+ return CollUtil.isNotEmpty(list) ? CollUtil.getFirst(list) : new ModelConfigResp();
+ }
+
+ /**
+ * 功能描述:新增
+ *
+ * @param modelConfigReq
+ * @author huise23
+ * @date 2025/07/02 20:57
+ **/
+ @SneakyThrows
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void saveData(ModelConfigReq modelConfigReq) {
+ ModelConfigEntity entity = BeanUtil.copyProperties(modelConfigReq, ModelConfigEntity.class);
+ AttachInfoEntity attachInfo = attachInfoService.getById(modelConfigReq.getAttachId());
+ entity.setModelPath(attachInfo.getAttachPath());
+ save(entity);
+ attachInfoService.updateBusinessIdByAttachIds(entity.getModelId(), ListUtil.of(modelConfigReq.getAttachId()), AttachBusinessTypeEnum.MODEL_FILE);
+ modelRegistry.loadModel(entity);
+ }
+
+ /**
+ * 功能描述:更新
+ *
+ * @param modelConfigReq
+ * @author huise23
+ * @date 2025/07/02 20:57
+ **/
+ @SneakyThrows
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateData(ModelConfigReq modelConfigReq) {
+ ModelConfigEntity entity = BeanUtil.copyProperties(modelConfigReq, ModelConfigEntity.class);
+ AttachInfoEntity attachInfo = attachInfoService.getById(modelConfigReq.getAttachId());
+ entity.setModelPath(attachInfo.getAttachPath());
+ attachInfoService.updateBusinessIdByAttachIds(entity.getModelId(), ListUtil.of(modelConfigReq.getAttachId()), AttachBusinessTypeEnum.MODEL_FILE);
+ updateById(entity);
+ modelRegistry.reloadModel(entity);
+ }
+
+ /**
+ * 功能描述:删除
+ *
+ * @param modelId Id
+ * @author huise23
+ * @date 2025/07/02 20:57
+ **/
+ @Override
+ public void deleteById(String modelId) {
+ modelRegistry.unloadModel(modelId);
+ removeById(modelId);
+ }
+
+}
diff --git a/core/src/main/java/com/dite/znpt/service/impl/MultiModelYoloService.java b/core/src/main/java/com/dite/znpt/service/impl/MultiModelYoloService.java
new file mode 100644
index 0000000..b1946b9
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/service/impl/MultiModelYoloService.java
@@ -0,0 +1,147 @@
+package com.dite.znpt.service.impl;
+
+import ai.onnxruntime.OnnxTensor;
+import ai.onnxruntime.OrtEnvironment;
+import ai.onnxruntime.OrtException;
+import ai.onnxruntime.OrtSession;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.StrUtil;
+import com.dite.znpt.config.YoloModelRegistry;
+import com.dite.znpt.domain.bo.Detection;
+import com.dite.znpt.domain.bo.Letterbox;
+import com.dite.znpt.domain.bo.ODConfig;
+import com.dite.znpt.domain.entity.ModelConfigEntity;
+import com.dite.znpt.util.ModelUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.opencv.core.Mat;
+import org.opencv.core.Point;
+import org.opencv.core.Scalar;
+import org.opencv.imgcodecs.Imgcodecs;
+import org.opencv.imgproc.Imgproc;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.File;
+import java.nio.FloatBuffer;
+import java.util.*;
+
+@Slf4j
+@Service
+public class MultiModelYoloService {
+
+ @Autowired
+ private YoloModelRegistry registry;
+
+ public List detect(String modelId, String inputFile, String outputFile, Float confThreshold) throws OrtException {
+ OrtSession session = registry.getSession(modelId);
+ YoloModelRegistry.ModelMetadata meta = registry.getMetadata(modelId);
+ OrtEnvironment environment = registry.getEnvironment();
+ ModelConfigEntity modelConfig = registry.getModelConfig(modelId);
+ confThreshold = (Objects.isNull(confThreshold) ? modelConfig.getConfThreshold() : confThreshold) / 100;
+
+ Mat img = Imgcodecs.imread(inputFile);
+ Mat image = img.clone();
+ Imgproc.cvtColor(image, image, Imgproc.COLOR_BGR2RGB);
+
+ // 在这里先定义下框的粗细、字的大小、字的类型、字的颜色(按比例设置大小粗细比较好一些)
+ int minDwDh = Math.min(img.width(), img.height());
+ int thickness = minDwDh/ ODConfig.lineThicknessRatio;
+ long start_time = System.currentTimeMillis();
+ // 更改 image 尺寸
+ Letterbox letterbox = new Letterbox();
+ image = letterbox.letterbox(image);
+
+ double ratio = letterbox.getRatio();
+ double dw = letterbox.getDw();
+ double dh = letterbox.getDh();
+ int rows = letterbox.getHeight();
+ int cols = letterbox.getWidth();
+ int channels = image.channels();
+
+ // 将Mat对象的像素值赋值给Float[]对象
+ float[] pixels = new float[channels * rows * cols];
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < cols; j++) {
+ double[] pixel = image.get(j,i);
+ for (int k = 0; k < channels; k++) {
+ // 这样设置相当于同时做了image.transpose((2, 0, 1))操作
+ pixels[rows*cols*k+j*cols+i] = (float) pixel[k]/255.0f;
+ }
+ }
+ }
+
+ // 创建OnnxTensor对象
+ long[] shape = { 1L, (long)channels, (long)rows, (long)cols };
+ OnnxTensor tensor = OnnxTensor.createTensor(environment, FloatBuffer.wrap(pixels), shape);
+ HashMap stringOnnxTensorHashMap = new HashMap<>();
+ stringOnnxTensorHashMap.put(session.getInputInfo().keySet().iterator().next(), tensor);
+
+ // 运行推理
+ OrtSession.Result output = session.run(stringOnnxTensorHashMap);
+ float[][] outputData = ((float[][][])output.get(0).getValue())[0];
+
+ outputData = ModelUtil.transposeMatrix(outputData);
+ Map> class2Bbox = new HashMap<>();
+
+ for (float[] bbox : outputData) {
+ float[] conditionalProbabilities = Arrays.copyOfRange(bbox, 4, bbox.length);
+ int label = ModelUtil.argmax(conditionalProbabilities);
+ float conf = conditionalProbabilities[label];
+ if (conf < confThreshold) continue;
+
+ bbox[4] = conf;
+
+ // xywh to (x1, y1, x2, y2)
+ ModelUtil.xywh2xyxy(bbox);
+
+ // skip invalid predictions
+ if (bbox[0] >= bbox[2] || bbox[1] >= bbox[3]) continue;
+
+ class2Bbox.putIfAbsent(label, new ArrayList<>());
+ class2Bbox.get(label).add(bbox);
+ }
+
+ List detections = new ArrayList<>();
+ for (Map.Entry> entry : class2Bbox.entrySet()) {
+ int label = entry.getKey();
+ List bboxes = entry.getValue();
+ bboxes = ModelUtil.nonMaxSuppression(bboxes, modelConfig.getNmsThreshold());
+ for (float[] bbox : bboxes) {
+ String labelString = meta.getLabels()[label];
+ detections.add(new Detection(labelString,entry.getKey(), Arrays.copyOfRange(bbox, 0, 4), bbox[4]));
+ }
+ }
+
+// 打印检测结果并将图片写入指定目录
+ for (Detection detection : detections) {
+ float[] bbox = detection.getBbox();
+ log.info(detection.toString());
+ // 画框
+ Point topLeft = new Point((bbox[0]-dw)/ratio, (bbox[1]-dh)/ratio);
+ Point bottomRight = new Point((bbox[2]-dw)/ratio, (bbox[3]-dh)/ratio);
+ Scalar color = new Scalar(meta.getColors().get(detection.getClsId()));
+ Imgproc.rectangle(img, topLeft, bottomRight, color, thickness);
+ // 框上写文字
+ Point boxNameLoc = new Point((bbox[0]-dw)/ratio, (bbox[1]-dh-2)/ratio-3);
+
+ Imgproc.putText(img, detection.getLabel(), boxNameLoc, Imgproc.FONT_HERSHEY_SIMPLEX, 2.5, color, thickness);
+ }
+ log.info("检测{},发现{}处问题,耗时:{} ms.", inputFile, detections.size(), (System.currentTimeMillis() - start_time));
+
+ // 保存图像到输出目录
+ Imgcodecs.imwrite(outputFile, img);
+ return detections;
+ }
+
+ public void runFolderDetection(String modelId, String inputFolder, String outputFolder, Float confThreshold) throws Exception {
+ List fileList = FileUtil.loopFiles(inputFolder, file -> {
+ String extName = FileUtil.extName(file);
+ return StrUtil.equalsAnyIgnoreCase(extName, "jpg", "png");
+ });
+ for (File file : fileList) {
+ List detections = detect(modelId, file.getAbsolutePath(), outputFolder+ FileUtil.FILE_SEPARATOR+FileUtil.getName(file), confThreshold);
+ }
+ }
+
+}
+
diff --git a/core/src/main/java/com/dite/znpt/service/impl/PartServiceImpl.java b/core/src/main/java/com/dite/znpt/service/impl/PartServiceImpl.java
index f4df3a7..1e5ba8c 100644
--- a/core/src/main/java/com/dite/znpt/service/impl/PartServiceImpl.java
+++ b/core/src/main/java/com/dite/znpt/service/impl/PartServiceImpl.java
@@ -17,7 +17,6 @@ import com.dite.znpt.domain.vo.PartResp;
import com.dite.znpt.enums.PartTypeEnum;
import com.dite.znpt.exception.ServiceException;
import com.dite.znpt.mapper.PartMapper;
-import com.dite.znpt.mapper.TurbineMapper;
import com.dite.znpt.service.PartService;
import com.dite.znpt.service.ProjectService;
import com.dite.znpt.service.TurbineService;
@@ -35,7 +34,7 @@ import java.util.stream.Collectors;
/**
* @author huise23
* @date 2025/04/11 23:17
- * @Description: 表服务实现类
+ * @Description: 部件表服务实现类
*/
@Service
@RequiredArgsConstructor
diff --git a/core/src/main/java/com/dite/znpt/service/impl/ProjectTaskServiceImpl.java b/core/src/main/java/com/dite/znpt/service/impl/ProjectTaskServiceImpl.java
index d734aee..8c26691 100644
--- a/core/src/main/java/com/dite/znpt/service/impl/ProjectTaskServiceImpl.java
+++ b/core/src/main/java/com/dite/znpt/service/impl/ProjectTaskServiceImpl.java
@@ -1,8 +1,8 @@
package com.dite.znpt.service.impl;
import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
-import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -10,20 +10,19 @@ import com.dite.znpt.constant.Message;
import com.dite.znpt.domain.entity.AttachInfoEntity;
import com.dite.znpt.domain.entity.ProjectTaskEntity;
import com.dite.znpt.domain.vo.ProjectTaskListReq;
-import com.dite.znpt.domain.vo.ProjectTaskResp;
import com.dite.znpt.domain.vo.ProjectTaskReq;
+import com.dite.znpt.domain.vo.ProjectTaskResp;
import com.dite.znpt.domain.vo.ProjectTaskStartReq;
import com.dite.znpt.enums.AttachBusinessTypeEnum;
import com.dite.znpt.enums.ProjectTaskStateEnum;
import com.dite.znpt.exception.ServiceException;
import com.dite.znpt.mapper.ProjectTaskGroupMapper;
+import com.dite.znpt.mapper.ProjectTaskMapper;
import com.dite.znpt.service.AttachInfoService;
import com.dite.znpt.service.ProjectTaskService;
-import com.dite.znpt.mapper.ProjectTaskMapper;
-import org.springframework.stereotype.Service;
-import cn.hutool.core.collection.CollUtil;
-import lombok.RequiredArgsConstructor;
import com.dite.znpt.util.PageUtil;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
@@ -82,7 +81,7 @@ public class ProjectTaskServiceImpl extends ServiceImpl list = selectList(projectTaskReq);
- return list.isEmpty() ? CollUtil.getFirst(list) : new ProjectTaskResp();
+ return CollUtil.isNotEmpty(list) ? CollUtil.getFirst(list) : new ProjectTaskResp();
}
/**
diff --git a/core/src/main/java/com/dite/znpt/service/impl/VideoFileInfoServiceImpl.java b/core/src/main/java/com/dite/znpt/service/impl/VideoFileInfoServiceImpl.java
index a93ee3c..fbf09f4 100644
--- a/core/src/main/java/com/dite/znpt/service/impl/VideoFileInfoServiceImpl.java
+++ b/core/src/main/java/com/dite/znpt/service/impl/VideoFileInfoServiceImpl.java
@@ -1,10 +1,10 @@
package com.dite.znpt.service.impl;
import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.dite.znpt.constant.Constants;
import com.dite.znpt.constant.Message;
import com.dite.znpt.domain.entity.VideoFileInfoEntity;
import com.dite.znpt.domain.vo.VideoFileInfoListReq;
@@ -12,15 +12,13 @@ import com.dite.znpt.domain.vo.VideoFileInfoReq;
import com.dite.znpt.domain.vo.VideoFileInfoResp;
import com.dite.znpt.enums.FilePathEnum;
import com.dite.znpt.exception.ServiceException;
+import com.dite.znpt.mapper.VideoFileInfoMapper;
import com.dite.znpt.service.PartService;
import com.dite.znpt.service.VideoFileInfoService;
-import com.dite.znpt.mapper.VideoFileInfoMapper;
-import lombok.SneakyThrows;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-import cn.hutool.core.collection.CollUtil;
-import lombok.RequiredArgsConstructor;
import com.dite.znpt.util.PageUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
@@ -120,7 +118,7 @@ public class VideoFileInfoServiceImpl extends ServiceImpl= re) {
+ re = a[i];
+ arg = i;
+ }
+ }
+ return arg;
+ }
+
+ public static List nonMaxSuppression(List bboxes, float iouThreshold) {
+
+ List bestBboxes = new ArrayList<>();
+
+ bboxes.sort(Comparator.comparing(a -> a[4]));
+
+ while (!bboxes.isEmpty()) {
+ float[] bestBbox = bboxes.remove(bboxes.size() - 1);
+ bestBboxes.add(bestBbox);
+ bboxes = bboxes.stream().filter(a -> computeIOU(a, bestBbox) < iouThreshold).collect(Collectors.toList());
+ }
+
+ return bestBboxes;
+ }
+
+ public static float computeIOU(float[] box1, float[] box2) {
+
+ float area1 = (box1[2] - box1[0]) * (box1[3] - box1[1]);
+ float area2 = (box2[2] - box2[0]) * (box2[3] - box2[1]);
+
+ float left = Math.max(box1[0], box2[0]);
+ float top = Math.max(box1[1], box2[1]);
+ float right = Math.min(box1[2], box2[2]);
+ float bottom = Math.min(box1[3], box2[3]);
+
+ float interArea = Math.max(right - left, 0) * Math.max(bottom - top, 0);
+ float unionArea = area1 + area2 - interArea;
+ return Math.max(interArea / unionArea, 1e-8f);
+
+ }
+
+}
diff --git a/core/src/main/resources/mapper/ModelConfigMapper.xml b/core/src/main/resources/mapper/ModelConfigMapper.xml
new file mode 100644
index 0000000..385cbab
--- /dev/null
+++ b/core/src/main/resources/mapper/ModelConfigMapper.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+ a.model_id, a.model_name, a.model_path, a.conf_threshold,
+ a.nms_threshold, a.update_by, a.create_time, a.create_by,
+ a.update_time
+
+
+
+
+
diff --git a/web/src/main/java/com/dite/znpt/web/controller/ModelConfigController.java b/web/src/main/java/com/dite/znpt/web/controller/ModelConfigController.java
new file mode 100644
index 0000000..51ceb8a
--- /dev/null
+++ b/web/src/main/java/com/dite/znpt/web/controller/ModelConfigController.java
@@ -0,0 +1,83 @@
+package com.dite.znpt.web.controller;
+
+
+import com.dite.znpt.config.YoloModelRegistry;
+import com.dite.znpt.domain.PageResult;
+import com.dite.znpt.domain.Result;
+import com.dite.znpt.domain.vo.ModelConfigListReq;
+import com.dite.znpt.domain.vo.ModelConfigReq;
+import com.dite.znpt.domain.vo.ModelConfigResp;
+import com.dite.znpt.service.ModelConfigService;
+import com.dite.znpt.service.impl.MultiModelYoloService;
+import com.dite.znpt.util.ValidationGroup;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+
+/**
+ * @author huise23
+ * @date 2025/07/02 21:21
+ */
+@Api(tags = "模型配置")
+@RestController
+@RequestMapping("/model-config")
+public class ModelConfigController {
+ @Resource
+ private ModelConfigService modelConfigService;
+ @Resource
+ private YoloModelRegistry registry;
+ @Autowired
+ private MultiModelYoloService multiModelYoloService;
+
+ @ApiOperation(value = "获取列表", httpMethod = "GET")
+ @GetMapping("/list")
+ public PageResult list(ModelConfigListReq modelConfigReq) {
+ return PageResult.ok(modelConfigService.selectList(modelConfigReq));
+ }
+
+ @ApiOperation(value = "根据Id获取详细信息", httpMethod = "GET")
+ @GetMapping("/{modelId}")
+ public Result getInfo(@PathVariable String modelId) {
+ return Result.ok(modelConfigService.selectById(modelId));
+ }
+
+ @ApiOperation(value = "新增", httpMethod = "POST")
+ @PostMapping
+ public Result