Merge branch 'development' of http://pms.dtyx.net:3000/cuizhibin/znpt-backend into development

This commit is contained in:
ybb 2025-08-13 19:49:38 +08:00
commit ead45b36f6
37 changed files with 2118 additions and 207 deletions

View File

@ -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;
}
}

View File

@ -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")

View File

@ -89,7 +89,7 @@ public class ProjectEntity extends AuditableEntity implements Serializable {
@ExcelProperty("项目规模")
@ApiModelProperty("项目规模")
@TableField("scale")
private String scale;
private Integer scale;
@ExcelProperty("总工期,单位天")
@ApiModelProperty("总工期,单位天")

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -18,9 +18,6 @@ import java.math.BigDecimal;
@ApiModel("合同响应实体")
public class ContractResp extends ContractEntity {
@ApiModelProperty("项目名称")
private String projectName;
@ApiModelProperty("业务员姓名")
private String salespersonName;

View File

@ -43,6 +43,9 @@ public class EquipmentApprovalListReq implements Serializable {
@ApiModelProperty("当前页码")
private Integer page;
@ApiModelProperty("当前页码 - 与前端保持一致")
private Integer pageNum;
@ApiModelProperty("每页大小")
private Integer pageSize;

View File

@ -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;
}

View File

@ -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<String> participantNames;
@ApiModelProperty("进度百分比")
private Integer progress;
@ApiModelProperty("任务层级")
private Integer level;
@ApiModelProperty("任务在时间轴上的位置(天数)")
private Integer position;
@ApiModelProperty("任务持续时间(天数)")
private Integer duration;
@ApiModelProperty("子任务列表")
private List<GanttChartResp> children;
@ApiModelProperty("任务备注")
private String remark;
@ApiModelProperty("项目ID")
private String projectId;
@ApiModelProperty("项目名称")
private String projectName;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<JSONObject> listAll() {
List<JSONObject> 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;
}
}

View File

@ -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<ProjectTaskEntity> {
List<ProjectTaskResp> queryBySelective(ProjectTaskListReq projectTaskReq);

View File

@ -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<VideoMonitorEntity> {
List<VideoMonitorEntity> selectAllByProjectIdAndPartId(@Param("projectId") String projectId, @Param("partId") String partId);
int batchInsert(@Param("list") List<VideoMonitorEntity> list);
}

View File

@ -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<EquipmentEntity> {
*/
Object getProcurementStats();
/**
* 确认收货并自动入库
*/
void receiveGoodsAndStockIn(String equipmentId, ReceiptRequest req);
/**
* 导出采购记录
*/
@ -72,4 +78,19 @@ public interface EquipmentService extends IService<EquipmentEntity> {
* 修改设备采购记录
*/
void updateProcurement(String equipmentId, EquipmentReq req);
/**
* 分页查询设备盘库记录
*/
IPage<EquipmentResp> inventoryPage(EquipmentListReq req);
/**
* 执行设备盘库
*/
void executeInventory(String equipmentId, String inventoryResult, String remark);
/**
* 批量执行设备盘库
*/
void batchExecuteInventory(List<String> equipmentIds, String inventoryResult, String remark);
}

View File

@ -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<GanttChartResp> 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);
}

View File

@ -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;
/**
* <p>
* 视频信息 服务类
* </p>
*
* @author hdc
* @since 2025-08-07
*/
public interface VideoMonitorService extends IService<VideoMonitorEntity> {
/**
* 批量上传视频
*/
List<VideoMonitorEntity> uploadBatch(String projectId,
String partId,
String type,
MultipartFile[] files) throws IOException;
/**
* 单文件上传
*/
VideoMonitorEntity upload(String projectId,
String partId,
String type,
MultipartFile file) throws IOException;
/**
* 分页列表
*/
PageResult<VideoMonitorEntity> page(Integer pageNo,
Integer pageSize,
String projectId,
String partId);
/**
* 列表
*/
List<VideoMonitorEntity> list(String projectId, String partId);
/**
* 删除
*/
void delete(String videoId);
/**
* 下载/播放
*/
void download(String videoId, HttpServletResponse response) throws IOException;
}

View File

@ -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);
}
}
}

View File

@ -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<ContractMapper, ContractEntity> implements ContractService {
private final ContractSettlementService contractSettlementService;
/**
* 功能描述查询合同列表
*
@ -96,9 +101,10 @@ public class ContractServiceImpl extends ServiceImpl<ContractMapper, ContractEnt
* @date 2025/07/21 20:29
**/
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteById(String contractId) {
// todo 校验
removeById(contractId);
contractSettlementService.lambdaUpdate().eq(ContractSettlementEntity::getContractId, contractId).remove();
}
}

