无关风月
2026-01-24 5ea0d97a1bf9030945fab70cd8e21aa6e98c7950
Merge remote-tracking branch 'origin/master'

# Conflicts:
# ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/EntrustProjectController.java
2个文件已删除
3个文件已添加
17个文件已修改
999 ■■■■■ 已修改文件
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/DashboardStatisticsController.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/EntrustProjectController.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/ProjectController.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/WorkPresentationController.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/controller/FileController.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/enums/BizResultCode.java 227 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/DeviceMapper.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/RecordMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/AccidentBrief.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/ClientParam.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/Device.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/ErrorReview.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/query/EntrustDeviceListQuery.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/RecordService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ClientServiceImpl.java 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/RecordServiceImpl.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ReportServiceImpl.java 264 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/vo/DeviceCountVo.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/vo/FixedAssetDashboardVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/vo/MachineryStatisticsVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/vo/ProblemVo.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/RecordMapper.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/DashboardStatisticsController.java
@@ -23,6 +23,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
/**
 * 数据统计面板 前端控制器
@@ -32,12 +33,10 @@
 */
@RestController
@RequestMapping("/dashboard-statistics")
@Api(tags = "数据统计面板")
@Api(tags = "首页")
@RequiredArgsConstructor(onConstructor_ = @Lazy)
public class DashboardStatisticsController {
    @Autowired
    private ScreenAccountService screenAccountService;
    @Autowired
    private TokenService tokenService;
@@ -54,6 +53,8 @@
    @Autowired
    private AccidentBriefService accidentBriefService;
    @Autowired
    private AccidentClassificationService accidentClassificationService;
    @ApiOperation("获取当前登录信息")
@@ -99,10 +100,10 @@
    @ApiOperation(value = "获取故事简讯列表",response = AccidentBrief.class)
    @PostMapping("/getStoryList")
    public R<Page<AccidentBrief>> getStoryList(@RequestBody StoryListQuery query) {
    @PostMapping("/getStoryPage")
    public R<Page<AccidentBrief>> getStoryPage(@RequestBody StoryListQuery query) {
        Page<AccidentBrief> page = new Page<>(query.getPageNum(), query.getPageSize());
        List<AccidentClassification> list = accidentClassificationService.list(new LambdaQueryWrapper<AccidentClassification>().eq(AccidentClassification::getDeleted, false));
        LambdaQueryWrapper<AccidentBrief> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AccidentBrief::getState,1);
        if(query.getCity()!=null && !query.getCity().isEmpty()){
@@ -121,6 +122,39 @@
        }
        wrapper.orderByDesc(AccidentBrief::getTime);
        Page<AccidentBrief> accidentBriefPage = accidentBriefService.page(page, wrapper);
        accidentBriefPage.getRecords().forEach(item->{
            item.setClassifyName(list.stream().filter(classify->classify.getId().equals(item.getClassifyId())).findFirst().get().getName());
        });
        return R.ok(accidentBriefPage);
    }
    @ApiOperation(value = "获取故事简讯列表-不分页",response = AccidentBrief.class)
    @PostMapping("/getStoryList")
    public R<List<AccidentBrief>> getStoryList(@RequestBody StoryListQuery query) {
        List<AccidentClassification> list = accidentClassificationService.list(new LambdaQueryWrapper<AccidentClassification>().eq(AccidentClassification::getDeleted, false));
        LambdaQueryWrapper<AccidentBrief> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AccidentBrief::getState,1);
        if(query.getCity()!=null && !query.getCity().isEmpty()){
            wrapper.eq(AccidentBrief::getCity, query.getCity());
        }
        if(query.getClassifyId()!=null){
            wrapper.eq(AccidentBrief::getClassifyId, query.getClassifyId());
        }
        if(query.getTime()!=null && !query.getTime().isEmpty()){
            String sTime = query.getTime().split(" - ")[0]+" 00:00:00";
            String eTime = query.getTime().split(" - ")[1]+" 23:59:59";
            wrapper.between(AccidentBrief::getTime,sTime,eTime);
        }
        if(query.getContent()!=null && !query.getContent().isEmpty()){
            wrapper.like(AccidentBrief::getContent, query.getContent());
        }
        wrapper.orderByDesc(AccidentBrief::getTime);
        List<AccidentBrief> accidentBriefPage = accidentBriefService.list( wrapper);
        accidentBriefPage.forEach(item->{
            item.setClassifyName(list.stream().filter(classify -> classify.getId().equals(item.getClassifyId())).findFirst().orElse(new AccidentClassification()).getName());
        });
        return R.ok(accidentBriefPage);
    }
