“mitao”
2026-01-21 5b56af3a13d7f36ad0d5b24fac83d14d7b622954
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ClientServiceImpl.java
@@ -1,11 +1,37 @@
package com.ruoyi.system.service.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
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.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.model.Record;
import com.ruoyi.system.service.ClientService;
import com.ruoyi.system.service.ProjectCategoryCalculator;
import com.ruoyi.system.vo.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
/**
 * <p>
@@ -16,6 +42,475 @@
 * @since 2026-01-12
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ClientServiceImpl extends ServiceImpl<ClientMapper, Client> implements ClientService {
    private final ProjectMapper projectMapper;
    private final PlanMapper planMapper;
    private final CheckMapper checkMapper;
    private final DeviceUsageMapper deviceUsageMapper;
    private final RecordMapper recordMapper;
    private final ProjectCategoryCalculator projectCategoryCalculator;
    @Override
    public ClientDashboardVO getClientDashboard(ClientDashboardQuery query) {
        ClientDashboardVO vo = new ClientDashboardVO();
        vo.setProjectCategory(query.getProjectCategory());
        // 获取委托方ID(如果未传则需要从当前登录用户获取,这里暂时使用传入的值)
        Long clientId = query.getClientId();
        // 获取日期范围
        Date startDate = getStartDate(query.getStartDate());
        Date endDate = getEndDate(query.getEndDate());
        // 根据项目类型进行不同的统计
        if ("ongoing".equals(query.getProjectCategory())) {
            // 在建工程统计
            OngoingProjectDashboardVO ongoingVO = calculateOngoingProjectStatistics(clientId, startDate, endDate);
            vo.setOngoingProject(ongoingVO);
        } else if ("fixed".equals(query.getProjectCategory())) {
            // 固定资产统计
            FixedAssetDashboardVO fixedVO = calculateFixedAssetStatistics(clientId, startDate, endDate);
            vo.setFixedAsset(fixedVO);
        }
        return vo;
    }
    /**
     * 在建工程统计
     */
    private OngoingProjectDashboardVO calculateOngoingProjectStatistics(Long clientId, Date monthStart, Date monthEnd) {
        OngoingProjectDashboardVO vo = new OngoingProjectDashboardVO();
        // 查询在建工程项目(usage_type 表示项目用途类型,假设 0 表示在建工程)
        LambdaQueryWrapper<Project> projectWrapper = new LambdaQueryWrapper<>();
        projectWrapper.eq(Project::getDeleted, false)
                .eq(Project::getUsageType, 0); // 在建工程
        if (clientId != null) {
            projectWrapper.eq(Project::getClientId, clientId);
        }
        List<Project> projects = projectMapper.selectList(projectWrapper);
        // 1. 统计项目总数及分类
        vo.setProjectTotal(projects.size());
        long housingCount = projects.stream().filter(p -> p.getType() != null && p.getType() == 0).count();
        long municipalCount = projects.stream().filter(p -> p.getType() != null && p.getType() == 1).count();
        vo.setHousingProjectCount((int) housingCount);
        vo.setMunicipalProjectCount((int) municipalCount);
        // 计算检查覆盖率
        List<Integer> projectIds = projects.stream().map(Project::getId).collect(Collectors.toList());
        if (!projectIds.isEmpty()) {
            LambdaQueryWrapper<Check> checkWrapper = new LambdaQueryWrapper<>();
            checkWrapper.in(Check::getProjectId, projectIds);
            List<Check> checks = checkMapper.selectList(checkWrapper);
            List<Integer> checkedProjectIds = checks.stream()
                    .map(Check::getProjectId)
                    .distinct()
                    .collect(Collectors.toList());
            // 房建检查覆盖率
            List<Integer> housingProjectIds = projects.stream()
                    .filter(p -> p.getType() != null && p.getType() == 0)
                    .map(Project::getId)
                    .toList();
            long checkedHousingCount = checkedProjectIds.stream()
                    .filter(housingProjectIds::contains)
                    .count();
            vo.setHousingInspectionCoverage(
                    housingCount > 0 ? (int) Math.round(checkedHousingCount * 100.0 / housingCount) : 0
            );
            // 市政检查覆盖率
            List<Integer> municipalProjectIds = projects.stream()
                    .filter(p -> p.getType() != null && p.getType() == 1)
                    .map(Project::getId)
                    .toList();
            long checkedMunicipalCount = checkedProjectIds.stream()
                    .filter(municipalProjectIds::contains)
                    .count();
            vo.setMunicipalInspectionCoverage(
                    municipalCount > 0 ? (int) Math.round(checkedMunicipalCount * 100.0 / municipalCount) : 0
            );
        } else {
            vo.setHousingInspectionCoverage(0);
            vo.setMunicipalInspectionCoverage(0);
        }
        // 2. 统计本月检查情况
        calculateMonthlyInspectionForOngoing(vo, projects, projectIds, monthStart, monthEnd);
        // 3. 统计大型机械设备数
        vo.setMachineryStatistics(calculateMachineryStatistics(projectIds));
        // 4. 统计重大隐患数
        calculateHazardStatistics(vo, projectIds);
        // 5. 获取地图标点数据
        vo.setProjectMapPoints(getProjectMapPoints(projects, clientId, monthStart, monthEnd));
        return vo;
    }
    /**
     * 固定资产统计
     */
    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); // 固定资产
        if (clientId != null) {
            projectWrapper.eq(Project::getClientId, clientId);
        }
        List<Project> projects = projectMapper.selectList(projectWrapper);
        // 1. 统计项目总数
        vo.setProjectTotal(projects.size());
        // 计算检查覆盖率
        List<Integer> projectIds = projects.stream().map(Project::getId).collect(Collectors.toList());
        if (!projectIds.isEmpty()) {
            LambdaQueryWrapper<Check> checkWrapper = new LambdaQueryWrapper<>();
            checkWrapper.in(Check::getProjectId, projectIds);
            List<Check> checks = checkMapper.selectList(checkWrapper);
            long checkedProjectCount = checks.stream()
                    .map(Check::getProjectId)
                    .distinct()
                    .count();
            vo.setInspectionCoverage(
                    projects.size() > 0 ? (int) Math.round(checkedProjectCount * 100.0 / projects.size()) : 0
            );
        } else {
            vo.setInspectionCoverage(0);
        }
        // 2. 统计本月检查情况
        calculateMonthlyInspectionForFixed(vo, projects, projectIds, monthStart, monthEnd);
        // 3. 统计重大隐患数
        calculateHazardStatisticsForFixed(vo, projectIds);
        // 4. 获取地图标点数据
        vo.setProjectMapPoints(getProjectMapPoints(projects, clientId, monthStart, monthEnd));
        return vo;
    }
    /**
     * 统计在建工程本月检查情况
     */
    private void calculateMonthlyInspectionForOngoing(OngoingProjectDashboardVO vo, List<Project> projects,
                                                      List<Integer> projectIds, Date monthStart, Date monthEnd) {
        if (projectIds.isEmpty()) {
            vo.setInspectedCount(0);
            vo.setInspectedHousingCount(0);
            vo.setInspectedMunicipalCount(0);
            vo.setUninspectedCount(projects.size());
            vo.setUninspectedHousingCount(vo.getHousingProjectCount());
            vo.setUninspectedMunicipalCount(vo.getMunicipalProjectCount());
            return;
        }
        // 查询本月有检查记录的项目
        LambdaQueryWrapper<Check> checkWrapper = new LambdaQueryWrapper<>();
        checkWrapper.in(Check::getProjectId, projectIds)
                .ge(Check::getTime, monthStart)
                .le(Check::getTime, monthEnd);
        List<Check> monthlyChecks = checkMapper.selectList(checkWrapper);
        List<Integer> inspectedProjectIds = monthlyChecks.stream()
                .map(Check::getProjectId)
                .distinct()
                .toList();
        // 已检查项目统计
        List<Project> inspectedProjects = projects.stream()
                .filter(p -> inspectedProjectIds.contains(p.getId()))
                .toList();
        vo.setInspectedCount(inspectedProjects.size());
        vo.setInspectedHousingCount((int) inspectedProjects.stream()
                .filter(p -> p.getType() != null && p.getType() == 0).count());
        vo.setInspectedMunicipalCount((int) inspectedProjects.stream()
                .filter(p -> p.getType() != null && p.getType() == 1).count());
        // 未检查项目统计
        List<Project> uninspectedProjects = projects.stream()
                .filter(p -> !inspectedProjectIds.contains(p.getId()))
                .toList();
        vo.setUninspectedCount(uninspectedProjects.size());
        vo.setUninspectedHousingCount((int) uninspectedProjects.stream()
                .filter(p -> p.getType() != null && p.getType() == 0).count());
        vo.setUninspectedMunicipalCount((int) uninspectedProjects.stream()
                .filter(p -> p.getType() != null && p.getType() == 1).count());
    }
    /**
     * 统计固定资产本月检查情况
     */
    private void calculateMonthlyInspectionForFixed(FixedAssetDashboardVO vo, List<Project> projects,
                                                    List<Integer> projectIds, Date monthStart, Date monthEnd) {
        if (projectIds.isEmpty()) {
            vo.setInspectedCount(0);
            vo.setUninspectedCount(projects.size());
            return;
        }
        // 查询本月有检查记录的项目
        LambdaQueryWrapper<Check> checkWrapper = new LambdaQueryWrapper<>();
        checkWrapper.in(Check::getProjectId, projectIds)
                .ge(Check::getTime, monthStart)
                .le(Check::getTime, monthEnd);
        List<Check> monthlyChecks = checkMapper.selectList(checkWrapper);
        long inspectedCount = monthlyChecks.stream()
                .map(Check::getProjectId)
                .distinct()
                .count();
        vo.setInspectedCount((int) inspectedCount);
        vo.setUninspectedCount(projects.size() - (int) inspectedCount);
    }
    /**
     * 统计大型机械设备数(含已检查/未检查)
     */
    private List<MachineryStatisticsVO> calculateMachineryStatistics(List<Integer> projectIds) {
        if (projectIds.isEmpty()) {
            return new ArrayList<>();
        }
        // 查询设备使用记录
        LambdaQueryWrapper<DeviceUsage> deviceWrapper = new LambdaQueryWrapper<>();
        deviceWrapper.eq(DeviceUsage::getDeleted, false)
                .in(DeviceUsage::getProjectId, projectIds);
        List<DeviceUsage> devices = deviceUsageMapper.selectList(deviceWrapper);
        // 按设备名称分组统计
        Map<String, List<DeviceUsage>> deviceGroupMap = devices.stream()
                .filter(d -> d.getName() != null && !d.getName().isEmpty())
                .collect(Collectors.groupingBy(DeviceUsage::getName));
        // 查询设备检查记录
        List<Integer> deviceIds = devices.stream().map(DeviceUsage::getId).collect(Collectors.toList());
        Map<Integer, Boolean> deviceInspectionMap = new HashMap<>();
        if (!deviceIds.isEmpty()) {
            LambdaQueryWrapper<Record> recordWrapper = new LambdaQueryWrapper<>();
            recordWrapper.eq(Record::getDeleted, false)
                    .in(Record::getDeviceId, deviceIds);
            List<Record> records = recordMapper.selectList(recordWrapper);
            Set<Integer> inspectedDeviceIds = records.stream()
                    .map(Record::getDeviceId)
                    .collect(Collectors.toSet());
            for (Integer deviceId : deviceIds) {
                deviceInspectionMap.put(deviceId, inspectedDeviceIds.contains(deviceId));
            }
        }
        // 构建统计结果
        List<MachineryStatisticsVO> result = new ArrayList<>();
        for (Map.Entry<String, List<DeviceUsage>> entry : deviceGroupMap.entrySet()) {
            MachineryStatisticsVO vo = new MachineryStatisticsVO();
            vo.setName(entry.getKey());
            vo.setTotal(entry.getValue().size());
            long inspectedCount = entry.getValue().stream()
                    .filter(d -> deviceInspectionMap.getOrDefault(d.getId(), false))
                    .count();
            vo.setInspectedCount((int) inspectedCount);
            vo.setUninspectedCount(entry.getValue().size() - (int) inspectedCount);
            result.add(vo);
        }
        return result;
    }
    /**
     * 统计在建工程重大隐患数
     */
    private void calculateHazardStatistics(OngoingProjectDashboardVO vo, List<Integer> projectIds) {
        if (projectIds.isEmpty()) {
            vo.setMajorHazardTotal(0);
            vo.setRectifiedHazardCount(0);
            vo.setUnrectifiedHazardCount(0);
            return;
        }
        // 查询检查任务
        LambdaQueryWrapper<Check> checkWrapper = new LambdaQueryWrapper<>();
        checkWrapper.in(Check::getProjectId, projectIds);
        List<Check> checks = checkMapper.selectList(checkWrapper);
        List<Integer> checkTaskIds = checks.stream().map(Check::getId).collect(Collectors.toList());
        if (checkTaskIds.isEmpty()) {
            vo.setMajorHazardTotal(0);
            vo.setRectifiedHazardCount(0);
            vo.setUnrectifiedHazardCount(0);
            return;
        }
        // 查询重大隐患记录(level 表示隐患等级,假设 3 表示重大隐患)
        LambdaQueryWrapper<Record> recordWrapper = new LambdaQueryWrapper<>();
        recordWrapper.eq(Record::getDeleted, false)
                .in(Record::getCheckTaskId, checkTaskIds)
                .eq(Record::getLevel, 3); // 重大隐患等级
        List<Record> majorHazards = recordMapper.selectList(recordWrapper);
        vo.setMajorHazardTotal(majorHazards.size());
        // 统计已整改和未整改数量
        long rectifiedCount = majorHazards.stream()
                .filter(r -> r.getReformed() != null && r.getReformed())
                .count();
        long unrectifiedCount = majorHazards.stream()
                .filter(r -> r.getReformed() == null || !r.getReformed())
                .count();
        vo.setRectifiedHazardCount((int) rectifiedCount);
        vo.setUnrectifiedHazardCount((int) unrectifiedCount);
    }
    /**
     * 统计固定资产重大隐患数
     */
    private void calculateHazardStatisticsForFixed(FixedAssetDashboardVO vo, List<Integer> projectIds) {
        if (projectIds.isEmpty()) {
            vo.setMajorHazardTotal(0);
            vo.setRectifiedHazardCount(0);
            vo.setUnrectifiedHazardCount(0);
            return;
        }
        // 查询检查任务
        LambdaQueryWrapper<Check> checkWrapper = new LambdaQueryWrapper<>();
        checkWrapper.in(Check::getProjectId, projectIds);
        List<Check> checks = checkMapper.selectList(checkWrapper);
        List<Integer> checkTaskIds = checks.stream().map(Check::getId).collect(Collectors.toList());
        if (checkTaskIds.isEmpty()) {
            vo.setMajorHazardTotal(0);
            vo.setRectifiedHazardCount(0);
            vo.setUnrectifiedHazardCount(0);
            return;
        }
        // 查询重大隐患记录(level 表示隐患等级,假设 3 表示重大隐患)
        LambdaQueryWrapper<Record> recordWrapper = new LambdaQueryWrapper<>();
        recordWrapper.eq(Record::getDeleted, false)
                .in(Record::getCheckTaskId, checkTaskIds)
                .eq(Record::getLevel, 3); // 重大隐患等级
        List<Record> majorHazards = recordMapper.selectList(recordWrapper);
        vo.setMajorHazardTotal(majorHazards.size());
        // 统计已整改和未整改数量
        long rectifiedCount = majorHazards.stream()
                .filter(r -> r.getReformed() != null && r.getReformed())
                .count();
        long unrectifiedCount = majorHazards.stream()
                .filter(r -> r.getReformed() == null || !r.getReformed())
                .count();
        vo.setRectifiedHazardCount((int) rectifiedCount);
        vo.setUnrectifiedHazardCount((int) unrectifiedCount);
    }
    /**
     * 获取项目地图标点数据
     */
    private List<ProjectMapPointVO> getProjectMapPoints(
            List<Project> projects, Long clientId, Date startDate, Date endDate) {
        List<ProjectMapPointVO> result = new ArrayList<>();
        // 计算项目类别
        Map<Integer, String> categoryMap;
        if (!projects.isEmpty()) {
            // 判断项目类型:在建工程 or 固定资产
            Integer usageType = projects.get(0).getUsageType();
            if (usageType != null && usageType == 0) {
                // 在建工程
                categoryMap = projectCategoryCalculator.calculateOngoingProjectCategories(
                        projects, clientId, startDate, endDate);
            } else if (usageType != null && usageType == 1) {
                // 固定资产
                categoryMap = projectCategoryCalculator.calculateFixedAssetCategories(
                        projects, clientId, startDate, endDate);
            } else {
                categoryMap = new HashMap<>();
            }
        } else {
            categoryMap = new HashMap<>();
        }
        // 构建地图标点数据
        for (Project p : projects) {
            if (!StringUtils.hasText(p.getAddressInfo())) {
                continue;
            }
            try {
                // 解析 address_info JSON 字符串
                JSONObject addressJson = JSON.parseObject(p.getAddressInfo());
                JSONArray gps = addressJson.getJSONArray("gps");
                if (gps != null && gps.size() >= 2) {
                    ProjectMapPointVO vo = new ProjectMapPointVO();
                    vo.setProjectId(p.getId());
                    vo.setProjectName(p.getName());
                    vo.setProjectType(p.getType());
                    // gps 数组格式:[经度, 纬度]
                    vo.setLongitude(gps.getString(0));
                    vo.setLatitude(gps.getString(1));
                    // 设置项目类别
                    vo.setCategory(categoryMap.get(p.getId()));
                    result.add(vo);
                }
            } catch (Exception e) {
                // 解析失败则跳过该项目
                log.warn("解析项目地址信息失败,项目ID: {}, address_info: {}", p.getId(), p.getAddressInfo(), e);
            }
        }
        return result;
    }
    /**
     * 获取开始日期
     */
    private Date getStartDate(Date startDate) {
        if (startDate != null) {
            return startDate;
        }
        // 默认为当前月份第一天
        YearMonth currentMonth = YearMonth.now();
        return Date.from(currentMonth.atDay(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
    }
    /**
     * 获取结束日期
     */
    private Date getEndDate(Date endDate) {
        if (endDate != null) {
            return endDate;
        }
        // 默认为当前月份最后一天
        YearMonth currentMonth = YearMonth.now();
        return Date.from(currentMonth.atEndOfMonth().atTime(23, 59, 59).atZone(ZoneId.systemDefault()).toInstant());
    }
}