View File

@ -11,7 +11,9 @@ import com.dite.znpt.domain.vo.EquipmentApprovalResp;
import com.dite.znpt.domain.vo.EquipmentProcurementApplyReq;
import com.dite.znpt.service.EquipmentApprovalService;
import com.dite.znpt.service.EquipmentStatusUpdateService;
import com.dite.znpt.util.PageUtil;
import com.dite.znpt.websocket.SimpleWebSocketHandler;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
@ -41,10 +43,16 @@ public class EquipmentApprovalServiceImpl implements EquipmentApprovalService {
@Override
public IPage<EquipmentApprovalResp> 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<EquipmentApprovalEntity> page = new Page<>(pageNum, pageSize);
log.info("分页参数 - pageNum: {}, pageSize: {}", pageNum, pageSize);
// 使用分页插件
PageUtil.startPage();
LambdaQueryWrapper<EquipmentApprovalEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(EquipmentApprovalEntity::getApprovalStatus, "PENDING");
@ -52,17 +60,38 @@ public class EquipmentApprovalServiceImpl implements EquipmentApprovalService {
// 添加查询条件
addQueryConditions(wrapper, req);
IPage<EquipmentApprovalEntity> result = equipmentApprovalMapper.selectPage(page, wrapper);
// 执行查询
List<EquipmentApprovalEntity> approvalList = equipmentApprovalMapper.selectList(wrapper);
return convertToRespPage(result);
// 转换为响应对象
List<EquipmentApprovalResp> respList = approvalList.stream()
.map(this::convertToResp)
.collect(Collectors.toList());
// 获取分页信息
PageInfo<EquipmentApprovalEntity> pageInfo = new PageInfo<>(approvalList);
log.info("待审批设备分页查询完成,共查询 {} 条记录,当前页码: {}, 总页码: {}", pageInfo.getTotal(), pageInfo.getPageNum(), pageInfo.getPages());
// 创建响应分页对象
Page<EquipmentApprovalResp> result = new Page<>(pageNum, pageSize, pageInfo.getTotal());
result.setRecords(respList);
return result;
}
@Override
public IPage<EquipmentApprovalResp> 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<EquipmentApprovalEntity> page = new Page<>(pageNum, pageSize);
log.info("分页参数 - pageNum: {}, pageSize: {}", pageNum, pageSize);
// 使用分页插件
PageUtil.startPage();
LambdaQueryWrapper<EquipmentApprovalEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.in(EquipmentApprovalEntity::getApprovalStatus, "APPROVED", "REJECTED");
@ -70,9 +99,24 @@ public class EquipmentApprovalServiceImpl implements EquipmentApprovalService {
// 添加查询条件
addQueryConditions(wrapper, req);
IPage<EquipmentApprovalEntity> result = equipmentApprovalMapper.selectPage(page, wrapper);
// 执行查询
List<EquipmentApprovalEntity> approvalList = equipmentApprovalMapper.selectList(wrapper);
return convertToRespPage(result);
// 转换为响应对象
List<EquipmentApprovalResp> respList = approvalList.stream()
.map(this::convertToResp)
.collect(Collectors.toList());
// 获取分页信息
PageInfo<EquipmentApprovalEntity> pageInfo = new PageInfo<>(approvalList);
log.info("已审批设备分页查询完成,共查询 {} 条记录,当前页码: {}, 总页码: {}", pageInfo.getTotal(), pageInfo.getPageNum(), pageInfo.getPages());
// 创建响应分页对象
Page<EquipmentApprovalResp> result = new Page<>(pageNum, pageSize, pageInfo.getTotal());
result.setRecords(respList);
return result;
}
@Override
@ -456,11 +500,13 @@ public class EquipmentApprovalServiceImpl implements EquipmentApprovalService {
public IPage<EquipmentApprovalResp> 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<EquipmentApprovalEntity> page = new Page<>(pageNum, pageSize);
log.info("分页参数 - pageNum: {}, pageSize: {}", pageNum, pageSize);
LambdaQueryWrapper<EquipmentApprovalEntity> wrapper = new LambdaQueryWrapper<>();
// 只查询当前用户的申请
@ -472,6 +518,8 @@ public class EquipmentApprovalServiceImpl implements EquipmentApprovalService {
IPage<EquipmentApprovalEntity> result = equipmentApprovalMapper.selectPage(page, wrapper);
log.info("我的采购申请分页查询完成,共查询 {} 条记录,当前页码: {}, 总页码: {}", result.getTotal(), result.getCurrent(), result.getPages());
return convertToRespPage(result);
}

View File

@ -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<EquipmentMapper, Equipment
public IPage<EquipmentResp> 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<EquipmentEntity> page = new Page<>(pageNum, pageSize);
log.info("分页参数 - pageNum: {}, pageSize: {}", pageNum, pageSize);
// 使用分页插件
PageUtil.startPage();
// 构建查询条件
LambdaQueryWrapper<EquipmentEntity> queryWrapper = new LambdaQueryWrapper<>();
@ -117,9 +127,14 @@ public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment
log.info("添加物理位置查询条件: {}", req.getPhysicalLocation());
conditionCount++;
}
if (StringUtils.hasText(req.getSupplierName())) {
queryWrapper.like(EquipmentEntity::getSupplierName, req.getSupplierName());
log.info("添加供应商名称查询条件: {}", req.getSupplierName());
if (StringUtils.hasText(req.getUsingDepartment())) {
queryWrapper.like(EquipmentEntity::getUsingDepartment, req.getUsingDepartment());
log.info("添加使用部门查询条件: {}", req.getUsingDepartment());
conditionCount++;
}
if (StringUtils.hasText(req.getPurchaseOrder())) {
queryWrapper.like(EquipmentEntity::getPurchaseOrder, req.getPurchaseOrder());
log.info("添加采购订单查询条件: {}", req.getPurchaseOrder());
conditionCount++;
}
if (StringUtils.hasText(req.getMaintenancePerson())) {
@ -127,40 +142,6 @@ public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment
log.info("添加维护人员查询条件: {}", req.getMaintenancePerson());
conditionCount++;
}
if (StringUtils.hasText(req.getInventoryBarcode())) {
queryWrapper.like(EquipmentEntity::getInventoryBarcode, req.getInventoryBarcode());
log.info("添加库存条码查询条件: {}", req.getInventoryBarcode());
conditionCount++;
}
if (StringUtils.hasText(req.getAssetRemark())) {
queryWrapper.like(EquipmentEntity::getAssetRemark, req.getAssetRemark());
log.info("添加资产备注查询条件: {}", req.getAssetRemark());
conditionCount++;
}
// 新增字段查询条件
if (StringUtils.hasText(req.getUsingDepartment())) {
queryWrapper.like(EquipmentEntity::getUsingDepartment, req.getUsingDepartment());
log.info("添加使用部门/人查询条件: {}", req.getUsingDepartment());
conditionCount++;
}
if (StringUtils.hasText(req.getInvoice())) {
queryWrapper.like(EquipmentEntity::getInvoice, req.getInvoice());
log.info("添加发票查询条件: {}", req.getInvoice());
conditionCount++;
}
if (StringUtils.hasText(req.getBarcode())) {
queryWrapper.like(EquipmentEntity::getBarcode, req.getBarcode());
log.info("添加条码查询条件: {}", req.getBarcode());
conditionCount++;
}
if (StringUtils.hasText(req.getImporter())) {
queryWrapper.like(EquipmentEntity::getImporter, req.getImporter());
log.info("添加导入人查询条件: {}", req.getImporter());
conditionCount++;
}
// 新增采购相关字段查询条件
if (StringUtils.hasText(req.getAccountNumber())) {
queryWrapper.like(EquipmentEntity::getAccountNumber, req.getAccountNumber());
log.info("添加次户号查询条件: {}", req.getAccountNumber());
@ -270,88 +251,15 @@ public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment
log.info("总共添加了 {} 个查询条件", conditionCount);
// 排序处理
// 处理排序
if (StringUtils.hasText(req.getOrderBy())) {
String orderBy = req.getOrderBy();
String orderDirection = "desc".equalsIgnoreCase(req.getOrderDirection()) ? "desc" : "asc";
switch (orderBy.toLowerCase()) {
case "equipment_name":
if ("desc".equals(orderDirection)) {
queryWrapper.orderByDesc(EquipmentEntity::getEquipmentName);
} else {
queryWrapper.orderByAsc(EquipmentEntity::getEquipmentName);
}
break;
case "equipment_sn":
if ("desc".equals(orderDirection)) {
queryWrapper.orderByDesc(EquipmentEntity::getEquipmentSn);
} else {
queryWrapper.orderByAsc(EquipmentEntity::getEquipmentSn);
}
break;
case "asset_code":
if ("desc".equals(orderDirection)) {
queryWrapper.orderByDesc(EquipmentEntity::getAssetCode);
} else {
queryWrapper.orderByAsc(EquipmentEntity::getAssetCode);
}
break;
case "equipment_type":
if ("desc".equals(orderDirection)) {
queryWrapper.orderByDesc(EquipmentEntity::getEquipmentType);
} else {
queryWrapper.orderByAsc(EquipmentEntity::getEquipmentType);
}
break;
case "equipment_status":
if ("desc".equals(orderDirection)) {
queryWrapper.orderByDesc(EquipmentEntity::getEquipmentStatus);
} else {
queryWrapper.orderByAsc(EquipmentEntity::getEquipmentStatus);
}
break;
case "brand":
if ("desc".equals(orderDirection)) {
queryWrapper.orderByDesc(EquipmentEntity::getBrand);
} else {
queryWrapper.orderByAsc(EquipmentEntity::getBrand);
}
break;
case "responsible_person":
if ("desc".equals(orderDirection)) {
queryWrapper.orderByDesc(EquipmentEntity::getResponsiblePerson);
} else {
queryWrapper.orderByAsc(EquipmentEntity::getResponsiblePerson);
}
break;
case "purchase_time":
if ("desc".equals(orderDirection)) {
queryWrapper.orderByDesc(EquipmentEntity::getPurchaseTime);
} else {
queryWrapper.orderByAsc(EquipmentEntity::getPurchaseTime);
}
break;
case "purchase_price":
if ("desc".equals(orderDirection)) {
queryWrapper.orderByDesc(EquipmentEntity::getTotalPrice);
} else {
queryWrapper.orderByAsc(EquipmentEntity::getTotalPrice);
}
break;
case "update_time":
if ("desc".equals(orderDirection)) {
queryWrapper.orderByDesc(EquipmentEntity::getUpdateTime);
} else {
queryWrapper.orderByAsc(EquipmentEntity::getUpdateTime);
}
break;
default:
// 默认按创建时间倒序
queryWrapper.orderByDesc(EquipmentEntity::getCreateTime);
break;
if ("asc".equalsIgnoreCase(req.getOrderDirection())) {
queryWrapper.orderByAsc(EquipmentEntity::getCreateTime);
log.info("使用升序排序: {}", req.getOrderBy());
} else {
queryWrapper.orderByDesc(EquipmentEntity::getCreateTime);
log.info("使用降序排序: {}", req.getOrderBy());
}
log.info("添加排序条件: {} {}", orderBy, orderDirection);
} else {
// 默认按创建时间倒序
queryWrapper.orderByDesc(EquipmentEntity::getCreateTime);
@ -360,18 +268,23 @@ public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment
log.info("执行分页查询SQL条件: {}", queryWrapper.getTargetSql());
// 执行分页查询
IPage<EquipmentEntity> equipmentPage = this.page(page, queryWrapper);
log.info("查询完成,总记录数: {}, 当前页记录数: {}", equipmentPage.getTotal(), equipmentPage.getRecords().size());
// 执行查询
List<EquipmentEntity> equipmentList = this.list(queryWrapper);
log.info("查询完成,当前页记录数: {}, 总记录数: {}",
equipmentList.size());
// 转换为响应对象
List<EquipmentResp> equipmentRespList = equipmentPage.getRecords().stream()
List<EquipmentResp> equipmentRespList = equipmentList.stream()
.map(this::convertToResp)
.collect(Collectors.toList());
// 创建响应分页对象
Page<EquipmentResp> respPage = new Page<>(equipmentPage.getCurrent(), equipmentPage.getSize(), equipmentPage.getTotal());
// 获取分页信息 - 使用正确的实体类型
PageInfo<EquipmentEntity> pageInfo = new PageInfo<>(equipmentList);
log.info("获取总数: {}", pageInfo.getTotal());
// 创建响应分页对象 - 使用正确的分页参数
Page<EquipmentResp> respPage = new Page<>(pageNum, pageSize, pageInfo.getTotal());
respPage.setRecords(equipmentRespList);
return respPage;
@ -597,10 +510,14 @@ public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment
public IPage<EquipmentResp> 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<EquipmentEntity> page = new Page<>(pageNum, pageSize);
log.info("分页参数 - pageNum: {}, pageSize: {}", pageNum, pageSize);
// 使用分页插件
PageUtil.startPage();
// 构建查询条件参考设备分页查询的逻辑
LambdaQueryWrapper<EquipmentEntity> queryWrapper = new LambdaQueryWrapper<>();
@ -624,14 +541,9 @@ public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment
log.info("添加设备状态查询条件: {}", req.getEquipmentStatus());
conditionCount++;
}
if (StringUtils.hasText(req.getEquipmentSn())) {
queryWrapper.like(EquipmentEntity::getEquipmentSn, req.getEquipmentSn());
log.info("添加设备序列号查询条件: {}", req.getEquipmentSn());
conditionCount++;
}
if (StringUtils.hasText(req.getAssetCode())) {
queryWrapper.like(EquipmentEntity::getAssetCode, req.getAssetCode());
log.info("添加资产编号查询条件: {}", req.getAssetCode());
if (StringUtils.hasText(req.getEquipmentModel())) {
queryWrapper.like(EquipmentEntity::getEquipmentModel, req.getEquipmentModel());
log.info("添加设备型号查询条件: {}", req.getEquipmentModel());
conditionCount++;
}
if (StringUtils.hasText(req.getBrand())) {
@ -639,19 +551,9 @@ public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment
log.info("添加品牌查询条件: {}", req.getBrand());
conditionCount++;
}
if (StringUtils.hasText(req.getLocationStatus())) {
queryWrapper.eq(EquipmentEntity::getLocationStatus, req.getLocationStatus());
log.info("添加位置状态查询条件: {}", req.getLocationStatus());
conditionCount++;
}
if (StringUtils.hasText(req.getHealthStatus())) {
queryWrapper.eq(EquipmentEntity::getHealthStatus, req.getHealthStatus());
log.info("添加健康状态查询条件: {}", req.getHealthStatus());
conditionCount++;
}
if (StringUtils.hasText(req.getResponsiblePerson())) {
queryWrapper.like(EquipmentEntity::getResponsiblePerson, req.getResponsiblePerson());
log.info("添加负责人查询条件: {}", req.getResponsiblePerson());
if (StringUtils.hasText(req.getSupplierName())) {
queryWrapper.like(EquipmentEntity::getSupplierName, req.getSupplierName());
log.info("添加供应商名称查询条件: {}", req.getSupplierName());
conditionCount++;
}
if (StringUtils.hasText(req.getPhysicalLocation())) {
@ -659,16 +561,24 @@ public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment
log.info("添加物理位置查询条件: {}", req.getPhysicalLocation());
conditionCount++;
}
// 添加采购相关的搜索条件
if (StringUtils.hasText(req.getUsingDepartment())) {
queryWrapper.like(EquipmentEntity::getUsingDepartment, req.getUsingDepartment());
log.info("添加使用部门查询条件: {}", req.getUsingDepartment());
conditionCount++;
}
if (StringUtils.hasText(req.getResponsiblePerson())) {
queryWrapper.like(EquipmentEntity::getResponsiblePerson, req.getResponsiblePerson());
log.info("添加负责人查询条件: {}", req.getResponsiblePerson());
conditionCount++;
}
if (StringUtils.hasText(req.getPurchaseOrder())) {
queryWrapper.like(EquipmentEntity::getPurchaseOrder, req.getPurchaseOrder());
log.info("添加采购订单查询条件: {}", req.getPurchaseOrder());
conditionCount++;
}
if (StringUtils.hasText(req.getSupplierName())) {
queryWrapper.like(EquipmentEntity::getSupplierName, req.getSupplierName());
log.info("添加供应商查询条件: {}", req.getSupplierName());
if (StringUtils.hasText(req.getMaintenancePerson())) {
queryWrapper.like(EquipmentEntity::getMaintenancePerson, req.getMaintenancePerson());
log.info("添加维护人员查询条件: {}", req.getMaintenancePerson());
conditionCount++;
}
if (StringUtils.hasText(req.getAccountNumber())) {
@ -691,6 +601,11 @@ public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment
log.info("添加总价查询条件: {}", req.getTotalPrice());
conditionCount++;
}
if (StringUtils.hasText(req.getEquipmentSn())) {
queryWrapper.like(EquipmentEntity::getEquipmentSn, req.getEquipmentSn());
log.info("添加设备序列号查询条件: {}", req.getEquipmentSn());
conditionCount++;
}
if (StringUtils.hasText(req.getInventoryBasis())) {
queryWrapper.like(EquipmentEntity::getInventoryBasis, req.getInventoryBasis());
log.info("添加盘点依据查询条件: {}", req.getInventoryBasis());
@ -713,19 +628,23 @@ public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment
queryWrapper.orderByDesc(EquipmentEntity::getPurchaseTime);
// 执行查询
IPage<EquipmentEntity> result = this.page(page, queryWrapper);
List<EquipmentEntity> equipmentList = this.list(queryWrapper);
// 转换为响应对象
List<EquipmentResp> records = result.getRecords().stream()
List<EquipmentResp> records = equipmentList.stream()
.map(this::convertToResp)
.collect(Collectors.toList());
// 创建新的分页结果
Page<EquipmentResp> respPage = new Page<>(result.getCurrent(), result.getSize(), result.getTotal());
respPage.setRecords(records);
// 获取分页信息 - 使用正确的实体类型
PageInfo<EquipmentEntity> pageInfo = new PageInfo<>(equipmentList);
log.info("设备采购记录分页查询完成,总记录数: {}", result.getTotal());
return respPage;
log.info("设备采购分页查询完成,共查询 {} 条记录,当前页码: {}, 总页码: {}", pageInfo.getTotal(), pageInfo.getPageNum(), pageInfo.getPages());
// 创建响应分页对象 - 使用正确的分页参数
Page<EquipmentResp> 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<EquipmentMapper, Equipment
@Transactional(rollbackFor = Exception.class)
@Override
public void updateProcurement(String equipmentId, EquipmentReq req) {
log.info("开始修改设备采购记录设备ID: {}, 请求参数: {}", equipmentId, req);
// 实现更新设备采购记录的逻辑
log.info("更新设备采购记录设备ID: {}, 请求参数: {}", equipmentId, req);
// 检查设备是否存在
EquipmentEntity existingEntity = this.getById(equipmentId);
if (existingEntity == null) {
// 验证设备是否存在
EquipmentEntity existingEquipment = this.getById(equipmentId);
if (existingEquipment == null) {
throw new ServiceException("设备不存在");
}
@ -782,7 +702,7 @@ public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment
entity.setEquipmentId(equipmentId);
this.updateById(entity);
log.info("设备采购记录修改成功设备ID: {}", equipmentId);
log.info("设备采购记录更新成功设备ID: {}", equipmentId);
}
@Override
@ -818,4 +738,175 @@ public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment
log.info("采购统计信息获取完成,总金额: {}, 供应商数: {}, 设备数: {}", totalAmount, supplierCount, equipmentCount);
return stats;
}
@Override
public IPage<EquipmentResp> 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<EquipmentEntity> 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<EquipmentEntity> page = this.page(new Page<>(pageNum, pageSize), queryWrapper);
// 转换为响应对象
IPage<EquipmentResp> 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<String> equipmentIds, String inventoryResult, String remark) {
log.info("批量执行设备盘库设备ID列表: {}, 盘点结果: {}, 备注: {}", equipmentIds, inventoryResult, remark);
if (equipmentIds == null || equipmentIds.isEmpty()) {
throw new ServiceException("设备ID列表不能为空");
}
// 批量更新盘点状态
List<EquipmentEntity> 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());
}
}

View File

@ -50,8 +50,13 @@ public class EquipmentUseRecordServiceImpl extends ServiceImpl<EquipmentUseRecor
@Override
public List<EquipmentUseRecordResp> page(EquipmentUseRecordListReq req) {
// 使用分页插件
PageUtil.startPage();
return this.list(req);
// 执行查询
List<EquipmentUseRecordResp> result = this.list(req);
return result;
}
@Override

View File

@ -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<GanttChartResp> getGanttChartData(GanttChartReq req) {
// 查询所有任务
QueryWrapper<ProjectTaskEntity> 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<ProjectTaskEntity> taskList = projectTaskMapper.selectList(queryWrapper);
// 获取用户信息
final Set<String> 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<String, UserEntity> userMap = new HashMap<>();
if (CollUtil.isNotEmpty(userIds)) {
List<UserEntity> users = userService.listByIds(userIds);
userMap.putAll(users.stream().collect(Collectors.toMap(UserEntity::getUserId, user -> user)));
}
// 批量获取项目时间范围避免重复查询
final Map<String, LocalDate> projectStartDates = new HashMap<>();
if (StrUtil.isNotBlank(req.getProjectId())) {
// 如果指定了项目ID直接获取该项目的时间范围
LocalDate projectStart = getProjectStartDate(req.getProjectId());
projectStartDates.put(req.getProjectId(), projectStart);
} else {
// 如果没有指定项目ID获取所有相关项目的时间范围
Set<String> 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<GanttChartResp> 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<String> 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<ProjectTaskEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("project_id", projectId);
List<ProjectTaskEntity> taskList = projectTaskMapper.selectList(queryWrapper);
Map<String, Object> 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<String, Object> 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<Map<String, Object>> timePoints = new ArrayList<>();
LocalDate currentDate = projectStartDate;
while (!currentDate.isAfter(projectEndDate)) {
Map<String, Object> 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<GanttChartResp> buildTreeStructure(List<GanttChartResp> allTasks) {
final Map<String, GanttChartResp> taskMap = allTasks.stream()
.collect(Collectors.toMap(GanttChartResp::getTaskId, task -> task));
final List<GanttChartResp> 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<ProjectTaskEntity> 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<ProjectTaskEntity> 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<String, LocalDate> getProjectTimeRange(String projectId) {
Map<String, LocalDate> timeRange = new HashMap<>();
timeRange.put("startDate", getProjectStartDate(projectId));
timeRange.put("endDate", getProjectEndDate(projectId));
return timeRange;
}
/**
* 批量获取项目时间范围
* 通过一次查询获取多个项目的时间范围提高性能
*/
private Map<String, Map<String, LocalDate>> getBatchProjectTimeRanges(Set<String> projectIds) {
Map<String, Map<String, LocalDate>> timeRanges = new HashMap<>();
for (String projectId : projectIds) {
Map<String, LocalDate> 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);
}
}
}

View File

@ -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<ProjectMapper, ProjectEntity
@Resource
private ProjectTaskService projectTaskService;
@Autowired
private ContractMapper contractMapper;
/**
* 功能描述查询项目信息列表
*
@ -135,9 +142,17 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, ProjectEntity
public void save(ProjectReq req) {
ProjectEntity entity = Converts.INSTANCE.toProjectEntity(req);
this.save(entity);
QueryWrapper<ContractEntity> 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);
}
}

View File

@ -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;

View File

@ -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;
/**
* <p>
* 视频信息 服务实现类
* </p>
*
* @author hdc
* @since 2025-08-07
*/
@Service
public class VideoMonitorServiceImpl extends ServiceImpl<VideoMonitorEntityMapper, VideoMonitorEntity> implements VideoMonitorService {
@Resource
private TurbineService turbineService;
@Autowired
private ProjectService projectService;
@Resource(name = "clearanceExecutor")
private ThreadPoolTaskExecutor clearanceExecutor;
@Override
@Transactional(rollbackFor = Exception.class)
public List<VideoMonitorEntity> 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<VideoMonitorEntity> 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<VideoMonitorEntity> page(Integer pageNo,
Integer pageSize,
String projectId,
String partId) {
LambdaQueryWrapper<VideoMonitorEntity> wrapper = Wrappers.lambdaQuery();
wrapper.eq(StrUtil.isNotBlank(projectId),VideoMonitorEntity::getProjectId, projectId)
.eq(StrUtil.isNotBlank(partId), VideoMonitorEntity::getTurbineId, partId)
.orderByDesc(VideoMonitorEntity::getCreateTime);
Page<VideoMonitorEntity> page = page(Page.of(pageNo, pageSize), wrapper);
return PageResult.ok(page.getRecords(), page.getTotal());
}
@Override
public List<VideoMonitorEntity> 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());
}
}

View File

@ -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);
}
}

View File

@ -3,22 +3,22 @@
<mapper namespace="com.dite.znpt.mapper.ContractMapper">
<sql id="Base_Column_List">
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
</sql>
<select id="queryBySelective" resultType="com.dite.znpt.domain.vo.ContractResp">
select
<include refid="Base_Column_List"/>,
u.name as salespersonName, d.dept_name as salespersonDeptName,
p.project_name, (select sum(cs.amount) from contract_settlement cs where a.contract_id=cs.contract_id) as settlementAmount
(select sum(cs.amount) from contract_settlement cs where a.contract_id=cs.contract_id) as settlementAmount
from contract a
left join user u on a.salesperson_id = u.user_id
left join dept d on a.department_id=d.dept_id
left join project p on a.project_id=p.project_id
<where>
<if test="contractId != null and contractId != ''">
and a.contract_id like concat ('%', #{contractId}, '%')
@ -29,8 +29,8 @@
<if test="code != null and code != ''">
and a.code like concat ('%', #{code}, '%')
</if>
<if test="projectId != null and projectId != ''">
and a.project_id like concat ('%', #{projectId}, '%')
<if test="projectName != null and projectName != ''">
and a.project_name like concat ('%', #{projectName}, '%')
</if>
<if test="salespersonId != null and salespersonId != ''">
and a.salesperson_id like concat ('%', #{salespersonId}, '%')
@ -56,13 +56,13 @@
<if test="accountNumber != null and accountNumber != ''">
and a.account_number like concat ('%', #{accountNumber}, '%')
</if>
<if test="notes != null">
and a.notes = #{notes}
<if test="notes != null and notes != ''">
and a.notes like concat ('%', #{notes}, '%')
</if>
<if test="contractStatus != null and contractStatus != ''">
and a.contract_status like concat ('%', #{contractStatus}, '%')
</if>
<if test="contractText != null">
<if test="contractText != null and contractText != ''">
and a.contract_text like concat ('%', #{contractText}, '%')
</if>
</where>

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dite.znpt.mapper.VideoMonitorEntityMapper">
<resultMap id="BaseResultMap" type="com.dite.znpt.domain.entity.VideoMonitorEntity">
<!--@mbg.generated-->
<!--@Table video_monitor-->
<id column="video_id" property="videoId" />
<result column="project_id" property="projectId" />
<result column="turbine_id" property="turbineId" />
<result column="video_name" property="videoName" />
<result column="video_path" property="videoPath" />
<result column="is_deleted" property="isDeleted" />
<result column="status" property="status" />
<result column="pre_image_path" property="preImagePath" />
<result column="pre_treatment" property="preTreatment" />
<result column="update_by" property="updateBy" />
<result column="create_time" property="createTime" />
<result column="create_by" property="createBy" />
<result column="update_time" property="updateTime" />
<result column="wind_speed" property="windSpeed" />
<result column="rpm" property="rpm" />
<result column="type" property="type" />
<result column="extra" property="extra" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
<result column="upload_time" property="uploadTime" />
</resultMap>
<sql id="Base_Column_List">
<!--@mbg.generated-->
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
</sql>
<select id="selectAllByProjectIdAndPartId" resultMap="BaseResultMap">
<!--@mbg.generated-->
select
<include refid="Base_Column_List"/>
from video_monitor
where project_id=#{projectId,jdbcType=VARCHAR} and part_id=#{partId,jdbcType=VARCHAR}
</select>
<insert id="batchInsert" keyColumn="video_id" keyProperty="videoId" parameterType="map" useGeneratedKeys="true">
<!--@mbg.generated-->
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
<foreach collection="list" item="item" separator=",">
(#{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})
</foreach>
</insert>
</mapper>

View File

@ -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<EquipmentResp> inventoryPage(EquipmentListReq req) {
log.info("=== 设备盘库记录查询接口被调用 ===");
log.info("接收到的请求参数: {}", req);
IPage<EquipmentResp> 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<String> 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("收货成功,设备已自动入库");
}
}

View File

@ -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<List<GanttChartResp>> getGanttChartData(GanttChartReq req) {
List<GanttChartResp> data = ganttChartService.getGanttChartData(req);
return Result.ok(data);
}
@ApiOperation(value = "获取项目甘特图统计信息", httpMethod = "GET")
@GetMapping("/statistics/{projectId}")
public Result<Object> getGanttChartStatistics(@PathVariable String projectId) {
Object statistics = ganttChartService.getGanttChartStatistics(projectId);
return Result.ok(statistics);
}
@ApiOperation(value = "获取项目甘特图时间轴信息", httpMethod = "GET")
@GetMapping("/timeline/{projectId}")
public Result<Object> getGanttChartTimeline(@PathVariable String projectId) {
Object timeline = ganttChartService.getGanttChartTimeline(projectId);
return Result.ok(timeline);
}
@ApiOperation(value = "更新任务进度", httpMethod = "PUT")
@PutMapping("/progress/{taskId}")
public Result<Boolean> 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<Boolean> 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);
}
}

View File

@ -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<ProjectListResp> 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<List<ContractEntity>> contracts() {
QueryWrapper<ContractEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("project_id");
return Result.ok(contractService.list(queryWrapper));
}
}

View File

@ -118,10 +118,14 @@ public class ProjectTaskController {
@GetMapping("/{projectId}/tasks")
public Result<ProjectTasksDetailResp> 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);
}
}

View File

@ -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<VideoMonitorEntity> 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<VideoMonitorEntity>> 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<List<VideoMonitorEntity>> 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<VideoMonitorEntity> 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);
}
}