@@ -132,6 +166,13 @@
        return R.ok(accidentBrief);
    }
    @ApiOperation(value = "获取故事简讯详情分类",response = AccidentClassification.class)
    @GetMapping("/getStoryClassify")
    public R<List<AccidentClassification>> getStoryClassify() {
        List<AccidentClassification> list = accidentClassificationService.list(new LambdaQueryWrapper<AccidentClassification>().eq(AccidentClassification::getDeleted, false));
        return R.ok(list);
    }
    /**
     * 获取委托方大屏首页统计数据
     *
@@ -139,7 +180,7 @@
     * @return 统计数据
     */
    @PostMapping("/index")
    @ApiOperation("获取委托方大屏首页统计数据")
    @ApiOperation(value = "获取委托方大屏首页统计数据",tags = {"委托方端"})
    public R<ClientDashboardVO> index(@RequestBody ClientDashboardQuery query) {
        ClientDashboardVO dashboard = clientService.getClientDashboard(query);
        return R.ok(dashboard);
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/EntrustProjectController.java
@@ -179,8 +179,20 @@
        recordService.updateById(record);
        return R.ok();
    }
//    @ApiOperation(value = "隐患详情")
//    @GetMapping("/errorDetail")
//    public R errorDetail(Integer id) {
//        Record record = recordService.getById(id);
//
//        return R.ok();
//    }
    @ApiOperation(value = "获取隐患详情")
    @GetMapping("/getHiddenDangerDetail/{problemId}")
    public R<?> getHiddenDangerDetail(@PathVariable Integer problemId) {
        var record = recordService.getHiddenDangerDetail(problemId);
        return R.ok(record);
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/ProjectController.java
@@ -12,6 +12,8 @@
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 org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -103,6 +105,10 @@
            projectVo.setCheckTime(task.getUpdateTime());
        }
        List<DeviceCountVo> list = deviceUsageService.getProjectDeviceCount(id);
        list.forEach(item->{
            DeviceType value = DeviceType.values()[item.getType()];
            item.setName(value.getDesc());
        });
        projectVo.setDeviceCountVos(list);
        return R.ok(projectVo);
    }
@@ -163,10 +169,10 @@
    }
    @ApiOperation(value = "获取隐患详情")
    @GetMapping("/getHiddenDangerDetail/{problemId}")
    public R<?> getHiddenDangerDetail(@PathVariable Integer problemId) {
        var record = recordService.getHiddenDangerDetail(problemId);
    @ApiOperation(value = "获取隐患详情",response = ProblemVo.class)
    @GetMapping("/getHiddenDangerDetail/{id}")
    public R<ProblemVo> getHiddenDangerDetail(@PathVariable Integer id) {
        ProblemVo record = recordService.getHiddenDangerDetail(id);
        return R.ok(record);
    }
@@ -355,4 +361,22 @@
        return R.ok(list);
    }
    @ApiOperation(value = "获取项目列表")
    @GetMapping("/list")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "clientId", value = "委托方ID", dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "projectName", value = "项目名称", 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);
        }
        if(projectName!=null && !projectName.isEmpty()){
            wrapper.like(Project::getName, projectName);
        }
        List<Project> list = projectService.list(wrapper);
        return R.ok(list);
    }
}
ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/WorkPresentationController.java
@@ -42,6 +42,7 @@
        LambdaQueryWrapper<WorkPresentation> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(WorkPresentation::getClientId,query.getClientId());
        wrapper.eq(WorkPresentation::getState, 1);
        if(query.getType()!=null){
            wrapper.eq(WorkPresentation::getType, query.getType());
        }
ruoyi-common/src/main/java/com/ruoyi/common/core/controller/FileController.java
@@ -40,7 +40,7 @@
        }
        String TimeDir =new SimpleDateFormat("yyyy-MM-dd").format(new Date());
