ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/DashboardStatisticsController.java
@@ -76,6 +76,8 @@ screenUserVo.setPermissionClient(param.getPermissionClient()); screenUserVo.setPermissionProject(param.getPermissionProject()); screenUserVo.setCommentType(param.getCommentType()); screenUserVo.setSafetyId(param.getSafetyId()); screenUserVo.setDeviceTemplateId(param.getDeviceTemplateId()); } }else if(type==2){ Project project = projectService.getById(id); @@ -86,6 +88,8 @@ ClientParam param = clientParamService.getOne(new LambdaQueryWrapper<ClientParam>().eq(ClientParam::getClientId, client.getId())); if(param!=null){ screenUserVo.setCommentType(param.getCommentType()); screenUserVo.setSafetyId(param.getSafetyId()); screenUserVo.setDeviceTemplateId(param.getDeviceTemplateId()); } screenUserVo.setName(project.getName()); screenUserVo.setPermissionProject(project.getPermission()); ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/ProjectController.java
@@ -1,8 +1,12 @@ package com.ruoyi.web.controller.api; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.icbc.hsm.utils.StringUtil; import com.ruoyi.common.core.domain.R; import com.ruoyi.system.dto.EditHiddenDangerResponsiblePersonDto; import com.ruoyi.system.enums.DeviceType; @@ -11,18 +15,29 @@ import com.ruoyi.system.query.*; import com.ruoyi.system.service.*; import com.ruoyi.system.vo.*; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.*; import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.parsing.Problem; import org.springframework.core.io.InputStreamResource; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.validation.Valid; import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; import java.net.URLEncoder; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; import java.util.stream.Collectors; @@ -64,6 +79,9 @@ @Autowired private ScreenAccountService screenAccountService; @Autowired private ProjectProgressService projectProgressService; @ApiOperation(value = "获取项目/委托方列表",response = LoginNameVo.class) @GetMapping("/getLoginNameVoList") @@ -83,6 +101,38 @@ } @ApiOperation(value = "获取项目最新进度",response = ProjectProgress.class) @GetMapping("/getProjectProgress/{projectId}") public R<ProjectProgress> getProjectProgress(@PathVariable Integer projectId) { ProjectProgress projectProgress = projectProgressService.getOne(new LambdaQueryWrapper<ProjectProgress>().eq(ProjectProgress::getProjectId, projectId).eq(ProjectProgress::getDeleted,0).orderByDesc(ProjectProgress::getTime).last("limit 1")); return R.ok(projectProgress); } @ApiOperation(value = "获取项目进度历史列表",response = ProjectProgress.class) @PostMapping("/getProjectProgressList") public R<Page<ProjectProgress>> getProjectProgressList(@Valid @RequestBody ProjectProgressQuery query) { LambdaQueryWrapper<ProjectProgress> wrapper = new LambdaQueryWrapper<ProjectProgress>().eq(ProjectProgress::getProjectId, query.getProjectId()).eq(ProjectProgress::getDeleted, 0); if(query.getContent()!=null && !query.getContent().isEmpty()){ wrapper.like(ProjectProgress::getDetail, query.getContent()); } if(query.getStartTime()!=null && !query.getStartTime().isEmpty()){ wrapper.ge(ProjectProgress::getTime, query.getStartTime()); } if(query.getEndTime()!=null && !query.getEndTime().isEmpty()){ wrapper.le(ProjectProgress::getTime, query.getEndTime()); } wrapper.orderByDesc(ProjectProgress::getTime); Page<ProjectProgress> page = projectProgressService.page(new Page<>(query.getPageNum(), query.getPageSize()),wrapper); return R.ok(page); } @ApiOperation(value = "获取项目进度详情",response = ProjectProgress.class) @GetMapping("/getProjectProgressDetail/{id}") public R<ProjectProgress> getProjectProgressDetail(@PathVariable Integer id) { return R.ok(projectProgressService.getById(id)); } @ApiOperation(value = "获取项目详情",response = Project.class) @@ -365,18 +415,97 @@ @GetMapping("/list") @ApiImplicitParams({ @ApiImplicitParam(name = "clientId", value = "委托方ID", dataType = "int", paramType = "query"), @ApiImplicitParam(name = "projectName", value = "项目名称", dataType = "string", paramType = "query") @ApiImplicitParam(name = "projectName", value = "项目名称", dataType = "string", paramType = "query"), @ApiImplicitParam(name = "startDate", value = "开始日期", dataType = "date", paramType = "query"), @ApiImplicitParam(name = "endDate", value = "结束日期", dataType = "date", paramType = "query"), @ApiImplicitParam(name = "projectCategory", value = "项目类型 ongoing-在建工程,fixed-固定资产 ", dataType = "string", paramType = "query") }) public R<List<Project>> getList(Integer clientId, String projectName) { LambdaQueryWrapper<Project> wrapper = new LambdaQueryWrapper<Project>().eq(Project::getDeleted, false); if(clientId!=null){ wrapper.eq(Project::getClientId, clientId); public R<List<Project>> getList(@RequestParam("clientId") Integer clientId, String projectName, @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate, @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate, @RequestParam("projectCategory") String projectCategory) { LambdaQueryWrapper<Project> queryWrapper = new LambdaQueryWrapper<Project>().eq(Project::getDeleted, false); if (clientId != null) { queryWrapper.eq(Project::getClientId, clientId); } if(projectName!=null && !projectName.isEmpty()){ wrapper.like(Project::getName, projectName); if (projectName != null && !projectName.isEmpty()) { queryWrapper.like(Project::getName, projectName); } List<Project> list = projectService.list(wrapper); if (Objects.isNull(startDate)) { // 获取本月日期 Date date = new Date(); startDate = DateUtil.beginOfMonth(date); } Date finalStartDate = startDate; queryWrapper.and(wrapper -> wrapper .eq(Project::getStatus, 1) // 在建项目 .or(w -> w .eq(Project::getStatus, 2) // 已完成项目 .ge(Project::getSuccessTime, finalStartDate) // 完成时间 >= 开始日期// 完成时间 <= 结束日期 )); if ("ongoing".equals(projectCategory)) { queryWrapper.ne(Project::getType, 2); } else if ("fixed".equals(projectCategory)) { queryWrapper.eq(Project::getType, 2); } List<Project> list = projectService.list(queryWrapper); return R.ok(list); } @Resource UserFileService userFileService; @SneakyThrows @ApiOperation(value = "下载文件(名称)") @GetMapping("/download/{name}") public ResponseEntity<org.springframework.core.io.Resource> downloadFileByName(@PathVariable String name) { var userFile = userFileService.getOne(new LambdaQueryWrapper<UserFile>().eq(UserFile::getName, name)); return downloadFile(userFile); } private ResponseEntity<org.springframework.core.io.Resource> downloadFile(UserFile userFile) throws IOException { InputStreamResource resource =resolveResource(userFile); var fileSize = userFile.getSize(); // if(resource == null) { // resource = new InputStreamResource(getClass().getClassLoader().getResourceAsStream("images/wukong.png")); // fileSize = 6215360L;// resource.contentLength(); // } String encodedFileName = URLEncoder.encode(userFile.getOriginFileName(), "UTF-8"); String headerValue = String.format("attachment; filename=\"%s\"", encodedFileName); return ResponseEntity.ok() .contentType(MediaType.parseMediaType(userFile.getContentType())) .contentLength(fileSize) .header(HttpHeaders.CONTENT_DISPOSITION, headerValue) .body(resource); } public InputStreamResource resolveResource(UserFile userFile) throws IOException { var filePath = fileLocation.resolve(userFile.getName() + "." + userFile.getExtension()).normalize(); if(!Files.exists(filePath)){ return null; } return new InputStreamResource(Files.newInputStream(filePath)); } private Path fileLocation; @Value("${file.upload.dir}/files") private String UPLOAD_DIR; @PostConstruct void init(){ fileLocation = Path.of(UPLOAD_DIR).toAbsolutePath().normalize(); try { Files.createDirectories(this.fileLocation); } catch (Exception ex) { throw new RuntimeException("Could not create the directory where the uploaded files will be stored.", ex); } } } ruoyi-admin/src/main/resources/application-test.yml
@@ -176,6 +176,7 @@ # 请求前缀 pathMapping: / # 防止XSS攻击 xss: # 过滤开关 @@ -187,6 +188,7 @@ # file upload file: upload: dir: F:\file\estate location: /file/ qrLocation: /file/qrCode/ accessPath: /file/ ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
@@ -119,6 +119,7 @@ "/index/**", "/merchant/**", "/openIdByJsCode","/wx/**","/html-config/*","/icbc-notify/**","/doc.html/**" ,"/project/download/**" ).permitAll() // 静态资源,可匿名访问 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() ruoyi-system/src/main/java/com/ruoyi/system/mapper/ProjectProgressMapper.java
New file @@ -0,0 +1,23 @@ package com.ruoyi.system.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.system.model.ProjectProgress; import org.apache.ibatis.annotations.Param; /** * <p> * 项目进度 Mapper 接口 * </p> * * @author administratorProjectProgressMapper.xml * @since 2026-01-26 */ public interface ProjectProgressMapper extends BaseMapper<ProjectProgress> { Page<ProjectProgress> pageList(@Param("page") Page<ProjectProgress> page, @Param("name") String name, @Param("clientId") Integer clientId, @Param("sTime") String sTime, @Param("eTime") String eTime); ProjectProgress detail(@Param("id") Integer id); } ruoyi-system/src/main/java/com/ruoyi/system/model/ProjectProgress.java
New file @@ -0,0 +1,67 @@ package com.ruoyi.system.model; 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.activerecord.Model; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.io.Serializable; import java.util.Date; /** * <p> * 项目进度 * </p> * * @author administrator * @since 2026-01-26 */ @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("project_progress") public class ProjectProgress extends Model<ProjectProgress> { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO) private Integer id; @TableField("client_id") @NotNull(message = "委托方ID不能为空") private Integer clientId; @TableField("project_id") @NotNull(message = "项目ID不能为空") private Integer projectId; @ApiModelProperty("进度说明") @NotBlank(message = "进度说明不能为空") private String detail; @TableField("time") @ApiModelProperty("进度更新时间") @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") @NotNull(message = "进度更新时间不能为空") private Date time; @ApiModelProperty("进度url链接") @NotBlank(message = "进度url链接不能为空") private String url; @TableField("create_time") private Date createTime; private Integer deleted; @TableField(exist = false) @ApiModelProperty("委托方名称") private String clientName; @TableField(exist = false) @ApiModelProperty("项目名称") private String projectName; } ruoyi-system/src/main/java/com/ruoyi/system/query/ProjectProgressQuery.java
New file @@ -0,0 +1,25 @@ package com.ruoyi.system.query; import com.ruoyi.common.core.domain.BasePage; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.validation.constraints.NotNull; @Data @ApiModel(value = "项目进度查询参数") public class ProjectProgressQuery extends BasePage { @ApiModelProperty(value = "内容") private String content; @ApiModelProperty(value = "开始时间") private String startTime; @ApiModelProperty(value = "结束时间") private String endTime; @ApiModelProperty(value = "项目id") @NotNull(message = "项目id不能为空") private Integer projectId; } ruoyi-system/src/main/java/com/ruoyi/system/service/ProjectCategoryCalculator.java
@@ -316,6 +316,8 @@ .average() .orElse(0.0); System.out.println("results"+results); // 计算设备检查平均分(需要解析 device_res_map) List<Float> deviceScores = results.stream() .filter(e -> StringUtils.hasText(e.getDeviceResMap())) @@ -495,11 +497,11 @@ // 根据阈值判断类别(从高到低判断) if (finalScore >= thresholdC) { return "A"; return "C"; } else if (finalScore >= thresholdB) { return "B"; } else if (finalScore >= thresholdA) { return "C"; return "A"; } else { return "C"; // 不满足任何类别 } ruoyi-system/src/main/java/com/ruoyi/system/service/ProjectProgressService.java
New file @@ -0,0 +1,22 @@ package com.ruoyi.system.service; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.system.model.ProjectProgress; /** * <p> * 项目进度 服务类 * </p> * * @author administrator * @since 2026-01-26 */ public interface ProjectProgressService extends IService<ProjectProgress> { Page<ProjectProgress> pageList(Page<ProjectProgress> page, String name, Integer clientId, String time); ProjectProgress detail(Integer id); } ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ClientServiceImpl.java
@@ -88,8 +88,7 @@ .eq(Project::getStatus, 1) // 在建项目 .or(w -> w .eq(Project::getStatus, 2) // 已完成项目 .ge(Project::getSuccessTime, monthStart) // 完成时间 >= 开始日期 .le(Project::getSuccessTime, monthEnd) // 完成时间 <= 结束日期 .ge(Project::getSuccessTime, monthStart) // 完成时间 >= 开始日期// 完成时间 <= 结束日期 ) ); if (clientId != null) { @@ -225,16 +224,15 @@ private FixedAssetDashboardVO calculateFixedAssetStatistics(Long clientId, Date monthStart, Date monthEnd) { FixedAssetDashboardVO vo = new FixedAssetDashboardVO(); // 查询固定资产项目(usage_type 表示项目用途类型,假设 1 表示固定资产) // 查询固定资产项目 LambdaQueryWrapper<Project> projectWrapper = new LambdaQueryWrapper<>(); projectWrapper.eq(Project::getDeleted, false) .eq(Project::getUsageType, 1) // 固定资产 .eq(Project::getType, 2) // 固定资产 .and(wrapper -> wrapper .eq(Project::getStatus, 1) // 在建项目 .or(w -> w .eq(Project::getStatus, 2) // 已完成项目 .ge(Project::getSuccessTime, monthStart) // 完成时间 >= 开始日期 .le(Project::getSuccessTime, monthEnd) // 完成时间 <= 结束日期 ) ); if (clientId != null) { @@ -443,24 +441,37 @@ log.debug("设备按类型分组完成,共 {} 种类型", deviceGroupMap.size()); // 查询设备检查记录 List<Integer> deviceUsageIds = devices.stream() .map(DeviceUsage::getId) .collect(Collectors.toList()); // 建立 device.id 到 device_usage.id 的映射 Map<Integer, List<Integer>> deviceToUsageMap = devices.stream() .filter(d -> d.getDeviceId() != null) .collect(Collectors.groupingBy( DeviceUsage::getDeviceId, Collectors.mapping(DeviceUsage::getId, Collectors.toList()) )); Map<Integer, Boolean> deviceInspectionMap = new HashMap<>(); if (!deviceUsageIds.isEmpty()) { if (!deviceIds.isEmpty()) { LambdaQueryWrapper<Record> recordWrapper = new LambdaQueryWrapper<>(); recordWrapper.eq(Record::getRecordType, "error") .isNotNull(Record::getDeviceId) .in(Record::getDeviceId, deviceUsageIds) .in(Record::getDeviceId, deviceIds) .eq(Record::getDeleted, false); List<Record> records = recordMapper.selectList(recordWrapper); // 获取已检查的 device.id 集合 Set<Integer> inspectedDeviceIds = records.stream() .map(Record::getDeviceId) .collect(Collectors.toSet()); for (Integer deviceUsageId : deviceUsageIds) { deviceInspectionMap.put(deviceUsageId, inspectedDeviceIds.contains(deviceUsageId)); // 通过映射关系,将 device.id 的检查状态映射到 device_usage.id for (Map.Entry<Integer, List<Integer>> entry : deviceToUsageMap.entrySet()) { Integer deviceId = entry.getKey(); boolean isInspected = inspectedDeviceIds.contains(deviceId); // 将该设备的检查状态应用到所有使用该设备的 device_usage 记录 for (Integer usageId : entry.getValue()) { deviceInspectionMap.put(usageId, isInspected); } } } ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ProjectInfoServiceImpl.java
@@ -73,10 +73,10 @@ } LocalDate thisMonth = LocalDate.now().minusMonths(0).with(TemporalAdjusters.firstDayOfMonth()); Map<String, List<Record>> map = list.stream().collect(groupingBy(Record::getHierarchyName)); List<ProjectHiddenDangerTrendVo> resultList = map.entrySet().stream().map(entry -> { List<ProjectHiddenDangerTrendVo> resultList = new ArrayList<>(); for (Map.Entry<String, List<Record>> entry : map.entrySet()) { ProjectHiddenDangerTrendVo vo = new ProjectHiddenDangerTrendVo(); vo.setName(entry.getKey()); List<Record> value = entry.getValue(); @@ -96,8 +96,8 @@ }else { vo.setRate(0.00d); } return vo; }).toList(); resultList.add(vo) ; } return resultList; ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ProjectProgressServiceImpl.java
New file @@ -0,0 +1,39 @@ package com.ruoyi.system.service.impl; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.system.mapper.ProjectProgressMapper; import com.ruoyi.system.model.ProjectProgress; import com.ruoyi.system.service.ProjectProgressService; import org.springframework.stereotype.Service; /** * <p> * 项目进度 服务实现类 * </p> * * @author administrator * @since 2026-01-26 */ @Service public class ProjectProgressServiceImpl extends ServiceImpl<ProjectProgressMapper, ProjectProgress> implements ProjectProgressService { @Override public Page<ProjectProgress> pageList(Page<ProjectProgress> page, String name, Integer clientId, String time) { String sTime = null; String eTime = null; if(time!=null && !time.isEmpty()){ String[] times = time.split(" - "); sTime = times[0] + " 00:00:00"; eTime = times[1] + " 23:59:59"; } return this.baseMapper.pageList(page,name,clientId,sTime,eTime); } @Override public ProjectProgress detail(Integer id) { return this.baseMapper.detail(id); } } ruoyi-system/src/main/java/com/ruoyi/system/vo/ScreenUserVo.java
@@ -22,4 +22,10 @@ @ApiModelProperty(value = "1委托方 2项目") private Integer type; @ApiModelProperty(value = "安全报告模板id") private Integer safetyId; @ApiModelProperty(value = "设备模板id") private Integer deviceTemplateId; } ruoyi-system/src/main/resources/mapper/system/ProjectProgressMapper.xml
New file @@ -0,0 +1,32 @@ <?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.ruoyi.system.mapper.ProjectProgressMapper"> <select id="pageList" resultType="com.ruoyi.system.model.ProjectProgress"> select t1.id,t1.detail,t1.time,t2.name as clientName,t3.name as projectName from project_progress t1 left join client t2 on t1.client_id = t2.id left join project t3 on t1.project_id = t3.id where t1.deleted= 0 <if test="name != null and name != ''"> and t3.name like concat('%',#{name},'%') </if> <if test="clientId != null"> and t1.client_id = #{clientId} </if> <if test="sTime != null and sTime != ''"> and t1.time between #{sTime} and #{eTime} </if> order by t1.time desc </select> <select id="detail" resultType="com.ruoyi.system.model.ProjectProgress"> select t1.id,t1.detail,t1.time,t2.name as clientName,t3.name as projectName from project_progress t1 left join client t2 on t1.client_id = t2.id left join project t3 on t1.project_id = t3.id where t1.deleted= 0 and t1.id = #{id} </select> </mapper>