diff --git a/core/src/main/java/com/dite/znpt/config/TaskConfig.java b/core/src/main/java/com/dite/znpt/config/TaskConfig.java
new file mode 100644
index 0000000..2726c63
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/config/TaskConfig.java
@@ -0,0 +1,24 @@
+package com.dite.znpt.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+/**
+ * @author hedechao
+ * @date 2025/8/11 11:02
+ * @Description: 形变配置线程
+ */
+@Configuration
+public class TaskConfig {
+ @Bean("clearanceExecutor") // 方法返回的对象会被注册成 Bean,名字叫 clearanceExecutor
+ public ThreadPoolTaskExecutor clearanceExecutor() {
+ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+ executor.setCorePoolSize(2); // 核心线程数
+ executor.setMaxPoolSize(4); // 最大线程数
+ executor.setQueueCapacity(100); // 队列容量
+ executor.setThreadNamePrefix("clearance-"); // 线程名前缀
+ executor.initialize(); // 初始化
+ return executor;
+ }
+}
diff --git a/core/src/main/java/com/dite/znpt/domain/entity/ProjectEntity.java b/core/src/main/java/com/dite/znpt/domain/entity/ProjectEntity.java
index 943620b..875b6c3 100644
--- a/core/src/main/java/com/dite/znpt/domain/entity/ProjectEntity.java
+++ b/core/src/main/java/com/dite/znpt/domain/entity/ProjectEntity.java
@@ -89,7 +89,7 @@ public class ProjectEntity extends AuditableEntity implements Serializable {
@ExcelProperty("项目规模")
@ApiModelProperty("项目规模")
@TableField("scale")
- private String scale;
+ private Integer scale;
@ExcelProperty("总工期,单位天")
@ApiModelProperty("总工期,单位天")
diff --git a/core/src/main/java/com/dite/znpt/domain/entity/ProjectTaskEntity.java b/core/src/main/java/com/dite/znpt/domain/entity/ProjectTaskEntity.java
index e2a25e8..2776261 100644
--- a/core/src/main/java/com/dite/znpt/domain/entity/ProjectTaskEntity.java
+++ b/core/src/main/java/com/dite/znpt/domain/entity/ProjectTaskEntity.java
@@ -85,5 +85,13 @@ public class ProjectTaskEntity extends AuditableEntity implements Serializable {
@ApiModelProperty("项目id")
@TableField("project_id")
private String projectId;
+
+ @ApiModelProperty("任务规模")
+ @TableField("scales")
+ private Integer scales;
+
+ @ApiModelProperty("已完成数")
+ @TableField("finished")
+ private Integer finished;
}
diff --git a/core/src/main/java/com/dite/znpt/domain/entity/VideoMonitorEntity.java b/core/src/main/java/com/dite/znpt/domain/entity/VideoMonitorEntity.java
new file mode 100644
index 0000000..d289d9a
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/domain/entity/VideoMonitorEntity.java
@@ -0,0 +1,170 @@
+package com.dite.znpt.domain.entity;
+
+import cn.hutool.json.JSONObject;
+import com.baomidou.mybatisplus.annotation.FieldFill;
+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.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.io.Serializable;
+import java.util.Date;
+import lombok.Data;
+
+/**
+ * 视频监测信息
+ */
+@ApiModel(description="视频监测信息")
+@Schema(description="视频监测信息")
+@Data
+@TableName(value = "video_monitor")
+public class VideoMonitorEntity implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 视频id
+ */
+ @TableId(value = "video_id", type = IdType.ASSIGN_UUID)
+ @ApiModelProperty(value="视频id")
+ @Schema(description="视频id")
+ private String videoId;
+
+ /**
+ * 项目id
+ */
+ @TableField(value = "project_id")
+ @ApiModelProperty(value="项目id")
+ @Schema(description="项目id")
+ private String projectId;
+
+ /**
+ * 机组id
+ */
+ @TableField(value = "turbine_id")
+ @ApiModelProperty(value="机组id")
+ @Schema(description="机组id")
+ private String turbineId;
+
+ /**
+ * 视频名称
+ */
+ @TableField(value = "video_name")
+ @ApiModelProperty(value="视频名称")
+ @Schema(description="视频名称")
+ private String videoName;
+
+ /**
+ * 视频路径
+ */
+ @TableField(value = "video_path")
+ @ApiModelProperty(value="视频路径")
+ @Schema(description="视频路径")
+ private String videoPath;
+
+ /**
+ * 0 正常 1 已删除
+ */
+ @TableField(value = "is_deleted")
+ @ApiModelProperty(value="0 正常 1 已删除")
+ @Schema(description="0 正常 1 已删除")
+ private Boolean isDeleted;
+
+ /**
+ * 0 待审核 1 已上线 2 下线
+ */
+ @TableField(value = "`status`")
+ @ApiModelProperty(value="0 待审核 1 已上线 2 下线")
+ @Schema(description="0 待审核 1 已上线 2 下线")
+ private Byte status;
+
+ /**
+ * 预处理后的视频路径
+ */
+ @TableField(value = "pre_image_path")
+ @ApiModelProperty(value="预处理后的视频路径")
+ @Schema(description="预处理后的视频路径")
+ private String preImagePath;
+
+ /**
+ * 是否处理,默认0
+ */
+ @TableField(value = "pre_treatment")
+ @ApiModelProperty(value="是否处理,默认0")
+ @Schema(description="是否处理,默认0")
+ private Boolean preTreatment;
+
+ /**
+ * 修改人
+ */
+ @TableField(value = "update_by")
+ @ApiModelProperty(value="修改人")
+ @Schema(description="修改人")
+ private String updateBy;
+
+ /**
+ * 创建时间
+ */
+ @TableField(value = "create_time",fill = FieldFill.INSERT)
+ @ApiModelProperty(value="创建时间")
+ @Schema(description="创建时间")
+ private Date createTime;
+
+ /**
+ * 创建人
+ */
+ @TableField(value = "create_by")
+ @ApiModelProperty(value="创建人")
+ @Schema(description="创建人")
+ private String createBy;
+
+ /**
+ * 修改时间
+ */
+ @TableField(value = "update_time",fill = FieldFill.INSERT_UPDATE)
+ @ApiModelProperty(value="修改时间")
+ @Schema(description="修改时间")
+ private Date updateTime;
+
+ /**
+ * 风速
+ */
+ @TableField(value = "wind_speed")
+ @ApiModelProperty(value="风速")
+ @Schema(description="风速")
+ private String windSpeed;
+
+ /**
+ * 转速
+ */
+ @TableField(value = "rpm")
+ @ApiModelProperty(value="转速")
+ @Schema(description="转速")
+ private String rpm;
+
+ /**
+ * 检测类型
+ */
+ @TableField(value = "`type`")
+ @ApiModelProperty(value="检测类型")
+ @Schema(description="检测类型")
+ private String type;
+
+ /**
+ * 业务扩展字段
+ */
+ @TableField(value = "extra",typeHandler = JacksonTypeHandler.class)
+ @ApiModelProperty(value="业务扩展字段")
+ @Schema(description="业务扩展字段")
+ private JSONObject extra;
+
+ /**
+ * 上传时间
+ */
+ @TableField(value = "upload_time")
+ @ApiModelProperty(value="上传时间")
+ @Schema(description="上传时间")
+ private Date uploadTime;
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/dite/znpt/domain/vo/ProjectInitTaskReq.java b/core/src/main/java/com/dite/znpt/domain/vo/ProjectInitTaskReq.java
index e1ef86f..7ec32f1 100644
--- a/core/src/main/java/com/dite/znpt/domain/vo/ProjectInitTaskReq.java
+++ b/core/src/main/java/com/dite/znpt/domain/vo/ProjectInitTaskReq.java
@@ -38,4 +38,7 @@ public class ProjectInitTaskReq {
@Size(groups = {ValidationGroup.Insert.class, ValidationGroup.Update.class}, max = 100, message = "任务负责人id长度不能超过100字符")
@ApiModelProperty("任务负责人id")
private String mainUserId;
+
+ @ApiModelProperty("任务规模")
+ private Integer scales;
}
diff --git a/core/src/main/java/com/dite/znpt/domain/vo/VideoReq.java b/core/src/main/java/com/dite/znpt/domain/vo/VideoReq.java
new file mode 100644
index 0000000..1b146e2
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/domain/vo/VideoReq.java
@@ -0,0 +1,29 @@
+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 hedechao
+ * @date 2025/8/8 09:10
+ * @Description:
+ */
+@Data
+@ApiModel("视频列表查询实体")
+public class VideoReq implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 771014582625089979L;
+ @ApiModelProperty("项目id")
+ private String projectId;
+ @ApiModelProperty("视频类型")
+ private String[] type;
+ @ApiModelProperty("机组id")
+ private String turbineId;
+ @ApiModelProperty("是否已审核,0未审核,1已审核")
+ private Boolean State;
+}
+
diff --git a/core/src/main/java/com/dite/znpt/enums/VideoMonitorEnum.java b/core/src/main/java/com/dite/znpt/enums/VideoMonitorEnum.java
new file mode 100644
index 0000000..4149417
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/enums/VideoMonitorEnum.java
@@ -0,0 +1,45 @@
+package com.dite.znpt.enums;
+
+import cn.hutool.json.JSONObject;
+import lombok.Getter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Getter
+public enum VideoMonitorEnum {
+ CLEARANCE("clearance","净空监测"),
+ DEFORMATION("deformation","形变监测");
+
+ private final String code;
+ private final String desc;
+
+ VideoMonitorEnum(String code, String desc) {
+ this.code = code;
+ this.desc = desc;
+ }
+
+ public static VideoMonitorEnum getByCode(String code) {
+ for (VideoMonitorEnum e : VideoMonitorEnum.values()) {
+ if (e.code.equals(code)) {
+ return e;
+ }
+ }
+ return null;
+ }
+
+ public static String getDescByCode(String code) {
+ VideoMonitorEnum e = getByCode(code);
+ return null == e ? null : e.desc;
+ }
+
+ public static List listAll() {
+ List list = new ArrayList<>(UserStatusEnum.values().length);
+ for (VideoMonitorEnum e : VideoMonitorEnum.values()) {
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.set(e.code, e.desc);
+ list.add(jsonObject);
+ }
+ return list;
+ }
+}
diff --git a/core/src/main/java/com/dite/znpt/mapper/VideoMonitorEntityMapper.java b/core/src/main/java/com/dite/znpt/mapper/VideoMonitorEntityMapper.java
new file mode 100644
index 0000000..831e9a3
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/mapper/VideoMonitorEntityMapper.java
@@ -0,0 +1,12 @@
+package com.dite.znpt.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.dite.znpt.domain.entity.VideoMonitorEntity;
+import java.util.List;
+import org.apache.ibatis.annotations.Param;
+
+public interface VideoMonitorEntityMapper extends BaseMapper {
+ List selectAllByProjectIdAndPartId(@Param("projectId") String projectId, @Param("partId") String partId);
+
+ int batchInsert(@Param("list") List list);
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/dite/znpt/service/VideoMonitorService.java b/core/src/main/java/com/dite/znpt/service/VideoMonitorService.java
new file mode 100644
index 0000000..6ef70bb
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/service/VideoMonitorService.java
@@ -0,0 +1,60 @@
+package com.dite.znpt.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.dite.znpt.domain.PageResult;
+import com.dite.znpt.domain.entity.VideoMonitorEntity;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ *
+ * 视频信息 服务类
+ *
+ *
+ * @author hdc
+ * @since 2025-08-07
+ */
+public interface VideoMonitorService extends IService {
+ /**
+ * 批量上传视频
+ */
+ List uploadBatch(String projectId,
+ String partId,
+ String type,
+ MultipartFile[] files) throws IOException;
+
+ /**
+ * 单文件上传
+ */
+ VideoMonitorEntity upload(String projectId,
+ String partId,
+ String type,
+ MultipartFile file) throws IOException;
+
+ /**
+ * 分页列表
+ */
+ PageResult page(Integer pageNo,
+ Integer pageSize,
+ String projectId,
+ String partId);
+
+ /**
+ * 列表
+ */
+ List list(String projectId, String partId);
+
+ /**
+ * 删除
+ */
+ void delete(String videoId);
+
+ /**
+ * 下载/播放
+ */
+ void download(String videoId, HttpServletResponse response) throws IOException;
+
+}
diff --git a/core/src/main/java/com/dite/znpt/service/impl/ClearanceTask.java b/core/src/main/java/com/dite/znpt/service/impl/ClearanceTask.java
new file mode 100644
index 0000000..1990405
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/service/impl/ClearanceTask.java
@@ -0,0 +1,51 @@
+package com.dite.znpt.service.impl;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.dite.znpt.domain.entity.VideoMonitorEntity;
+import com.dite.znpt.enums.FilePathEnum;
+import com.dite.znpt.util.PythonUtil;
+import lombok.RequiredArgsConstructor;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+
+@RequiredArgsConstructor
+public class ClearanceTask implements Runnable {
+
+ private final String videoAbsolutePath; // 上传后的完整磁盘路径
+ private final String outputDir; // 结果目录
+ private final String videoId; // 数据库主键,用于更新状态
+ private final VideoMonitorServiceImpl service;
+
+ @Override
+ public void run() {
+ try {
+ // 1. 调用 Python(阻塞,但跑在子线程)
+ PythonUtil.runClearance(videoAbsolutePath,outputDir);
+ // 2. 更新数据库:status = 已完成 / 预处理成功
+ VideoMonitorEntity update = new VideoMonitorEntity();
+ update.setVideoId(videoId);
+ update.setPreTreatment(true); // 或自定义状态字段
+ update.setPreImagePath(FilePathEnum.VIDEO.getFileDownPath(outputDir));
+ File resultFile = new File(outputDir, "results.json");
+ if (!resultFile.exists()) {
+ throw new IllegalStateException("results.json 不存在");
+ }
+ String jsonStr = FileUtil.readString(resultFile, StandardCharsets.UTF_8);
+
+ // 3. 转成 hutool 的 JSONObject(对应 MySQL 的 JSON 字段)
+ JSONObject jsonObj = JSONUtil.parseObj(jsonStr);
+ update.setExtra(jsonObj);
+ service.updateById(update);
+
+ } catch (Exception e) {
+ // 失败时可将 status 置为失败
+ VideoMonitorEntity update = new VideoMonitorEntity();
+ update.setVideoId(videoId);
+ update.setStatus((byte) -1); // 自定义失败码
+ service.updateById(update);
+ }
+ }
+}
diff --git a/core/src/main/java/com/dite/znpt/service/impl/ProjectServiceImpl.java b/core/src/main/java/com/dite/znpt/service/impl/ProjectServiceImpl.java
index de2a7e4..38d8b40 100644
--- a/core/src/main/java/com/dite/znpt/service/impl/ProjectServiceImpl.java
+++ b/core/src/main/java/com/dite/znpt/service/impl/ProjectServiceImpl.java
@@ -137,7 +137,11 @@ public class ProjectServiceImpl extends ServiceImpl
+ * 视频信息 服务实现类
+ *
+ *
+ * @author hdc
+ * @since 2025-08-07
+ */
+@Service
+public class VideoMonitorServiceImpl extends ServiceImpl implements VideoMonitorService {
+ @Resource
+ private TurbineService turbineService;
+ @Autowired
+ private ProjectService projectService;
+ @Resource(name = "clearanceExecutor")
+ private ThreadPoolTaskExecutor clearanceExecutor;
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public List uploadBatch(String projectId,
+ String turbineId,
+ String type,
+ MultipartFile[] files) throws IOException {
+ if (files == null || files.length == 0) {
+ throw new ServiceException("上传文件为空");
+ }
+ if (Objects.isNull(projectService.detail(projectId))) {
+ throw new ServiceException(Message.PROJECT_ID_IS_NOT_EXIST);
+ }
+ if (StrUtil.isNotBlank(turbineId) && turbineService.getById(turbineId) == null) {
+ throw new ServiceException(Message.PART_ID_IS_NOT_EXIST);
+ }
+ String userId = StpUtil.getLoginIdAsString();
+ String dateStr = DateUtil.today();
+ String storeDir = FilePathEnum.VIDEO.getFileAbsolutePathPrefix()
+ + projectId;
+ if (!turbineId.isEmpty() )
+ storeDir+=File.separator+turbineId+ File.separator + dateStr;
+ else
+ storeDir+=File.separator + dateStr;
+
+ FileUtil.mkdir(storeDir);
+
+ List list = Lists.newArrayList();
+
+ for (MultipartFile file : files) {
+ String original = file.getOriginalFilename();
+ String suffix = FileUtil.extName(original);
+ if (suffix != null && !suffix.equals("mp4")) throw new ServerException("非视频文件");
+ String uuid = IdUtil.simpleUUID();
+ String fileName = uuid + StrUtil.DOT + suffix;
+ String absolutePath = storeDir + File.separator + fileName;
+
+ File dest = new File(absolutePath);
+ file.transferTo(dest);
+
+ VideoMonitorEntity entity = new VideoMonitorEntity();
+ entity.setVideoId(uuid);
+ entity.setProjectId(projectId);
+ entity.setTurbineId(turbineId);
+ entity.setVideoName(original);
+ entity.setVideoPath(FilePathEnum.VIDEO.getFileDownPath(absolutePath));
+ entity.setType(type);
+ entity.setStatus((byte) 0); // 待审核
+ entity.setPreTreatment(false);
+ entity.setIsDeleted(false);
+ entity.setCreateTime(new Date());
+ entity.setUploadTime(new Date());
+ entity.setUpdateBy(userId);
+ entity.setCreateBy(userId);
+ list.add(entity);
+ if (type.equals(VideoMonitorEnum.CLEARANCE.getCode()))
+ clearanceExecutor.execute(
+ new ClearanceTask(absolutePath, storeDir+File.separator+ type+File.separator+uuid, uuid, this)
+ );
+ else if (type.equals(VideoMonitorEnum.DEFORMATION.getCode())) {
+ //TODO
+ }
+ }
+
+ saveBatch(list);
+ return list;
+ }
+
+ @Override
+ public VideoMonitorEntity upload(String projectId,
+ String partId,
+ String type,
+ MultipartFile file) throws IOException {
+ return uploadBatch(projectId, partId, type, new MultipartFile[]{file}).get(0);
+ }
+
+ @Override
+ public PageResult page(Integer pageNo,
+ Integer pageSize,
+ String projectId,
+ String partId) {
+ LambdaQueryWrapper wrapper = Wrappers.lambdaQuery();
+ wrapper.eq(StrUtil.isNotBlank(projectId),VideoMonitorEntity::getProjectId, projectId)
+ .eq(StrUtil.isNotBlank(partId), VideoMonitorEntity::getTurbineId, partId)
+ .orderByDesc(VideoMonitorEntity::getCreateTime);
+ Page page = page(Page.of(pageNo, pageSize), wrapper);
+ return PageResult.ok(page.getRecords(), page.getTotal());
+ }
+
+ @Override
+ public List list(String projectId, String partId) {
+ return lambdaQuery()
+ .eq(StrUtil.isNotBlank(projectId),VideoMonitorEntity::getProjectId, projectId)
+ .eq(StrUtil.isNotBlank(partId), VideoMonitorEntity::getTurbineId, partId)
+ .list();
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void delete(String videoId) {
+ VideoMonitorEntity entity = getById(videoId);
+ if (Objects.isNull(entity)) {
+ throw new ServiceException("视频不存在");
+ }
+ entity.setIsDeleted(true);
+ entity.setUpdateBy(StpUtil.getLoginIdAsString());
+ updateById(entity);
+ // 物理删除文件
+ FileUtil.del(FilePathEnum.VIDEO.getFileAbsolutePath(entity.getVideoPath()));
+ }
+
+ @Override
+ public void download(String videoId, HttpServletResponse response) throws IOException {
+ VideoMonitorEntity entity = getById(videoId);
+ if (entity == null || Boolean.TRUE.equals(entity.getIsDeleted())) {
+ throw new ServiceException("视频不存在或已删除");
+ }
+ File file = new File(FilePathEnum.VIDEO.getFileAbsolutePath(entity.getVideoPath()));
+ if (!file.exists()) {
+ throw new ServiceException("视频文件不存在");
+ }
+ response.setContentType("video/mp4");
+ response.setHeader("Content-Disposition",
+ "inline; filename=" + URLEncoder.encode(entity.getVideoName(), StandardCharsets.UTF_8));
+ FileUtil.writeToStream(file, response.getOutputStream());
+ }
+}
diff --git a/core/src/main/java/com/dite/znpt/util/PythonUtil.java b/core/src/main/java/com/dite/znpt/util/PythonUtil.java
new file mode 100644
index 0000000..a4329ea
--- /dev/null
+++ b/core/src/main/java/com/dite/znpt/util/PythonUtil.java
@@ -0,0 +1,49 @@
+package com.dite.znpt.util;
+
+import cn.hutool.extra.spring.SpringUtil;
+import org.springframework.core.env.Environment;
+
+import java.io.IOException;
+
+/**
+ * @author hedechao
+ * @Date 2025/8/11 09:04
+ * @Description: 形变python脚本执行工具
+ */
+public class PythonUtil {
+ /**
+ * 调用叶片净空计算脚本
+ *
+ * @param videoPath 待检测视频路径
+ * @throws IOException 如果启动进程失败
+ * @throws InterruptedException 如果等待进程完成被中断
+ */
+ public static void runClearance(
+ String videoPath,
+ String outputPath
+ ) throws IOException, InterruptedException {
+ String pyScriptPath=SpringUtil.getBean(Environment.class).getProperty("pyScript.clearance");
+ String modelPath=SpringUtil.getBean(Environment.class).getProperty("model.tip-hub");
+ // 1. 构造命令
+ ProcessBuilder pb = new ProcessBuilder(
+ "python", // 也可以是 python3 / 绝对路径
+ pyScriptPath,
+ "--video_path=" + videoPath,
+ "--model_path=" + modelPath,
+ "--output_dir=" + outputPath
+ );
+
+ // 2. 把子进程的标准输出 / 错误流重定向到 Java 控制台(可选)
+ pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
+ pb.redirectError(ProcessBuilder.Redirect.INHERIT);
+
+ // 3. 启动进程并等待完成
+ Process process = pb.start();
+ int exitCode = process.waitFor();
+ if (exitCode != 0) {
+ throw new RuntimeException("脚本返回非 0 状态码: " + exitCode);
+ }
+
+ System.out.println("净空计算完成,结果已保存到: " + outputPath);
+ }
+}
diff --git a/core/src/main/resources/mapper/VideoMonitorMapper.xml b/core/src/main/resources/mapper/VideoMonitorMapper.xml
new file mode 100644
index 0000000..37a7f78
--- /dev/null
+++ b/core/src/main/resources/mapper/VideoMonitorMapper.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ video_id, project_id, part_id, video_name, video_path, is_deleted, `status`, pre_image_path,
+ pre_treatment, update_by, create_time, create_by, update_time, wind_speed, rpm, `type`,
+ extra, upload_time
+
+
+
+
+ insert into video_monitor
+ (project_id, part_id, video_name, video_path, is_deleted, `status`, pre_image_path,
+ pre_treatment, update_by, create_time, create_by, update_time, wind_speed, rpm,
+ `type`, extra, upload_time)
+ values
+
+ (#{item.projectId}, #{item.turbineId}, #{item.videoName}, #{item.videoPath}, #{item.isDeleted},
+ #{item.status}, #{item.preImagePath}, #{item.preTreatment}, #{item.updateBy}, #{item.createTime},
+ #{item.createBy}, #{item.updateTime}, #{item.windSpeed}, #{item.rpm}, #{item.type},
+ #{item.extra}, #{item.uploadTime})
+
+
+
\ No newline at end of file
diff --git a/web/src/main/java/com/dite/znpt/web/controller/ProjectController.java b/web/src/main/java/com/dite/znpt/web/controller/ProjectController.java
index 4136c4b..c7e1806 100644
--- a/web/src/main/java/com/dite/znpt/web/controller/ProjectController.java
+++ b/web/src/main/java/com/dite/znpt/web/controller/ProjectController.java
@@ -4,9 +4,11 @@ package com.dite.znpt.web.controller;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dite.znpt.constant.Constants;
+import com.dite.znpt.domain.entity.ContractEntity;
import com.dite.znpt.domain.entity.EquipmentEntity;
import com.dite.znpt.domain.vo.*;
import com.dite.znpt.domain.entity.ProjectEntity;
+import com.dite.znpt.service.ContractService;
import com.dite.znpt.service.EquipmentService;
import com.dite.znpt.service.ProjectService;
import com.dite.znpt.domain.Result;
@@ -38,6 +40,9 @@ public class ProjectController {
@Resource
private EquipmentService equipmentService;
+ @Resource
+ private ContractService contractService;
+
@ApiOperation(value = "分页查询项目信息列表", httpMethod = "GET")
@GetMapping("/page")
public PageResult page(ProjectListReq req) {
@@ -112,4 +117,13 @@ public class ProjectController {
queryWrapper.eq("project_id", projectId);
return Result.ok(equipmentService.list(queryWrapper));
}
+
+ @ApiOperation(value = "查询未立项的合同列表", httpMethod = "GET")
+ @GetMapping("/contracts")
+ public Result> contracts() {
+ QueryWrapper queryWrapper = new QueryWrapper<>();
+ queryWrapper.isNull("project_id");
+ return Result.ok(contractService.list(queryWrapper));
+ }
+
}
\ No newline at end of file
diff --git a/web/src/main/java/com/dite/znpt/web/controller/ProjectTaskController.java b/web/src/main/java/com/dite/znpt/web/controller/ProjectTaskController.java
index 1bf92ce..149c905 100644
--- a/web/src/main/java/com/dite/znpt/web/controller/ProjectTaskController.java
+++ b/web/src/main/java/com/dite/znpt/web/controller/ProjectTaskController.java
@@ -118,10 +118,14 @@ public class ProjectTaskController {
@GetMapping("/{projectId}/tasks")
public Result getTaskByProjectId(@PathVariable String projectId) {
ProjectTasksDetailResp resp = new ProjectTasksDetailResp();
+ resp.setTotal(0);
+ resp.setFinished(0);
resp.setProjectName(projectService.getById(projectId).getProjectName());
resp.setList(projectTaskService.getTaskByProjectId(projectId));
- resp.setTotal(resp.getList().size());
- resp.setFinished((int) resp.getList().stream().filter(projectTaskResp -> projectTaskResp.getStatus() == 2).count());
+ for (ProjectTaskResp projectTaskResp : resp.getList()) {
+ resp.setTotal(resp.getTotal() + projectTaskResp.getScales());
+ resp.setFinished(resp.getFinished() + projectTaskResp.getFinished());
+ }
return Result.ok(resp);
}
}
\ No newline at end of file
diff --git a/web/src/main/java/com/dite/znpt/web/controller/VideoMonitorController.java b/web/src/main/java/com/dite/znpt/web/controller/VideoMonitorController.java
new file mode 100644
index 0000000..845fb61
--- /dev/null
+++ b/web/src/main/java/com/dite/znpt/web/controller/VideoMonitorController.java
@@ -0,0 +1,77 @@
+package com.dite.znpt.web.controller;
+
+import com.dite.znpt.domain.PageResult;
+import com.dite.znpt.domain.Result;
+import com.dite.znpt.domain.entity.VideoMonitorEntity;
+import com.dite.znpt.service.VideoMonitorService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author hedechao
+ * @date 2025/8/8 17:15
+ * @Description:
+ */
+@Api(tags = "视频监测")
+@RestController
+@RequestMapping("/video-monitor")
+@RequiredArgsConstructor
+public class VideoMonitorController {
+@Resource
+ VideoMonitorService videoService;
+
+ @ApiOperation("分页查询")
+ @GetMapping("/page")
+ public PageResult page(@RequestParam(defaultValue = "1") Integer pageNo,
+ @RequestParam(defaultValue = "10") Integer pageSize,
+ @RequestParam String projectId,
+ @RequestParam(required = false) String turbineId) {
+ return videoService.page(pageNo, pageSize, projectId, turbineId);
+ }
+
+ @ApiOperation("列表查询")
+ @GetMapping("/list")
+ public Result> list(@RequestParam(required = false) String projectId,
+ @RequestParam(required = false) String turbineId) {
+ return Result.ok(videoService.list(projectId, turbineId));
+ }
+
+ @ApiOperation("批量上传")
+ @PostMapping("/{projectId}/upload-batch")
+ public Result> uploadBatch(@PathVariable String projectId,
+ @RequestParam(required = false) String turbineId,
+ @RequestParam String type,
+ @RequestParam("files") MultipartFile[] files) throws IOException {
+ return Result.ok(videoService.uploadBatch(projectId, turbineId, type, files));
+ }
+
+ @ApiOperation("单文件上传")
+ @PostMapping("/{projectId}/upload")
+ public Result upload(@PathVariable String projectId,
+ @RequestParam(required = false) String turbineId,
+ @RequestParam String type,
+ @RequestParam("file") MultipartFile file) throws IOException {
+ return Result.ok(videoService.upload(projectId, turbineId, type, file));
+ }
+
+ @ApiOperation("删除")
+ @DeleteMapping("/{videoId}")
+ public Result> delete(@PathVariable String videoId) {
+ videoService.delete(videoId);
+ return Result.ok();
+ }
+
+ @ApiOperation("下载/播放")
+ @GetMapping("/download/{videoId}")
+ public void download(@PathVariable String videoId, HttpServletResponse response) throws IOException {
+ videoService.download(videoId, response);
+ }
+}