//        String realPath = fileUploadConfig.getLocation() + TimeDir;
        String realPath =  "/mnt/file/" + TimeDir;
        String realPath =  "F:\\file\\estate\\files";
        File file = new File(realPath);
        // 没有目录就创建
        if (!file.exists()) {
@@ -61,7 +61,7 @@
        mf.transferTo(targetFile);
        //拼接数据
//        String imgstr = fileUploadConfig.getAccessPath() + TimeDir +"\\"+ filename;
        String imgstr = TimeDir +"/"+ filename;
        String imgstr = filename;
        return R.ok(imgstr);
    }
ruoyi-system/src/main/java/com/ruoyi/system/enums/BizResultCode.java
File was deleted
ruoyi-system/src/main/java/com/ruoyi/system/mapper/DeviceMapper.java
New file
@@ -0,0 +1,19 @@
package com.ruoyi.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.system.model.Device;
import org.apache.ibatis.annotations.Mapper;
/**
 * <p>
 * 设备 Mapper 接口
 * </p>
 *
 * @author administrator
 * @since 2026-01-22
 */
@Mapper
public interface DeviceMapper extends BaseMapper<Device> {
    // 继承 BaseMapper 提供基础 CRUD 方法
    // 批量查询使用 selectBatchIds 方法
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/RecordMapper.java
@@ -3,6 +3,7 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.system.model.ErrorReview;
import com.ruoyi.system.model.Record;
import com.ruoyi.system.query.HiddenDangerListQuery;
import com.ruoyi.system.vo.HiddenDangerListVo;
@@ -27,5 +28,11 @@
    ProblemVo getHiddenDangerDetail(@Param("problemId") Integer problemId);
    
    ErrorReview getErrorReview(@Param("errorReviewId") Integer errorReviewId);
    List<String> findReviewImg(@Param("errorReviewId") Integer errorReviewId);
    String getProjectName(@Param("checkTaskId") Integer checkTaskId);
}
ruoyi-system/src/main/java/com/ruoyi/system/model/AccidentBrief.java
@@ -85,5 +85,9 @@
    @TableField("create_time")
    private LocalDateTime createTime;
    @TableField(exist = false)
    @ApiModelProperty(value = "类别名称")
    private String classifyName;
}
ruoyi-system/src/main/java/com/ruoyi/system/model/ClientParam.java
@@ -59,9 +59,13 @@
    @TableField("device_template_id")
    private Integer deviceTemplateId;
    @ApiModelProperty(value = "项目总数")
    @TableField("project_num")
    private Integer projectNum;
    @ApiModelProperty(value = "房建项目总数")
    @TableField("housing_project_num")
    private Integer housingProjectNum;
    @ApiModelProperty(value = "市政项目总数")
    @TableField("municipal_project_num")
    private Integer municipalProjectNum;
    @ApiModelProperty(value = "在建项目json")
    @TableField("under_construction_project")
