From 5b56af3a13d7f36ad0d5b24fac83d14d7b622954 Mon Sep 17 00:00:00 2001
From: “mitao” <“2763622819@qq.com”>
Date: 星期三, 21 一月 2026 21:06:30 +0800
Subject: [PATCH] feat:新增委托方大屏首页接口
---
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ClientServiceImpl.java | 495 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 495 insertions(+), 0 deletions(-)
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ClientServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ClientServiceImpl.java
index b760b87..76ffe50 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/ClientServiceImpl.java
+++ b/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());
+ }
}
--
Gitblit v1.7.1