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/ContractEntity.java b/core/src/main/java/com/dite/znpt/domain/entity/ContractEntity.java index e33d747..cc70a24 100644 --- a/core/src/main/java/com/dite/znpt/domain/entity/ContractEntity.java +++ b/core/src/main/java/com/dite/znpt/domain/entity/ContractEntity.java @@ -45,10 +45,10 @@ public class ContractEntity extends AuditableEntity implements Serializable { @TableField("code") private String code; - @ExcelProperty("项目id") - @ApiModelProperty("项目id") - @TableField("project_id") - private String projectId; + @ExcelProperty("项目名称") + @ApiModelProperty("项目名称") + @TableField("project_name") + private String projectName; @ExcelProperty("业务员id") @ApiModelProperty("业务员id") 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/ContractListReq.java b/core/src/main/java/com/dite/znpt/domain/vo/ContractListReq.java index facfb38..905971f 100644 --- a/core/src/main/java/com/dite/znpt/domain/vo/ContractListReq.java +++ b/core/src/main/java/com/dite/znpt/domain/vo/ContractListReq.java @@ -32,8 +32,8 @@ public class ContractListReq implements Serializable { @ApiModelProperty("合同编号") private String code; - @ApiModelProperty("项目id") - private String projectId; + @ApiModelProperty("项目名称") + private String projectName; @ApiModelProperty("业务员id") private String salespersonId; diff --git a/core/src/main/java/com/dite/znpt/domain/vo/ContractReq.java b/core/src/main/java/com/dite/znpt/domain/vo/ContractReq.java index e6030cb..06e760a 100644 --- a/core/src/main/java/com/dite/znpt/domain/vo/ContractReq.java +++ b/core/src/main/java/com/dite/znpt/domain/vo/ContractReq.java @@ -31,8 +31,8 @@ public class ContractReq implements Serializable { @ApiModelProperty("合同编号") private String code; - @ApiModelProperty("项目id") - private String projectId; + @ApiModelProperty("项目名称") + private String projectName; @ApiModelProperty("业务员id") private String salespersonId; diff --git a/core/src/main/java/com/dite/znpt/domain/vo/ContractResp.java b/core/src/main/java/com/dite/znpt/domain/vo/ContractResp.java index a1f4ff0..e3c8e0a 100644 --- a/core/src/main/java/com/dite/znpt/domain/vo/ContractResp.java +++ b/core/src/main/java/com/dite/znpt/domain/vo/ContractResp.java @@ -18,9 +18,6 @@ import java.math.BigDecimal; @ApiModel("合同响应实体") public class ContractResp extends ContractEntity { - @ApiModelProperty("项目名称") - private String projectName; - @ApiModelProperty("业务员姓名") private String salespersonName; diff --git a/core/src/main/java/com/dite/znpt/domain/vo/EquipmentApprovalListReq.java b/core/src/main/java/com/dite/znpt/domain/vo/EquipmentApprovalListReq.java index 39b5833..55cae55 100644 --- a/core/src/main/java/com/dite/znpt/domain/vo/EquipmentApprovalListReq.java +++ b/core/src/main/java/com/dite/znpt/domain/vo/EquipmentApprovalListReq.java @@ -43,6 +43,9 @@ public class EquipmentApprovalListReq implements Serializable { @ApiModelProperty("当前页码") private Integer page; + @ApiModelProperty("当前页码 - 与前端保持一致") + private Integer pageNum; + @ApiModelProperty("每页大小") private Integer pageSize; diff --git a/core/src/main/java/com/dite/znpt/domain/vo/GanttChartReq.java b/core/src/main/java/com/dite/znpt/domain/vo/GanttChartReq.java new file mode 100644 index 0000000..7e96367 --- /dev/null +++ b/core/src/main/java/com/dite/znpt/domain/vo/GanttChartReq.java @@ -0,0 +1,45 @@ +package com.dite.znpt.domain.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.time.LocalDate; + +/** + * 甘特图查询请求类 + */ +@Data +@ApiModel(value = "GanttChartReq", description = "甘特图查询请求") +public class GanttChartReq { + + @ApiModelProperty("项目ID") + private String projectId; + + @ApiModelProperty("任务组ID") + private String taskGroupId; + + @ApiModelProperty("任务状态") + private Integer status; + + @ApiModelProperty("负责人ID") + private String mainUserId; + + @ApiModelProperty("开始时间范围-开始") + private LocalDate startDateFrom; + + @ApiModelProperty("开始时间范围-结束") + private LocalDate startDateTo; + + @ApiModelProperty("结束时间范围-开始") + private LocalDate endDateFrom; + + @ApiModelProperty("结束时间范围-结束") + private LocalDate endDateTo; + + @ApiModelProperty("是否包含已完成任务") + private Boolean includeCompleted = true; + + @ApiModelProperty("是否只显示逾期任务") + private Boolean overdueOnly = false; +} diff --git a/core/src/main/java/com/dite/znpt/domain/vo/GanttChartResp.java b/core/src/main/java/com/dite/znpt/domain/vo/GanttChartResp.java new file mode 100644 index 0000000..69e38a0 --- /dev/null +++ b/core/src/main/java/com/dite/znpt/domain/vo/GanttChartResp.java @@ -0,0 +1,91 @@ +package com.dite.znpt.domain.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.time.LocalDate; +import java.util.List; + +/** + * 甘特图数据响应类 + */ +@Data +@ApiModel(value = "GanttChartResp", description = "甘特图数据响应") +public class GanttChartResp { + + @ApiModelProperty("任务ID") + private String taskId; + + @ApiModelProperty("任务名称") + private String taskName; + + @ApiModelProperty("任务编号") + private String taskCode; + + @ApiModelProperty("上级任务ID") + private String parentTaskId; + + @ApiModelProperty("任务组ID") + private String taskGroupId; + + @ApiModelProperty("任务组名称") + private String taskGroupName; + + @ApiModelProperty("计划开始时间") + private LocalDate planStartDate; + + @ApiModelProperty("计划结束时间") + private LocalDate planEndDate; + + @ApiModelProperty("实际开始时间") + private LocalDate actualStartDate; + + @ApiModelProperty("实际结束时间") + private LocalDate actualEndDate; + + @ApiModelProperty("任务状态:0未开始,1进行中,2已结束") + private Integer status; + + @ApiModelProperty("任务状态描述") + private String statusDesc; + + @ApiModelProperty("是否逾期:0未逾期,1已逾期") + private Integer overdueStatus; + + @ApiModelProperty("任务负责人ID") + private String mainUserId; + + @ApiModelProperty("任务负责人姓名") + private String mainUserName; + + @ApiModelProperty("任务参与人") + private String userIds; + + @ApiModelProperty("任务参与人姓名列表") + private List participantNames; + + @ApiModelProperty("进度百分比") + private Integer progress; + + @ApiModelProperty("任务层级") + private Integer level; + + @ApiModelProperty("任务在时间轴上的位置(天数)") + private Integer position; + + @ApiModelProperty("任务持续时间(天数)") + private Integer duration; + + @ApiModelProperty("子任务列表") + private List children; + + @ApiModelProperty("任务备注") + private String remark; + + @ApiModelProperty("项目ID") + private String projectId; + + @ApiModelProperty("项目名称") + private String projectName; +} 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/ReceiptRequest.java b/core/src/main/java/com/dite/znpt/domain/vo/ReceiptRequest.java new file mode 100644 index 0000000..d1776ef --- /dev/null +++ b/core/src/main/java/com/dite/znpt/domain/vo/ReceiptRequest.java @@ -0,0 +1,160 @@ +package com.dite.znpt.domain.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.math.BigDecimal; + +/** + * 收货请求参数(扩展版,包含完整设备信息) + * + * @author system + * @date 2025-01-08 + */ +@Data +@ApiModel(value = "收货请求参数", description = "收货请求参数,包含收货信息和设备信息") +public class ReceiptRequest { + + // 收货特有信息 + @NotBlank(message = "收货时间不能为空") + @ApiModelProperty("收货时间") + private String receiptTime; + + @NotBlank(message = "收货人不能为空") + @ApiModelProperty("收货人") + private String receiptPerson; + + @NotNull(message = "收货数量不能为空") + @ApiModelProperty("收货数量") + private Integer receiptQuantity; + + @ApiModelProperty("收货备注") + private String receiptRemark; + + @NotBlank(message = "外观检查结果不能为空") + @ApiModelProperty("外观检查结果") + private String appearanceCheck; + + @NotBlank(message = "功能测试结果不能为空") + @ApiModelProperty("功能测试结果") + private String functionTest; + + @NotBlank(message = "包装完整性不能为空") + @ApiModelProperty("包装完整性") + private String packageIntegrity; + + @NotBlank(message = "配件完整性不能为空") + @ApiModelProperty("配件完整性") + private String accessoryIntegrity; + + @NotBlank(message = "检查结果不能为空") + @ApiModelProperty("检查结果") + private String checkResult; + + @ApiModelProperty("检查备注") + private String checkRemark; + + @NotBlank(message = "入库位置不能为空") + @ApiModelProperty("入库位置") + private String storageLocation; + + @NotBlank(message = "库管员不能为空") + @ApiModelProperty("库管员") + private String storageManager; + + // 设备基本信息(从采购数据继承) + @ApiModelProperty("设备名称") + private String equipmentName; + + @ApiModelProperty("设备型号") + private String equipmentModel; + + @ApiModelProperty("设备类型") + private String equipmentType; + + @ApiModelProperty("设备序列号") + private String equipmentSn; + + @ApiModelProperty("品牌") + private String brand; + + @ApiModelProperty("配置规格/参数") + private String specification; + + @ApiModelProperty("资产编号") + private String assetCode; + + // 采购信息(从采购数据继承) + @ApiModelProperty("采购订单号") + private String purchaseOrder; + + @ApiModelProperty("供应商名称") + private String supplierName; + + @ApiModelProperty("采购价格") + private BigDecimal purchasePrice; + + @ApiModelProperty("采购时间") + private String purchaseTime; + + @ApiModelProperty("数量") + private Integer quantity; + + @ApiModelProperty("单价") + private BigDecimal unitPrice; + + @ApiModelProperty("总价") + private BigDecimal totalPrice; + + // 入库信息 + @ApiModelProperty("入库时间") + private String inStockTime; + + @ApiModelProperty("物理位置") + private String physicalLocation; + + @ApiModelProperty("位置状态") + private String locationStatus; + + @ApiModelProperty("负责人") + private String responsiblePerson; + + @ApiModelProperty("库存条码") + private String inventoryBarcode; + + // 状态信息 + @ApiModelProperty("设备状态") + private String equipmentStatus; + + @ApiModelProperty("使用状态") + private String useStatus; + + @ApiModelProperty("健康状态") + private String healthStatus; + + @ApiModelProperty("收货状态") + private String receiptStatus; + + // 其他管理信息 + @ApiModelProperty("折旧方法") + private String depreciationMethod; + + @ApiModelProperty("折旧年限") + private Integer depreciationYears; + + @ApiModelProperty("残值") + private BigDecimal salvageValue; + + @ApiModelProperty("当前净值") + private BigDecimal currentNetValue; + + // 系统字段 + @ApiModelProperty("创建时间") + private String createTime; + + @ApiModelProperty("更新时间") + private String updateTime; +} 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/ProjectTaskMapper.java b/core/src/main/java/com/dite/znpt/mapper/ProjectTaskMapper.java index afb1d09..4101f84 100644 --- a/core/src/main/java/com/dite/znpt/mapper/ProjectTaskMapper.java +++ b/core/src/main/java/com/dite/znpt/mapper/ProjectTaskMapper.java @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.dite.znpt.domain.entity.ProjectTaskEntity; import com.dite.znpt.domain.vo.ProjectTaskListReq; import com.dite.znpt.domain.vo.ProjectTaskResp; +import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; @@ -13,6 +14,7 @@ import java.util.List; * @date 2025/06/24 16:44 * @Description: 项目任务信息表数据库访问层 */ +@Mapper public interface ProjectTaskMapper extends BaseMapper { List queryBySelective(ProjectTaskListReq projectTaskReq); 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/EquipmentService.java b/core/src/main/java/com/dite/znpt/service/EquipmentService.java index 0f7a604..dbded87 100644 --- a/core/src/main/java/com/dite/znpt/service/EquipmentService.java +++ b/core/src/main/java/com/dite/znpt/service/EquipmentService.java @@ -6,6 +6,7 @@ import com.dite.znpt.domain.entity.EquipmentEntity; import com.dite.znpt.domain.vo.EquipmentListReq; import com.dite.znpt.domain.vo.EquipmentReq; import com.dite.znpt.domain.vo.EquipmentResp; +import com.dite.znpt.domain.vo.ReceiptRequest; import java.util.List; import java.util.Map; @@ -53,6 +54,11 @@ public interface EquipmentService extends IService { */ Object getProcurementStats(); + /** + * 确认收货并自动入库 + */ + void receiveGoodsAndStockIn(String equipmentId, ReceiptRequest req); + /** * 导出采购记录 */ @@ -72,4 +78,19 @@ public interface EquipmentService extends IService { * 修改设备采购记录 */ void updateProcurement(String equipmentId, EquipmentReq req); + + /** + * 分页查询设备盘库记录 + */ + IPage inventoryPage(EquipmentListReq req); + + /** + * 执行设备盘库 + */ + void executeInventory(String equipmentId, String inventoryResult, String remark); + + /** + * 批量执行设备盘库 + */ + void batchExecuteInventory(List equipmentIds, String inventoryResult, String remark); } diff --git a/core/src/main/java/com/dite/znpt/service/GanttChartService.java b/core/src/main/java/com/dite/znpt/service/GanttChartService.java new file mode 100644 index 0000000..d3e1c26 --- /dev/null +++ b/core/src/main/java/com/dite/znpt/service/GanttChartService.java @@ -0,0 +1,55 @@ +package com.dite.znpt.service; + +import com.dite.znpt.domain.vo.GanttChartReq; +import com.dite.znpt.domain.vo.GanttChartResp; + +import java.util.List; + +/** + * 甘特图服务接口 + */ +public interface GanttChartService { + + /** + * 获取项目甘特图数据 + * + * @param req 查询条件 + * @return 甘特图数据列表 + */ + List getGanttChartData(GanttChartReq req); + + /** + * 获取项目甘特图统计信息 + * + * @param projectId 项目ID + * @return 统计信息 + */ + Object getGanttChartStatistics(String projectId); + + /** + * 获取项目甘特图时间轴信息 + * + * @param projectId 项目ID + * @return 时间轴信息 + */ + Object getGanttChartTimeline(String projectId); + + /** + * 更新任务进度 + * + * @param taskId 任务ID + * @param progress 进度百分比 + * @return 是否成功 + */ + boolean updateTaskProgress(String taskId, Integer progress); + + /** + * 拖拽更新任务时间 + * + * @param taskId 任务ID + * @param startDate 开始时间 + * @param endDate 结束时间 + * @return 是否成功 + */ + boolean updateTaskTime(String taskId, String startDate, String endDate); +} 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/ContractServiceImpl.java b/core/src/main/java/com/dite/znpt/service/impl/ContractServiceImpl.java index 8794210..73d183b 100644 --- a/core/src/main/java/com/dite/znpt/service/impl/ContractServiceImpl.java +++ b/core/src/main/java/com/dite/znpt/service/impl/ContractServiceImpl.java @@ -4,15 +4,18 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.dite.znpt.domain.entity.ContractEntity; +import com.dite.znpt.domain.entity.ContractSettlementEntity; import com.dite.znpt.domain.vo.ContractListReq; import com.dite.znpt.domain.vo.ContractReq; import com.dite.znpt.domain.vo.ContractResp; import com.dite.znpt.enums.ContractStatusEnum; import com.dite.znpt.mapper.ContractMapper; import com.dite.znpt.service.ContractService; +import com.dite.znpt.service.ContractSettlementService; import com.dite.znpt.util.PageUtil; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -25,6 +28,8 @@ import java.util.List; @RequiredArgsConstructor public class ContractServiceImpl extends ServiceImpl implements ContractService { + private final ContractSettlementService contractSettlementService; + /** * 功能描述:查询合同列表 * @@ -96,9 +101,10 @@ public class ContractServiceImpl extends ServiceImpl getPendingApprovals(EquipmentApprovalListReq req) { - // 创建分页对象,处理null值 - Integer pageNum = req.getPage() != null ? req.getPage() : 1; + log.info("开始执行待审批设备分页查询,请求参数: {}", req); + + // 获取分页参数 + Integer pageNum = req.getPageNum() != null ? req.getPageNum() : 1; Integer pageSize = req.getPageSize() != null ? req.getPageSize() : 10; - Page page = new Page<>(pageNum, pageSize); + + log.info("分页参数 - pageNum: {}, pageSize: {}", pageNum, pageSize); + + // 使用分页插件 + PageUtil.startPage(); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(EquipmentApprovalEntity::getApprovalStatus, "PENDING"); @@ -52,17 +60,38 @@ public class EquipmentApprovalServiceImpl implements EquipmentApprovalService { // 添加查询条件 addQueryConditions(wrapper, req); - IPage result = equipmentApprovalMapper.selectPage(page, wrapper); + // 执行查询 + List approvalList = equipmentApprovalMapper.selectList(wrapper); - return convertToRespPage(result); + // 转换为响应对象 + List respList = approvalList.stream() + .map(this::convertToResp) + .collect(Collectors.toList()); + + // 获取分页信息 + PageInfo pageInfo = new PageInfo<>(approvalList); + + log.info("待审批设备分页查询完成,共查询 {} 条记录,当前页码: {}, 总页码: {}", pageInfo.getTotal(), pageInfo.getPageNum(), pageInfo.getPages()); + + // 创建响应分页对象 + Page result = new Page<>(pageNum, pageSize, pageInfo.getTotal()); + result.setRecords(respList); + + return result; } @Override public IPage getApprovedApprovals(EquipmentApprovalListReq req) { - // 创建分页对象,处理null值 - Integer pageNum = req.getPage() != null ? req.getPage() : 1; + log.info("开始执行已审批设备分页查询,请求参数: {}", req); + + // 获取分页参数 + Integer pageNum = req.getPageNum() != null ? req.getPageNum() : 1; Integer pageSize = req.getPageSize() != null ? req.getPageSize() : 10; - Page page = new Page<>(pageNum, pageSize); + + log.info("分页参数 - pageNum: {}, pageSize: {}", pageNum, pageSize); + + // 使用分页插件 + PageUtil.startPage(); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.in(EquipmentApprovalEntity::getApprovalStatus, "APPROVED", "REJECTED"); @@ -70,9 +99,24 @@ public class EquipmentApprovalServiceImpl implements EquipmentApprovalService { // 添加查询条件 addQueryConditions(wrapper, req); - IPage result = equipmentApprovalMapper.selectPage(page, wrapper); + // 执行查询 + List approvalList = equipmentApprovalMapper.selectList(wrapper); - return convertToRespPage(result); + // 转换为响应对象 + List respList = approvalList.stream() + .map(this::convertToResp) + .collect(Collectors.toList()); + + // 获取分页信息 + PageInfo pageInfo = new PageInfo<>(approvalList); + + log.info("已审批设备分页查询完成,共查询 {} 条记录,当前页码: {}, 总页码: {}", pageInfo.getTotal(), pageInfo.getPageNum(), pageInfo.getPages()); + + // 创建响应分页对象 + Page result = new Page<>(pageNum, pageSize, pageInfo.getTotal()); + result.setRecords(respList); + + return result; } @Override @@ -456,11 +500,13 @@ public class EquipmentApprovalServiceImpl implements EquipmentApprovalService { public IPage getMyProcurementApplications(EquipmentApprovalListReq req) { log.info("开始获取我的采购申请,请求参数: {}", req); - // 创建分页对象 - Integer pageNum = req.getPage() != null ? req.getPage() : 1; + // 创建分页对象 - 使用pageNum保持一致性 + Integer pageNum = req.getPageNum() != null ? req.getPageNum() : 1; Integer pageSize = req.getPageSize() != null ? req.getPageSize() : 10; Page page = new Page<>(pageNum, pageSize); + log.info("分页参数 - pageNum: {}, pageSize: {}", pageNum, pageSize); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); // 只查询当前用户的申请 @@ -472,6 +518,8 @@ public class EquipmentApprovalServiceImpl implements EquipmentApprovalService { IPage result = equipmentApprovalMapper.selectPage(page, wrapper); + log.info("我的采购申请分页查询完成,共查询 {} 条记录,当前页码: {}, 总页码: {}", result.getTotal(), result.getCurrent(), result.getPages()); + return convertToRespPage(result); } diff --git a/core/src/main/java/com/dite/znpt/service/impl/EquipmentServiceImpl.java b/core/src/main/java/com/dite/znpt/service/impl/EquipmentServiceImpl.java index ebaac5c..9d6beec 100644 --- a/core/src/main/java/com/dite/znpt/service/impl/EquipmentServiceImpl.java +++ b/core/src/main/java/com/dite/znpt/service/impl/EquipmentServiceImpl.java @@ -16,14 +16,20 @@ import com.dite.znpt.enums.LocationStatusEnum; import com.dite.znpt.exception.ServiceException; import com.dite.znpt.mapper.EquipmentMapper; import com.dite.znpt.service.EquipmentService; +import com.dite.znpt.util.PageUtil; +import com.github.pagehelper.PageInfo; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; +import javax.annotation.Resource; import java.time.LocalDateTime; import java.util.List; import java.util.stream.Collectors; +import com.dite.znpt.domain.vo.ReceiptRequest; +import cn.dev33.satoken.stp.StpUtil; +import org.springframework.beans.BeanUtils; /** * @author Bear.G @@ -38,10 +44,14 @@ public class EquipmentServiceImpl extends ServiceImpl page(EquipmentListReq req) { log.info("开始执行设备分页查询,请求参数: {}", req); - // 创建分页对象,处理null值 - Integer pageNum = req.getPage() != null ? req.getPage() : (req.getPageNum() != null ? req.getPageNum() : 1); + // 获取分页参数 + Integer pageNum = req.getPageNum() != null ? req.getPageNum() : 1; Integer pageSize = req.getPageSize() != null ? req.getPageSize() : 10; - Page page = new Page<>(pageNum, pageSize); + + log.info("分页参数 - pageNum: {}, pageSize: {}", pageNum, pageSize); + + // 使用分页插件 + PageUtil.startPage(); // 构建查询条件 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); @@ -117,9 +127,14 @@ public class EquipmentServiceImpl extends ServiceImpl equipmentPage = this.page(page, queryWrapper); - - log.info("查询完成,总记录数: {}, 当前页记录数: {}", equipmentPage.getTotal(), equipmentPage.getRecords().size()); + // 执行查询 + List equipmentList = this.list(queryWrapper); + log.info("查询完成,当前页记录数: {}, 总记录数: {}", + equipmentList.size()); + // 转换为响应对象 - List equipmentRespList = equipmentPage.getRecords().stream() + List equipmentRespList = equipmentList.stream() .map(this::convertToResp) .collect(Collectors.toList()); - // 创建响应分页对象 - Page respPage = new Page<>(equipmentPage.getCurrent(), equipmentPage.getSize(), equipmentPage.getTotal()); + // 获取分页信息 - 使用正确的实体类型 + PageInfo pageInfo = new PageInfo<>(equipmentList); + + log.info("获取总数: {}", pageInfo.getTotal()); + // 创建响应分页对象 - 使用正确的分页参数 + Page respPage = new Page<>(pageNum, pageSize, pageInfo.getTotal()); respPage.setRecords(equipmentRespList); return respPage; @@ -597,10 +510,14 @@ public class EquipmentServiceImpl extends ServiceImpl procurementPage(EquipmentListReq req) { log.info("开始执行设备采购记录分页查询,请求参数: {}", req); - // 创建分页对象,处理null值 - Integer pageNum = req.getPage() != null ? req.getPage() : (req.getPageNum() != null ? req.getPageNum() : 1); + // 获取分页参数 + Integer pageNum = req.getPageNum() != null ? req.getPageNum() : 1; Integer pageSize = req.getPageSize() != null ? req.getPageSize() : 10; - Page page = new Page<>(pageNum, pageSize); + + log.info("分页参数 - pageNum: {}, pageSize: {}", pageNum, pageSize); + + // 使用分页插件 + PageUtil.startPage(); // 构建查询条件,参考设备分页查询的逻辑 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); @@ -624,14 +541,9 @@ public class EquipmentServiceImpl extends ServiceImpl result = this.page(page, queryWrapper); + List equipmentList = this.list(queryWrapper); // 转换为响应对象 - List records = result.getRecords().stream() + List records = equipmentList.stream() .map(this::convertToResp) .collect(Collectors.toList()); - // 创建新的分页结果 - Page respPage = new Page<>(result.getCurrent(), result.getSize(), result.getTotal()); - respPage.setRecords(records); + // 获取分页信息 - 使用正确的实体类型 + PageInfo pageInfo = new PageInfo<>(equipmentList); - log.info("设备采购记录分页查询完成,总记录数: {}", result.getTotal()); - return respPage; + log.info("设备采购分页查询完成,共查询 {} 条记录,当前页码: {}, 总页码: {}", pageInfo.getTotal(), pageInfo.getPageNum(), pageInfo.getPages()); + + // 创建响应分页对象 - 使用正确的分页参数 + Page result = new Page<>(pageNum, pageSize, pageInfo.getTotal()); + result.setRecords(records); + + return result; } @Transactional(rollbackFor = Exception.class) @@ -758,11 +677,12 @@ public class EquipmentServiceImpl extends ServiceImpl inventoryPage(EquipmentListReq req) { + log.info("开始执行设备盘库分页查询,请求参数: {}", req); + + // 获取分页参数 + Integer pageNum = req.getPageNum() != null ? req.getPageNum() : 1; + Integer pageSize = req.getPageSize() != null ? req.getPageSize() : 10; + + log.info("分页参数 - pageNum: {}, pageSize: {}", pageNum, pageSize); + + // 使用分页插件 + PageUtil.startPage(); + + // 构建查询条件 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + + // 添加搜索条件 + if (StringUtils.hasText(req.getEquipmentName())) { + queryWrapper.like(EquipmentEntity::getEquipmentName, req.getEquipmentName()); + } + if (StringUtils.hasText(req.getEquipmentType())) { + queryWrapper.eq(EquipmentEntity::getEquipmentType, req.getEquipmentType()); + } + if (StringUtils.hasText(req.getAssetCode())) { + queryWrapper.like(EquipmentEntity::getAssetCode, req.getAssetCode()); + } + if (StringUtils.hasText(req.getBrand())) { + queryWrapper.like(EquipmentEntity::getBrand, req.getBrand()); + } + if (StringUtils.hasText(req.getLocationStatus())) { + queryWrapper.eq(EquipmentEntity::getLocationStatus, req.getLocationStatus()); + } + if (StringUtils.hasText(req.getResponsiblePerson())) { + queryWrapper.like(EquipmentEntity::getResponsiblePerson, req.getResponsiblePerson()); + } + if (StringUtils.hasText(req.getInventoryBarcode())) { + queryWrapper.like(EquipmentEntity::getInventoryBarcode, req.getInventoryBarcode()); + } + + // 按创建时间倒序排列 + queryWrapper.orderByDesc(EquipmentEntity::getCreateTime); + + // 执行查询 + IPage page = this.page(new Page<>(pageNum, pageSize), queryWrapper); + + // 转换为响应对象 + IPage result = page.convert(this::convertToResp); + + log.info("设备盘库分页查询完成,总记录数: {}, 当前页记录数: {}", result.getTotal(), result.getRecords().size()); + + return result; + } + + @Override + public void executeInventory(String equipmentId, String inventoryResult, String remark) { + log.info("执行设备盘库,设备ID: {}, 盘点结果: {}, 备注: {}", equipmentId, inventoryResult, remark); + + // 验证设备是否存在 + EquipmentEntity equipment = this.getById(equipmentId); + if (equipment == null) { + throw new ServiceException("设备不存在"); + } + + // 更新盘点状态 + EquipmentEntity updateEquipment = new EquipmentEntity(); + updateEquipment.setEquipmentId(equipmentId); + updateEquipment.setInventoryTimeStatus1(LocalDateTime.now().toString() + "_" + inventoryResult); + updateEquipment.setDynamicRecord(remark); + + // 保存更新 + boolean success = this.updateById(updateEquipment); + if (!success) { + throw new ServiceException("执行设备盘库失败"); + } + + log.info("设备盘库执行成功,设备ID: {}", equipmentId); + } + + @Override + public void batchExecuteInventory(List equipmentIds, String inventoryResult, String remark) { + log.info("批量执行设备盘库,设备ID列表: {}, 盘点结果: {}, 备注: {}", equipmentIds, inventoryResult, remark); + + if (equipmentIds == null || equipmentIds.isEmpty()) { + throw new ServiceException("设备ID列表不能为空"); + } + + // 批量更新盘点状态 + List updateList = equipmentIds.stream() + .map(equipmentId -> { + EquipmentEntity equipment = new EquipmentEntity(); + equipment.setEquipmentId(equipmentId); + equipment.setInventoryTimeStatus1(LocalDateTime.now().toString() + "_" + inventoryResult); + equipment.setDynamicRecord(remark); + return equipment; + }) + .collect(Collectors.toList()); + + // 批量更新 + boolean success = this.updateBatchById(updateList); + if (!success) { + throw new ServiceException("批量执行设备盘库失败"); + } + + log.info("批量设备盘库执行成功,处理设备数量: {}", equipmentIds.size()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void receiveGoodsAndStockIn(String equipmentId, ReceiptRequest req) { + log.info("开始处理设备收货和入库,设备ID: {}", equipmentId); + log.info("收货请求数据: {}", req); + + // 1. 查找采购记录 + EquipmentEntity procurementRecord = this.getById(equipmentId); + if (procurementRecord == null) { + throw new ServiceException("采购记录不存在"); + } + + log.info("找到采购记录: {}", procurementRecord); + + // 2. 更新现有设备记录(而不是创建新记录) + EquipmentEntity equipment = new EquipmentEntity(); + equipment.setEquipmentId(equipmentId); + + // 设置收货相关信息 + if (StringUtils.hasText(req.getReceiptTime())) { + try { + equipment.setInStockTime(LocalDateTime.parse(req.getReceiptTime())); + } catch (Exception e) { + log.warn("解析收货时间失败,使用当前时间: {}", req.getReceiptTime()); + equipment.setInStockTime(LocalDateTime.now()); + } + } else { + equipment.setInStockTime(LocalDateTime.now()); + } + + // 设置入库位置信息 + if (StringUtils.hasText(req.getStorageLocation())) { + equipment.setPhysicalLocation(req.getStorageLocation()); + } + if (StringUtils.hasText(req.getStorageManager())) { + equipment.setResponsiblePerson(req.getStorageManager()); + } + + // 设置状态信息 + equipment.setLocationStatus("in_stock"); + equipment.setEquipmentStatus("normal"); + equipment.setUseStatus("0"); + equipment.setHealthStatus("good"); + + // 设置库存条码 + if (StringUtils.hasText(req.getInventoryBarcode())) { + equipment.setInventoryBarcode(req.getInventoryBarcode()); + } else if (StringUtils.hasText(procurementRecord.getInventoryBarcode())) { + equipment.setInventoryBarcode(procurementRecord.getInventoryBarcode()); + } + + // 设置系统字段 + equipment.setUpdateTime(LocalDateTime.now()); + equipment.setUpdateBy(StpUtil.getLoginIdAsString()); + + // 3. 更新设备记录 + boolean success = this.updateById(equipment); + if (!success) { + throw new ServiceException("更新设备记录失败"); + } + + log.info("设备收货和入库完成,设备ID: {}, 入库时间: {}, 位置: {}", + equipmentId, equipment.getInStockTime(), equipment.getPhysicalLocation()); + } } diff --git a/core/src/main/java/com/dite/znpt/service/impl/EquipmentUseRecordServiceImpl.java b/core/src/main/java/com/dite/znpt/service/impl/EquipmentUseRecordServiceImpl.java index 33e7555..4df621a 100644 --- a/core/src/main/java/com/dite/znpt/service/impl/EquipmentUseRecordServiceImpl.java +++ b/core/src/main/java/com/dite/znpt/service/impl/EquipmentUseRecordServiceImpl.java @@ -50,8 +50,13 @@ public class EquipmentUseRecordServiceImpl extends ServiceImpl page(EquipmentUseRecordListReq req) { + // 使用分页插件 PageUtil.startPage(); - return this.list(req); + + // 执行查询 + List result = this.list(req); + + return result; } @Override diff --git a/core/src/main/java/com/dite/znpt/service/impl/GanttChartServiceImpl.java b/core/src/main/java/com/dite/znpt/service/impl/GanttChartServiceImpl.java new file mode 100644 index 0000000..e1f8f44 --- /dev/null +++ b/core/src/main/java/com/dite/znpt/service/impl/GanttChartServiceImpl.java @@ -0,0 +1,486 @@ +package com.dite.znpt.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.dite.znpt.domain.entity.ProjectTaskEntity; +import com.dite.znpt.domain.entity.UserEntity; +import com.dite.znpt.domain.vo.GanttChartReq; +import com.dite.znpt.domain.vo.GanttChartResp; +import com.dite.znpt.mapper.ProjectTaskMapper; +import com.dite.znpt.service.GanttChartService; +import com.dite.znpt.service.UserService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.stream.Collectors; + +/** + * 甘特图服务实现类 + */ +@Service +@RequiredArgsConstructor +public class GanttChartServiceImpl implements GanttChartService { + + private final ProjectTaskMapper projectTaskMapper; + private final UserService userService; + + @Override + public List getGanttChartData(GanttChartReq req) { + // 查询所有任务 + QueryWrapper queryWrapper = new QueryWrapper<>(); + + if (StrUtil.isNotBlank(req.getProjectId())) { + queryWrapper.eq("project_id", req.getProjectId()); + } + if (StrUtil.isNotBlank(req.getTaskGroupId())) { + queryWrapper.eq("task_group_id", req.getTaskGroupId()); + } + if (req.getStatus() != null) { + queryWrapper.eq("status", req.getStatus()); + } + if (StrUtil.isNotBlank(req.getMainUserId())) { + queryWrapper.eq("main_user_id", req.getMainUserId()); + } + if (req.getStartDateFrom() != null) { + queryWrapper.ge("plan_start_date", req.getStartDateFrom()); + } + if (req.getStartDateTo() != null) { + queryWrapper.le("plan_start_date", req.getStartDateTo()); + } + if (req.getEndDateFrom() != null) { + queryWrapper.ge("plan_end_date", req.getEndDateFrom()); + } + if (req.getEndDateTo() != null) { + queryWrapper.le("plan_end_date", req.getEndDateTo()); + } + if (req.getOverdueOnly() != null && req.getOverdueOnly()) { + queryWrapper.eq("overdue_status", 1); + } + if (req.getIncludeCompleted() != null && !req.getIncludeCompleted()) { + queryWrapper.ne("status", 2); + } + + queryWrapper.orderByAsc("plan_start_date"); + + List taskList = projectTaskMapper.selectList(queryWrapper); + + // 获取用户信息 + final Set userIds = new HashSet<>(); + taskList.forEach(task -> { + if (StrUtil.isNotBlank(task.getMainUserId())) { + userIds.add(task.getMainUserId()); + } + if (StrUtil.isNotBlank(task.getUserIds())) { + userIds.addAll(Arrays.asList(task.getUserIds().split(","))); + } + }); + + final Map userMap = new HashMap<>(); + if (CollUtil.isNotEmpty(userIds)) { + List users = userService.listByIds(userIds); + userMap.putAll(users.stream().collect(Collectors.toMap(UserEntity::getUserId, user -> user))); + } + + // 批量获取项目时间范围,避免重复查询 + final Map projectStartDates = new HashMap<>(); + if (StrUtil.isNotBlank(req.getProjectId())) { + // 如果指定了项目ID,直接获取该项目的时间范围 + LocalDate projectStart = getProjectStartDate(req.getProjectId()); + projectStartDates.put(req.getProjectId(), projectStart); + } else { + // 如果没有指定项目ID,获取所有相关项目的时间范围 + Set projectIds = taskList.stream() + .map(ProjectTaskEntity::getProjectId) + .filter(StrUtil::isNotBlank) + .collect(Collectors.toSet()); + + for (String projectId : projectIds) { + LocalDate projectStart = getProjectStartDate(projectId); + projectStartDates.put(projectId, projectStart); + } + } + + // 转换为甘特图数据 + List ganttData = taskList.stream().map(task -> { + GanttChartResp resp = BeanUtil.copyProperties(task, GanttChartResp.class); + + // 设置状态描述 + switch (task.getStatus()) { + case 0: + resp.setStatusDesc("未开始"); + break; + case 1: + resp.setStatusDesc("进行中"); + break; + case 2: + resp.setStatusDesc("已结束"); + break; + default: + resp.setStatusDesc("未知"); + } + + // 设置负责人姓名 + if (StrUtil.isNotBlank(task.getMainUserId()) && userMap.containsKey(task.getMainUserId())) { + resp.setMainUserName(userMap.get(task.getMainUserId()).getName()); + } + + // 设置参与人姓名列表 + if (StrUtil.isNotBlank(task.getUserIds())) { + List participantNames = Arrays.stream(task.getUserIds().split(",")) + .filter(userId -> userMap.containsKey(userId)) + .map(userId -> userMap.get(userId).getName()) + .collect(Collectors.toList()); + resp.setParticipantNames(participantNames); + } + + // 计算进度(这里可以根据实际业务逻辑调整) + if (task.getStatus() == 2) { + resp.setProgress(100); + } else if (task.getStatus() == 1) { + resp.setProgress(50); // 默认进度,实际应该从数据库字段获取 + } else { + resp.setProgress(0); + } + + // 计算任务在时间轴上的位置和持续时间 + LocalDate projectStart = projectStartDates.get(task.getProjectId()); + if (projectStart != null && task.getPlanStartDate() != null) { + int position = (int) ChronoUnit.DAYS.between(projectStart, task.getPlanStartDate()); + resp.setPosition(position); + } + + // 计算任务持续时间 + if (task.getPlanStartDate() != null && task.getPlanEndDate() != null) { + int duration = (int) ChronoUnit.DAYS.between(task.getPlanStartDate(), task.getPlanEndDate()) + 1; + resp.setDuration(duration); + } else { + resp.setDuration(1); // 默认持续1天 + } + + return resp; + }).collect(Collectors.toList()); + + // 构建树形结构 + return buildTreeStructure(ganttData); + } + + @Override + public Object getGanttChartStatistics(String projectId) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("project_id", projectId); + + List taskList = projectTaskMapper.selectList(queryWrapper); + + Map statistics = new HashMap<>(); + statistics.put("totalTasks", taskList.size()); + statistics.put("notStarted", (int) taskList.stream().filter(task -> task.getStatus() == 0).count()); + statistics.put("inProgress", (int) taskList.stream().filter(task -> task.getStatus() == 1).count()); + statistics.put("completed", (int) taskList.stream().filter(task -> task.getStatus() == 2).count()); + statistics.put("overdue", (int) taskList.stream().filter(task -> task.getOverdueStatus() == 1).count()); + + // 计算总体进度 + if (taskList.size() > 0) { + final double avgProgress = taskList.stream() + .mapToInt(task -> { + if (task.getStatus() == 2) return 100; + else if (task.getStatus() == 1) return 50; + else return 0; + }) + .average() + .orElse(0.0); + statistics.put("overallProgress", Math.round(avgProgress)); + } else { + statistics.put("overallProgress", 0); + } + + return statistics; + } + + @Override + public Object getGanttChartTimeline(String projectId) { + if (StrUtil.isBlank(projectId)) { + return null; + } + + LocalDate projectStartDate = getProjectStartDate(projectId); + LocalDate projectEndDate = getProjectEndDate(projectId); + + Map timeline = new HashMap<>(); + timeline.put("projectId", projectId); + timeline.put("startDate", projectStartDate); + timeline.put("endDate", projectEndDate); + timeline.put("totalDays", ChronoUnit.DAYS.between(projectStartDate, projectEndDate) + 1); + + // 计算时间轴上的关键时间点(比如每周、每月) + List> timePoints = new ArrayList<>(); + LocalDate currentDate = projectStartDate; + + while (!currentDate.isAfter(projectEndDate)) { + Map timePoint = new HashMap<>(); + timePoint.put("date", currentDate); + timePoint.put("dayOfWeek", currentDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.SHORT, java.util.Locale.CHINESE)); + timePoint.put("isWeekend", currentDate.getDayOfWeek().getValue() >= 6); + timePoints.add(timePoint); + + currentDate = currentDate.plusDays(1); + } + + timeline.put("timePoints", timePoints); + + return timeline; + } + + @Override + public boolean updateTaskProgress(String taskId, Integer progress) { + ProjectTaskEntity task = projectTaskMapper.selectById(taskId); + if (task == null) { + return false; + } + + // 更新进度 + // 这里需要根据实际业务逻辑添加进度字段 + // task.setProgress(progress); + + // 根据进度更新状态 + if (progress >= 100) { + task.setStatus(2); // 已完成 + task.setActualEndDate(LocalDate.now()); + } else if (progress > 0) { + task.setStatus(1); // 进行中 + if (task.getActualStartDate() == null) { + task.setActualStartDate(LocalDate.now()); + } + } + + return projectTaskMapper.updateById(task) > 0; + } + + @Override + public boolean updateTaskTime(String taskId, String startDate, String endDate) { + ProjectTaskEntity task = projectTaskMapper.selectById(taskId); + if (task == null) { + return false; + } + + final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + LocalDate newStartDate = null; + LocalDate newEndDate = null; + + try { + if (StrUtil.isNotBlank(startDate)) { + newStartDate = LocalDate.parse(startDate, formatter); + } + if (StrUtil.isNotBlank(endDate)) { + newEndDate = LocalDate.parse(endDate, formatter); + } + } catch (DateTimeParseException e) { + return false; // 日期格式错误 + } + + // 验证时间约束 + if (!validateTaskTimeConstraints(task, newStartDate, newEndDate)) { + return false; + } + + // 更新任务时间 + if (newStartDate != null) { + task.setPlanStartDate(newStartDate); + } + if (newEndDate != null) { + task.setPlanEndDate(newEndDate); + } + + // 更新逾期状态 + updateOverdueStatus(task); + + return projectTaskMapper.updateById(task) > 0; + } + + /** + * 构建树形结构 + */ + private List buildTreeStructure(List allTasks) { + final Map taskMap = allTasks.stream() + .collect(Collectors.toMap(GanttChartResp::getTaskId, task -> task)); + + final List rootTasks = new ArrayList<>(); + + for (GanttChartResp task : allTasks) { + if (StrUtil.isBlank(task.getParentTaskId())) { + // 根任务 + task.setLevel(0); + rootTasks.add(task); + } else { + // 子任务 + GanttChartResp parentTask = taskMap.get(task.getParentTaskId()); + if (parentTask != null) { + task.setLevel(parentTask.getLevel() + 1); + if (parentTask.getChildren() == null) { + parentTask.setChildren(new ArrayList<>()); + } + parentTask.getChildren().add(task); + } + } + } + + return rootTasks; + } + + /** + * 获取项目开始时间 + * 从项目任务中计算最早的计划开始时间作为项目开始时间 + */ + private LocalDate getProjectStartDate(String projectId) { + if (StrUtil.isBlank(projectId)) { + return LocalDate.now().minusDays(30); // 默认值 + } + + // 查询项目中所有任务的计划开始时间,取最早的时间 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("project_id", projectId) + .isNotNull("plan_start_date") + .orderByAsc("plan_start_date") + .last("LIMIT 1"); + + ProjectTaskEntity earliestTask = projectTaskMapper.selectOne(queryWrapper); + + if (earliestTask != null && earliestTask.getPlanStartDate() != null) { + return earliestTask.getPlanStartDate(); + } + + // 如果没有找到有效的开始时间,返回当前时间减去30天作为默认值 + return LocalDate.now().minusDays(30); + } + + /** + * 获取项目结束时间 + * 从项目任务中计算最晚的计划结束时间作为项目结束时间 + */ + private LocalDate getProjectEndDate(String projectId) { + if (StrUtil.isBlank(projectId)) { + return LocalDate.now().plusDays(30); // 默认值 + } + + // 查询项目中所有任务的计划结束时间,取最晚的时间 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("project_id", projectId) + .isNotNull("plan_end_date") + .orderByDesc("plan_end_date") + .last("LIMIT 1"); + + ProjectTaskEntity latestTask = projectTaskMapper.selectOne(queryWrapper); + + if (latestTask != null && latestTask.getPlanEndDate() != null) { + return latestTask.getPlanEndDate(); + } + + // 如果没有找到有效的结束时间,返回当前时间加上30天作为默认值 + return LocalDate.now().plusDays(30); + } + + /** + * 获取项目时间范围 + * 返回项目的开始时间和结束时间 + */ + private Map getProjectTimeRange(String projectId) { + Map timeRange = new HashMap<>(); + timeRange.put("startDate", getProjectStartDate(projectId)); + timeRange.put("endDate", getProjectEndDate(projectId)); + return timeRange; + } + + /** + * 批量获取项目时间范围 + * 通过一次查询获取多个项目的时间范围,提高性能 + */ + private Map> getBatchProjectTimeRanges(Set projectIds) { + Map> timeRanges = new HashMap<>(); + + for (String projectId : projectIds) { + Map timeRange = getProjectTimeRange(projectId); + timeRanges.put(projectId, timeRange); + } + + return timeRanges; + } + + /** + * 计算任务进度 + */ + private Integer calculateProgress(ProjectTaskEntity task) { + if (task.getStatus() == 2) return 100; // 已完成 + else if (task.getStatus() == 1) return 50; // 进行中 + else return 0; // 未开始 + } + + /** + * 计算任务持续时间 + */ + private Integer calculateDuration(ProjectTaskEntity task) { + if (task.getPlanStartDate() != null && task.getPlanEndDate() != null) { + return (int) ChronoUnit.DAYS.between(task.getPlanStartDate(), task.getPlanEndDate()) + 1; + } + return 1; + } + + /** + * 计算任务在时间轴上的位置 + */ + private Integer calculatePosition(ProjectTaskEntity task) { + LocalDate projectStart = getProjectStartDate(task.getProjectId()); + if (task.getPlanStartDate() != null && projectStart != null) { + return (int) ChronoUnit.DAYS.between(projectStart, task.getPlanStartDate()); + } + return 0; + } + + /** + * 验证任务时间约束 + */ + private boolean validateTaskTimeConstraints(ProjectTaskEntity task, + LocalDate newStartDate, + LocalDate newEndDate) { + // 验证子任务不能早于父任务开始 + if (StrUtil.isNotBlank(task.getParentTaskId())) { + ProjectTaskEntity parentTask = projectTaskMapper.selectById(task.getParentTaskId()); + if (parentTask != null && newStartDate != null) { + if (newStartDate.isBefore(parentTask.getPlanStartDate())) { + return false; + } + } + } + + // 验证结束时间不能早于开始时间 + if (newStartDate != null && newEndDate != null) { + if (newEndDate.isBefore(newStartDate)) { + return false; + } + } + + return true; + } + + /** + * 更新逾期状态 + */ + private void updateOverdueStatus(ProjectTaskEntity task) { + if (task.getPlanEndDate() != null && + task.getPlanEndDate().isBefore(LocalDate.now()) && + task.getStatus() != 2) { + task.setOverdueStatus(1); // 标记为逾期 + } else { + task.setOverdueStatus(0); + } + } +} 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..517c6d6 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 @@ -3,9 +3,11 @@ package com.dite.znpt.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.dite.znpt.constant.Message; import com.dite.znpt.converts.Converts; +import com.dite.znpt.domain.entity.ContractEntity; import com.dite.znpt.domain.entity.ProjectEntity; import com.dite.znpt.domain.entity.ProjectTaskEntity; import com.dite.znpt.domain.entity.UserEntity; @@ -16,12 +18,14 @@ import com.dite.znpt.domain.vo.ProjectReq; import com.dite.znpt.domain.vo.ProjectResp; import com.dite.znpt.enums.ProjectStatusEnum; import com.dite.znpt.exception.ServiceException; +import com.dite.znpt.mapper.ContractMapper; import com.dite.znpt.mapper.ProjectMapper; import com.dite.znpt.service.ProjectService; import com.dite.znpt.service.UserService; import com.dite.znpt.service.ProjectTaskService; import com.dite.znpt.util.PageUtil; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -47,6 +51,9 @@ public class ProjectServiceImpl extends ServiceImpl queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("contract_code", req.getProjectOrigin()); + ContractEntity contractEntity = contractMapper.selectOne(queryWrapper); + contractMapper.updateById(contractEntity); for (ProjectInitTaskReq taskReq : req.getTasks()) { ProjectTaskEntity taskEntity = BeanUtil.copyProperties(taskReq, ProjectTaskEntity.class); + if (taskEntity.getScales() == null) { + taskEntity.setScales(entity.getScale()); + } taskEntity.setProjectId(entity.getProjectId()); + taskEntity.setFinished(0); projectTaskService.save(taskEntity); } } 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 04dbc10..a322168 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 @@ -10,6 +10,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.dite.znpt.constant.Message; import com.dite.znpt.domain.entity.AttachInfoEntity; +import com.dite.znpt.domain.entity.ProjectEntity; import com.dite.znpt.domain.entity.ProjectTaskEntity; import com.dite.znpt.domain.vo.ProjectTaskListReq; import com.dite.znpt.domain.vo.ProjectTaskReq; @@ -18,6 +19,7 @@ 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.ProjectMapper; import com.dite.znpt.mapper.ProjectTaskGroupMapper; import com.dite.znpt.mapper.ProjectTaskMapper; import com.dite.znpt.service.AttachInfoService; diff --git a/core/src/main/java/com/dite/znpt/service/impl/VideoMonitorServiceImpl.java b/core/src/main/java/com/dite/znpt/service/impl/VideoMonitorServiceImpl.java new file mode 100644 index 0000000..f5aca30 --- /dev/null +++ b/core/src/main/java/com/dite/znpt/service/impl/VideoMonitorServiceImpl.java @@ -0,0 +1,181 @@ +package com.dite.znpt.service.impl; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.dite.znpt.constant.Message; +import com.dite.znpt.domain.PageResult; +import com.dite.znpt.domain.entity.VideoMonitorEntity; +import com.dite.znpt.enums.FilePathEnum; +import com.dite.znpt.enums.VideoMonitorEnum; +import com.dite.znpt.exception.ServiceException; +import com.dite.znpt.mapper.VideoMonitorEntityMapper; +import com.dite.znpt.service.ProjectService; +import com.dite.znpt.service.TurbineService; +import com.dite.znpt.service.VideoMonitorService; +import com.google.common.collect.Lists; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.rmi.ServerException; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + *

+ * 视频信息 服务实现类 + *

+ * + * @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/ContractMapper.xml b/core/src/main/resources/mapper/ContractMapper.xml index 078d6e2..f8e3015 100644 --- a/core/src/main/resources/mapper/ContractMapper.xml +++ b/core/src/main/resources/mapper/ContractMapper.xml @@ -3,22 +3,22 @@ - a.contract_id, a.customer, a.code, a.project_id, + a.contract_id, a.customer, a.code, a.project_name, a.salesperson_id, a.department_id, a.sign_date, a.duration, - a.type, a.product_service, a.payment_date, a.payment_address, - a.amount, a.account_number, a.notes, a.contract_status, - a.create_time, a.create_by, a.update_time, a.update_by + a.type, a.product_service, a.payment_date, a.performance_deadline, + a.payment_address, a.amount, a.account_number, a.notes, + a.contract_status, a.contract_text, a.create_time, a.create_by, + a.update_time, a.update_by + + select + + from video_monitor + where project_id=#{projectId,jdbcType=VARCHAR} and part_id=#{partId,jdbcType=VARCHAR} + + + + 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/EquipmentController.java b/web/src/main/java/com/dite/znpt/web/controller/EquipmentController.java index bbfdccb..1b8280c 100644 --- a/web/src/main/java/com/dite/znpt/web/controller/EquipmentController.java +++ b/web/src/main/java/com/dite/znpt/web/controller/EquipmentController.java @@ -15,6 +15,8 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; +import java.util.List; +import com.dite.znpt.domain.vo.ReceiptRequest; /** * @author Bear.G @@ -103,9 +105,50 @@ public class EquipmentController { return Result.ok(); } + @ApiOperation(value = "分页查询设备盘库记录", httpMethod = "GET") + @GetMapping("/inventory/page") + public PageResult inventoryPage(EquipmentListReq req) { + log.info("=== 设备盘库记录查询接口被调用 ==="); + log.info("接收到的请求参数: {}", req); + + IPage page = equipmentService.inventoryPage(req); + return PageResult.ok(page.getRecords(), page.getTotal()); + } + + @ApiOperation(value = "执行设备盘库", httpMethod = "POST") + @PostMapping("/inventory/{equipmentId}") + public Result executeInventory( + @PathVariable String equipmentId, + @RequestParam String inventoryResult, + @RequestParam(required = false) String remark) { + equipmentService.executeInventory(equipmentId, inventoryResult, remark); + return Result.ok(); + } + + @ApiOperation(value = "批量执行设备盘库", httpMethod = "POST") + @PostMapping("/inventory/batch") + public Result batchExecuteInventory( + @RequestParam List equipmentIds, + @RequestParam String inventoryResult, + @RequestParam(required = false) String remark) { + equipmentService.batchExecuteInventory(equipmentIds, inventoryResult, remark); + return Result.ok(); + } + @ApiOperation(value = "获取采购统计信息", httpMethod = "GET") @GetMapping("/procurement/stats") public Result getProcurementStats(){ return Result.ok(equipmentService.getProcurementStats()); } + + @ApiOperation(value = "确认收货并自动入库", httpMethod = "POST") + @PostMapping("/procurement/receipt/{equipmentId}") + public Result receiveGoods(@PathVariable String equipmentId, @Validated @RequestBody ReceiptRequest req) { + log.info("=== 设备收货接口被调用 ==="); + log.info("设备ID: {}", equipmentId); + log.info("收货数据: {}", req); + + equipmentService.receiveGoodsAndStockIn(equipmentId, req); + return Result.ok("收货成功,设备已自动入库"); + } } diff --git a/web/src/main/java/com/dite/znpt/web/controller/GanttChartController.java b/web/src/main/java/com/dite/znpt/web/controller/GanttChartController.java new file mode 100644 index 0000000..6da8f57 --- /dev/null +++ b/web/src/main/java/com/dite/znpt/web/controller/GanttChartController.java @@ -0,0 +1,61 @@ +package com.dite.znpt.web.controller; + +import com.dite.znpt.domain.Result; +import com.dite.znpt.domain.vo.GanttChartReq; +import com.dite.znpt.domain.vo.GanttChartResp; +import com.dite.znpt.service.GanttChartService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 甘特图控制器 + */ +@Api(tags = "甘特图管理") +@RestController +@RequestMapping("/gantt-chart") +public class GanttChartController { + + @Resource + private GanttChartService ganttChartService; + + @ApiOperation(value = "获取项目甘特图数据", httpMethod = "GET") + @GetMapping("/data") + public Result> getGanttChartData(GanttChartReq req) { + List data = ganttChartService.getGanttChartData(req); + return Result.ok(data); + } + + @ApiOperation(value = "获取项目甘特图统计信息", httpMethod = "GET") + @GetMapping("/statistics/{projectId}") + public Result getGanttChartStatistics(@PathVariable String projectId) { + Object statistics = ganttChartService.getGanttChartStatistics(projectId); + return Result.ok(statistics); + } + + @ApiOperation(value = "获取项目甘特图时间轴信息", httpMethod = "GET") + @GetMapping("/timeline/{projectId}") + public Result getGanttChartTimeline(@PathVariable String projectId) { + Object timeline = ganttChartService.getGanttChartTimeline(projectId); + return Result.ok(timeline); + } + + @ApiOperation(value = "更新任务进度", httpMethod = "PUT") + @PutMapping("/progress/{taskId}") + public Result updateTaskProgress(@PathVariable String taskId, @RequestParam Integer progress) { + boolean success = ganttChartService.updateTaskProgress(taskId, progress); + return Result.ok(success); + } + + @ApiOperation(value = "拖拽更新任务时间", httpMethod = "PUT") + @PutMapping("/time/{taskId}") + public Result updateTaskTime(@PathVariable String taskId, + @RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate) { + boolean success = ganttChartService.updateTaskTime(taskId, startDate, endDate); + return Result.ok(success); + } +} 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); + } +}