ruoyi-system/src/main/java/com/ruoyi/system/model/Device.java
New file
@@ -0,0 +1,95 @@
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.ruoyi.system.enums.DeviceType;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import java.io.Serializable;
import java.util.Date;
/**
 * <p>
 * 设备实体类
 * </p>
 *
 * @author administrator
 * @since 2026-01-22
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("device")
@Slf4j
public class Device extends Model<Device> {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    @TableField("create_time")
    private Date createTime;
    private Boolean deleted;
    /**
     * 设备编号
     */
    private String name;
    @TableField("update_time")
    private Date updateTime;
    /**
     * 设备是否正在使用中
     */
    private Boolean busy;
    /**
     * 出厂编号
     */
    @TableField("manufacturer_number")
    private String manufacturerNumber;
    /**
     * 设备详细信息(JSON格式)
     */
    private String properties;
    /**
     * 设备类型(数据库存储值:1-10)
     * 1-塔式起重机 2-施工升降机 3-物料提升机 4-桥式起重机
     * 5-附着式升降脚手架 6-流动式起重机 7-高处作业吊篮
     * 8-门式起重机 9-架桥机 10-其他
     *
     * 需要通过 getDeviceType() 方法转换为 DeviceType 枚举
     */
    private Integer type;
    @TableField("creator_id")
    private Integer creatorId;
    /**
     * 获取设备类型枚举
     * <p>
     * 数据库 type 值范围:1-10
     * DeviceType 枚举序号:0-9
     * 转换公式:DeviceType.values()[type - 1]
     * </p>
     *
     * @return DeviceType 枚举,如果 type 无效则返回 OTHER
     */
    public DeviceType getDeviceType() {
        // 数据库值减1得到枚举序号
        DeviceType value = DeviceType.values()[type ];
        return value;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/model/ErrorReview.java
New file
@@ -0,0 +1,62 @@
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 io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
 * <p>
 *
 * </p>
 *
 * @author administrator
 * @since 2026-01-23
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("error_review")
public class ErrorReview extends Model<ErrorReview> {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    @TableField("create_time")
    @ApiModelProperty(value = "创建时间")
    private Date createTime;
    private Boolean deleted;
    @ApiModelProperty(value = "名称")
    private String name;
    @TableField("update_time")
    @ApiModelProperty(value = "更新时间")
    private Date updateTime;
    @ApiModelProperty(value = "复核内容")
    private String content;
    @ApiModelProperty(value = "0未整改  1部分整改  2已整改")
    private Integer state;
    @TableField("creator_id")
    private Integer creatorId;
    @ApiModelProperty(value = "复核人")
    private String  creator;
    @ApiModelProperty(value = "图片 没有前缀")
    private List<String> img;
}
ruoyi-system/src/main/java/com/ruoyi/system/query/EntrustDeviceListQuery.java
@@ -12,7 +12,6 @@
public class EntrustDeviceListQuery extends BasePage {
    @ApiModelProperty("项目id")
    @NotNull(message = "项目id不能为空")
    private Integer projectId;
    @ApiModelProperty("整改情况 true已整改 false未整改")
    private Boolean reformed;
ruoyi-system/src/main/java/com/ruoyi/system/service/RecordService.java
@@ -24,7 +24,7 @@
    List<HiddenDangerListVo> getHiddenDangerList(Page<HiddenDangerListVo> page, @Valid HiddenDangerListQuery query, List<Integer> taskIds);
    ProblemVo getHiddenDangerDetail(Integer problemId);
    ProblemVo getHiddenDangerDetail(Integer id);
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ClientServiceImpl.java
@@ -6,20 +6,10 @@
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.system.mapper.CheckMapper;
import com.ruoyi.system.mapper.ClientMapper;
import com.ruoyi.system.mapper.DeviceUsageMapper;
import com.ruoyi.system.mapper.PlanMapper;
import com.ruoyi.system.mapper.ProjectMapper;
import com.ruoyi.system.mapper.RecordMapper;
import com.ruoyi.system.mapper.TaskMapper;
import com.ruoyi.system.model.Check;
import com.ruoyi.system.model.Client;
import com.ruoyi.system.model.DeviceUsage;
import com.ruoyi.system.model.Plan;
import com.ruoyi.system.model.Project;
import com.ruoyi.system.enums.DeviceType;
import com.ruoyi.system.mapper.*;
import com.ruoyi.system.model.*;
import com.ruoyi.system.model.Record;
import com.ruoyi.system.model.Task;
import com.ruoyi.system.service.ClientService;
import com.ruoyi.system.service.ProjectCategoryCalculator;
import com.ruoyi.system.vo.*;
@@ -55,6 +45,8 @@
    private final DeviceUsageMapper deviceUsageMapper;
    private final RecordMapper recordMapper;
    private final ProjectCategoryCalculator projectCategoryCalculator;
    private final ClientParamMapper clientParamMapper;
    private final DeviceMapper deviceMapper;
    @Override
    public ClientDashboardVO getClientDashboard(ClientDashboardQuery query) {
@@ -145,6 +137,15 @@
                        .distinct()
                        .toList();
                // 查询配置的项目总数
                ClientParam clientParam = null;
                if (clientId != null) {
                    clientParam = clientParamMapper.selectOne(
                        new LambdaQueryWrapper<ClientParam>()
                            .eq(ClientParam::getClientId, clientId.intValue())
                    );
                }
                // 房建检查覆盖率
                List<Integer> housingProjectIds = projects.stream()
                        .filter(p -> p.getType() != null && p.getType() == 0)
@@ -153,8 +154,13 @@
                long checkedHousingCount = checkedProjectIds.stream()
                        .filter(housingProjectIds::contains)
                        .count();
                // 使用配置的房建项目总数,如果没有配置则使用实际数量
                int configuredHousingNum = (clientParam != null && clientParam.getHousingProjectNum() != null)
                        ? clientParam.getHousingProjectNum()
                        : (int) housingCount;
                vo.setHousingInspectionCoverage(
                        housingCount > 0 ? (int) Math.round(checkedHousingCount * 100.0 / housingCount) : 0
                        configuredHousingNum > 0 ? (int) Math.round(checkedHousingCount * 100.0 / configuredHousingNum) : 0
                );
                // 市政检查覆盖率
@@ -165,8 +171,13 @@
                long checkedMunicipalCount = checkedProjectIds.stream()
                        .filter(municipalProjectIds::contains)
                        .count();
                // 使用配置的市政项目总数,如果没有配置则使用实际数量
                int configuredMunicipalNum = (clientParam != null && clientParam.getMunicipalProjectNum() != null)
                        ? clientParam.getMunicipalProjectNum()
                        : (int) municipalCount;
                vo.setMunicipalInspectionCoverage(
                        municipalCount > 0 ? (int) Math.round(checkedMunicipalCount * 100.0 / municipalCount) : 0
                        configuredMunicipalNum > 0 ? (int) Math.round(checkedMunicipalCount * 100.0 / configuredMunicipalNum) : 0
                );
            } else {
                vo.setHousingInspectionCoverage(0);
@@ -234,47 +245,7 @@
        // 1. 统计项目总数
        vo.setProjectTotal(projects.size());
        // 计算检查覆盖率
        List<Integer> projectIds = projects.stream().map(Project::getId).collect(Collectors.toList());
        if (!projectIds.isEmpty()) {
            // 1. 先查询符合条件的 Task
            LambdaQueryWrapper<Task> taskWrapper = new LambdaQueryWrapper<>();
            taskWrapper.in(Task::getProjectId, projectIds)
                    .eq(Task::getDeleted, false);
            List<Task> tasks = taskMapper.selectList(taskWrapper);
            if (!tasks.isEmpty()) {
                // 2. 获取 Task ID 列表
                List<Integer> taskIds = tasks.stream().map(Task::getId).collect(Collectors.toList());
                // 3. 建立 Task ID 到 Project ID 的映射
                Map<Integer, Integer> taskToProjectMap = tasks.stream()
                        .collect(Collectors.toMap(Task::getId, Task::getProjectId));
                // 4. 查询 record 表中 record_type='error' 且 device_id 为 null 的记录(项目检查)
                LambdaQueryWrapper<Record> recordWrapper = new LambdaQueryWrapper<>();
                recordWrapper.eq(Record::getRecordType, "error")
                        .isNull(Record::getDeviceId)
                        .in(Record::getCheckTaskId, taskIds)
                        .eq(Record::getDeleted, false);
                List<Record> projectRecords = recordMapper.selectList(recordWrapper);
                // 5. 通过 Task 映射获取有检查记录的项目 ID
                long checkedProjectCount = projectRecords.stream()
                        .map(Record::getCheckTaskId)
                        .map(taskToProjectMap::get)
                        .filter(Objects::nonNull)
                        .distinct()
                        .count();
                vo.setInspectionCoverage(
                        projects.size() > 0 ? (int) Math.round(checkedProjectCount * 100.0 / projects.size()) : 0
                );
            } else {
                vo.setInspectionCoverage(0);
            }
        } else {
            vo.setInspectionCoverage(0);
        }
        // 2. 统计本月检查情况
        calculateMonthlyInspectionForFixed(vo, projects, projectIds, monthStart, monthEnd);
@@ -433,19 +404,54 @@
                .in(DeviceUsage::getProjectId, projectIds);
        List<DeviceUsage> devices = deviceUsageMapper.selectList(deviceWrapper);
        // 按设备名称分组统计
        // 批量查询 Device 信息
        List<Integer> deviceIds = devices.stream()
                .map(DeviceUsage::getDeviceId)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        Map<Integer, Device> deviceMap = new HashMap<>();
        if (!deviceIds.isEmpty()) {
            List<Device> deviceList = deviceMapper.selectBatchIds(deviceIds);
            deviceMap = deviceList.stream()
                    .collect(Collectors.toMap(Device::getId, d -> d));
            log.debug("批量查询到 {} 个设备信息", deviceMap.size());
            // 检查是否有设备信息查询失败
            if (deviceIds.size() != deviceMap.size()) {
                log.warn("部分设备信息查询失败,期望 {} 个,实际查询到 {} 个",
                         deviceIds.size(), deviceMap.size());
            }
        }
        // 按设备类型名称分组统计
        final Map<Integer, Device> finalDeviceMap = deviceMap;
        Map<String, List<DeviceUsage>> deviceGroupMap = devices.stream()
                .filter(d -> d.getName() != null && !d.getName().isEmpty())
                .collect(Collectors.groupingBy(DeviceUsage::getName));
                .filter(d -> d.getDeviceId() != null)
                .filter(d -> finalDeviceMap.containsKey(d.getDeviceId()))
                .collect(Collectors.groupingBy(d -> {
                    try {
                        Device device = finalDeviceMap.get(d.getDeviceId());
                        return device.getDeviceType().getDesc();
                    } catch (Exception e) {
                        log.error("获取设备类型失败,deviceId: {}", d.getDeviceId(), e);
                        return DeviceType.OTHER.getDesc();
                    }
                }));
        log.debug("设备按类型分组完成,共 {} 种类型", deviceGroupMap.size());
        // 查询设备检查记录
        List<Integer> deviceIds = devices.stream().map(DeviceUsage::getId).collect(Collectors.toList());
        List<Integer> deviceUsageIds = devices.stream()
                .map(DeviceUsage::getId)
                .collect(Collectors.toList());
        Map<Integer, Boolean> deviceInspectionMap = new HashMap<>();
        if (!deviceIds.isEmpty()) {
        if (!deviceUsageIds.isEmpty()) {
            LambdaQueryWrapper<Record> recordWrapper = new LambdaQueryWrapper<>();
            recordWrapper.eq(Record::getRecordType, "error")
                    .isNotNull(Record::getDeviceId)
                    .in(Record::getDeviceId, deviceIds)
                    .in(Record::getDeviceId, deviceUsageIds)
                    .eq(Record::getDeleted, false);
            List<Record> records = recordMapper.selectList(recordWrapper);
@@ -453,8 +459,8 @@
                    .map(Record::getDeviceId)
                    .collect(Collectors.toSet());
            for (Integer deviceId : deviceIds) {
                deviceInspectionMap.put(deviceId, inspectedDeviceIds.contains(deviceId));
            for (Integer deviceUsageId : deviceUsageIds) {
                deviceInspectionMap.put(deviceUsageId, inspectedDeviceIds.contains(deviceUsageId));
            }
        }
@@ -465,6 +471,16 @@
            vo.setName(entry.getKey());
            vo.setTotal(entry.getValue().size());
            // 设置设备类型字段
            // 从当前分组的第一个设备使用记录中获取设备类型
            if (!entry.getValue().isEmpty()) {
                DeviceUsage firstUsage = entry.getValue().get(0);
                Device device = finalDeviceMap.get(firstUsage.getDeviceId());
                if (device != null && device.getType() != null) {
                    vo.setType(device.getType());
                }
            }
            long inspectedCount = entry.getValue().stream()
                    .filter(d -> deviceInspectionMap.getOrDefault(d.getId(), false))
                    .count();
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/RecordServiceImpl.java
@@ -6,6 +6,7 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.system.mapper.RecordImageMapper;
import com.ruoyi.system.mapper.RecordMapper;
import com.ruoyi.system.model.ErrorReview;
import com.ruoyi.system.model.Record;
import com.ruoyi.system.model.RecordImage;
import com.ruoyi.system.model.UserFile;
@@ -70,9 +71,14 @@
    }
    @Override
    public ProblemVo getHiddenDangerDetail(Integer problemId) {
    public ProblemVo getHiddenDangerDetail(Integer id) {
        // 根据id找到记录
        Record record = baseMapper.selectById(id);
        // 直接查询并返回,让前端处理JSON字符串解析
        ProblemVo hiddenDangerDetail = baseMapper.getHiddenDangerDetail(problemId);
        ProblemVo hiddenDangerDetail = baseMapper.getHiddenDangerDetail(record.getProblemId());
        String projectName =baseMapper.getProjectName(record.getCheckTaskId());
        hiddenDangerDetail.setProjectName(projectName);
        if(hiddenDangerDetail!=null){
            if (hiddenDangerDetail.getSpecificTermsStr()!=null && !hiddenDangerDetail.getSpecificTermsStr().isEmpty()) {
                String specificTermsStr = hiddenDangerDetail.getSpecificTermsStr();
@@ -89,6 +95,17 @@
                hiddenDangerDetail.setNumberFactors(JSON.parseArray(numberFactorsStr, String.class));
            }
        }
        Integer errorReviewId = record.getErrorReviewId();
        if(errorReviewId!=null){
            ErrorReview errorReview = baseMapper.getErrorReview(errorReviewId);
            if(errorReview!=null){
                // 查询图片
                List<String> img = baseMapper.findReviewImg(errorReviewId);
                errorReview.setImg(img);
                hiddenDangerDetail.setErrorReview(errorReview);
            }
        }
        return hiddenDangerDetail;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ReportServiceImpl.java
File was deleted
ruoyi-system/src/main/java/com/ruoyi/system/vo/DeviceCountVo.java
@@ -12,4 +12,7 @@
    @ApiModelProperty("设备类型")
    private Integer type;
    @ApiModelProperty("设备名称")
    private String name;
}
ruoyi-system/src/main/java/com/ruoyi/system/vo/FixedAssetDashboardVO.java
@@ -20,9 +20,6 @@
    @ApiModelProperty("项目总数")
    private Integer projectTotal;
    @ApiModelProperty("检查覆盖率(百分比,如:77 表示 77%)")
    private Integer inspectionCoverage;
    // ==================== 本月检查情况 ====================
    @ApiModelProperty("已检查项目数")
    private Integer inspectedCount;
ruoyi-system/src/main/java/com/ruoyi/system/vo/MachineryStatisticsVO.java
@@ -13,6 +13,8 @@
@Data
@ApiModel("机械设备统计数据")
public class MachineryStatisticsVO {
    @ApiModelProperty("设备类型")
    private Integer type;
    @ApiModelProperty("设备名称")
    private String name;
ruoyi-system/src/main/java/com/ruoyi/system/vo/ProblemVo.java
@@ -1,6 +1,7 @@
package com.ruoyi.system.vo;
import com.ruoyi.system.model.ErrorReview;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
@@ -68,4 +69,10 @@
    @ApiModelProperty(value = "问题数量集合")
    List<String> numberFactors;
    @ApiModelProperty(value = "复核信息")
    ErrorReview errorReview;
    @ApiModelProperty(value = "项目名称")
    String projectName;
}
ruoyi-system/src/main/resources/mapper/system/RecordMapper.xml
@@ -60,7 +60,7 @@
            and t1.create_time between #{query.startTime} and #{query.endTime}
        </if>
        ) as o
        ) as o where 1=1
        <if test="query.type != null">
            and o.type = #{query.type}
        </if>
@@ -72,5 +72,27 @@
        from problem
        where id = #{problemId}
    </select>
    <select id="getErrorReview" resultType="com.ruoyi.system.model.ErrorReview">
        select t1.id, t1.create_time,
            t1.deleted,
            t1.name,
            t1.update_time,
            t1.content,
            t1.state,
            t1.creator_id, t2.name creator
        from error_review t1
                 left join user t2 on t1.creator_id = t2.id
        where t1.id = #{errorReviewId}
    </select>
    <select id="findReviewImg" resultType="java.lang.String">
        select CONCAT(t2.name, '.', t2.extension) from error_review_image t1
                 left join user_file t2 on t1.user_file_id = t2.id
        where t1.error_review_id = #{errorReviewId}
    </select>
    <select id="getProjectName" resultType="java.lang.String">
        select t2.name from task t1 left join project t2 on t1.project_id = t2.id
                    where t1.id = #{checkTaskId}
    </select>
</mapper>