diff --git a/IOT_API/Controllers/Inspection/StatisticsReportController.cs b/IOT_API/Controllers/Inspection/StatisticsReportController.cs index a063e36..371a068 100644 --- a/IOT_API/Controllers/Inspection/StatisticsReportController.cs +++ b/IOT_API/Controllers/Inspection/StatisticsReportController.cs @@ -1,14 +1,114 @@ using Microsoft.AspNetCore.Mvc; +using Service.Interface; +using System.Threading.Tasks; namespace WebAPI.Controllers { /// - /// 统计报表 + /// 统计报表(设备统计/维护报表/维修报表) /// [ApiController] [Route("api/inspection/report")] public class StatisticsReportController : ControllerBase { - // TODO: 实现 统计报表 相关接口 + private readonly IStatisticsReportService _statisticsReportService; + + public StatisticsReportController(IStatisticsReportService statisticsReportService) + { + _statisticsReportService = statisticsReportService; + } + + #region 设备统计报表 + + /// + /// 设备统计概览(总数/在用/闲置/报废等) + /// + [HttpGet("device/statistics")] + public async Task GetDeviceStatistics() + { + return Ok(await _statisticsReportService.GetDeviceStatisticsAsync()); + } + + /// + /// 按分类统计设备数量 + /// + [HttpGet("device/category")] + public async Task GetCategoryStatistics() + { + return Ok(await _statisticsReportService.GetCategoryStatisticsAsync()); + } + + /// + /// 按部门统计设备数量 + /// + [HttpGet("device/department")] + public async Task GetDepartmentStatistics() + { + return Ok(await _statisticsReportService.GetDepartmentStatisticsAsync()); + } + + #endregion + + #region 设备维护报表 + + /// + /// 设备维护统计(校准/保养及时率、超期数量等) + /// + [HttpGet("maintenance/statistics")] + public async Task GetMaintenanceStatistics() + { + return Ok(await _statisticsReportService.GetMaintenanceStatisticsAsync()); + } + + /// + /// 维护超期设备明细 + /// + [HttpGet("maintenance/overdue")] + public async Task GetMaintenanceOverdueDetails() + { + return Ok(await _statisticsReportService.GetMaintenanceOverdueDetailsAsync()); + } + + #endregion + + #region 维修类报表 + + /// + /// 维修统计概览 + /// + [HttpGet("repair/statistics")] + public async Task GetRepairStatistics() + { + return Ok(await _statisticsReportService.GetRepairStatisticsAsync()); + } + + /// + /// 故障类型分布 + /// + [HttpGet("repair/fault-type")] + public async Task GetFaultTypeDistribution() + { + return Ok(await _statisticsReportService.GetFaultTypeDistributionAsync()); + } + + /// + /// 维修工时分析 + /// + [HttpGet("repair/hours-analysis")] + public async Task GetRepairHoursAnalysis() + { + return Ok(await _statisticsReportService.GetRepairHoursAnalysisAsync()); + } + + /// + /// 维修记录明细列表 + /// + [HttpGet("repair/records")] + public async Task GetRepairRecordDetails() + { + return Ok(await _statisticsReportService.GetRepairRecordDetailsAsync()); + } + + #endregion } } diff --git a/Model/Dto/Inspection/StatisticsReportDto.cs b/Model/Dto/Inspection/StatisticsReportDto.cs new file mode 100644 index 0000000..a0c8f9e --- /dev/null +++ b/Model/Dto/Inspection/StatisticsReportDto.cs @@ -0,0 +1,212 @@ +namespace Model.Dto.Inspection +{ + #region 设备统计报表 + + /// + /// 设备统计概览 + /// + public class DeviceStatisticsDto + { + /// 设备总数 + public int Total { get; set; } + + /// 在用数量 + public int InUse { get; set; } + + /// 闲置数量 + public int Idle { get; set; } + + /// 报废数量(已处置+待处置) + public int Scrapped { get; set; } + + /// 维修中数量 + public int Repairing { get; set; } + + /// 已停用数量 + public int Suspended { get; set; } + + /// 其他数量(待验收/已借出/入库) + public int Other { get; set; } + } + + /// + /// 按分类统计 + /// + public class CategoryStatisticsDto + { + /// 分类名称 + public string? CategoryName { get; set; } + + /// 设备数量 + public int Count { get; set; } + } + + /// + /// 按部门统计 + /// + public class DepartmentStatisticsDto + { + /// 部门名称 + public string? Department { get; set; } + + /// 设备数量 + public int Count { get; set; } + } + + #endregion + + #region 设备维护报表 + + /// + /// 设备维护统计 + /// + public class MaintenanceStatisticsDto + { + /// 设备总数 + public int Total { get; set; } + + /// 校准正常数量 + public int CalibrationNormal { get; set; } + + /// 校准超期数量 + public int CalibrationOverdue { get; set; } + + /// 保养正常数量 + public int MaintenanceNormal { get; set; } + + /// 保养超期数量 + public int MaintenanceOverdue { get; set; } + + /// 校准及时率(%) + public decimal CalibrationTimelyRate { get; set; } + + /// 保养及时率(%) + public decimal MaintenanceTimelyRate { get; set; } + } + + /// + /// 维护超期设备明细 + /// + public class MaintenanceOverdueDetailDto + { + /// 设备编号 + public string? EquipmentCode { get; set; } + + /// 设备名称 + public string? EquipmentName { get; set; } + + /// 部门 + public string? Department { get; set; } + + /// 超期类型(校准超期/保养超期) + public string? OverdueType { get; set; } + + /// 到期日期 + public DateTime? ExpireDate { get; set; } + + /// 超期天数 + public int OverdueDays { get; set; } + } + + #endregion + + #region 维修类报表 + + /// + /// 维修统计概览 + /// + public class RepairStatisticsDto + { + /// 维修记录总数 + public int Total { get; set; } + + /// 已完成数量 + public int Completed { get; set; } + + /// 维修中数量 + public int InProgress { get; set; } + + /// 待维修数量 + public int Pending { get; set; } + + /// 平均维修工时(小时) + public decimal AvgRepairHours { get; set; } + + /// 总维修工时(小时) + public decimal TotalRepairHours { get; set; } + + /// 总维修费用(元) + public decimal TotalRepairCost { get; set; } + } + + /// + /// 故障类型分布 + /// + public class FaultTypeDistributionDto + { + /// 故障类型 + public string? FaultType { get; set; } + + /// 数量 + public int Count { get; set; } + } + + /// + /// 维修工时分析 + /// + public class RepairHoursAnalysisDto + { + /// 故障类型 + public string? FaultType { get; set; } + + /// 平均工时(小时) + public decimal AvgHours { get; set; } + + /// 最大工时(小时) + public decimal MaxHours { get; set; } + + /// 最小工时(小时) + public decimal MinHours { get; set; } + + /// 记录数 + public int Count { get; set; } + } + + /// + /// 维修记录明细 + /// + public class RepairRecordDetailDto + { + /// 设备编号 + public string? EquipmentCode { get; set; } + + /// 设备名称 + public string? EquipmentName { get; set; } + + /// 故障类型 + public string? FaultType { get; set; } + + /// 故障描述 + public string? FaultDescription { get; set; } + + /// 报修日期 + public DateTime? ReportDate { get; set; } + + /// 维修完成日期 + public DateTime? RepairEndDate { get; set; } + + /// 维修工时 + public decimal? RepairHours { get; set; } + + /// 维修人员 + public string? RepairPerson { get; set; } + + /// 维修费用 + public decimal? RepairCost { get; set; } + + /// 维修状态 + public string? RepairStatus { get; set; } + } + + #endregion +} diff --git a/Model/Entity/Inspection/RepairRecordEntity.cs b/Model/Entity/Inspection/RepairRecordEntity.cs new file mode 100644 index 0000000..7c155e9 --- /dev/null +++ b/Model/Entity/Inspection/RepairRecordEntity.cs @@ -0,0 +1,92 @@ +using SqlSugar; + +namespace Model.Entity.Inspection +{ + /// + /// 设备维修记录 + /// + public class RepairRecordEntity : BaseEntity + { + /// + /// 设备Id(关联 EquipmentEntity.Id) + /// + public long EquipmentId { get; set; } + + /// + /// 设备编号(冗余便于展示) + /// + [SugarColumn(Length = 50, IsNullable = true)] + public string? EquipmentCode { get; set; } + + /// + /// 设备名称(冗余便于展示) + /// + [SugarColumn(Length = 100, IsNullable = true)] + public string? EquipmentName { get; set; } + + /// + /// 故障类型(机械故障/电气故障/软件故障/传感器故障/其他) + /// + [SugarColumn(Length = 50, IsNullable = true)] + public string? FaultType { get; set; } + + /// + /// 故障描述 + /// + [SugarColumn(ColumnDataType = "text", IsNullable = true)] + public string? FaultDescription { get; set; } + + /// + /// 报修日期 + /// + public DateTime? ReportDate { get; set; } + + /// + /// 维修开始日期 + /// + [SugarColumn(IsNullable = true)] + public DateTime? RepairStartDate { get; set; } + + /// + /// 维修完成日期 + /// + [SugarColumn(IsNullable = true)] + public DateTime? RepairEndDate { get; set; } + + /// + /// 维修工时(小时) + /// + [SugarColumn(IsNullable = true)] + public decimal? RepairHours { get; set; } + + /// + /// 维修人员 + /// + [SugarColumn(Length = 50, IsNullable = true)] + public string? RepairPerson { get; set; } + + /// + /// 维修费用(元) + /// + [SugarColumn(DecimalDigits = 2, Length = 18, IsNullable = true)] + public decimal? RepairCost { get; set; } + + /// + /// 维修状态(待维修/维修中/已完成) + /// + [SugarColumn(Length = 20, IsNullable = true)] + public string? RepairStatus { get; set; } + + /// + /// 维修结果描述 + /// + [SugarColumn(ColumnDataType = "text", IsNullable = true)] + public string? RepairResult { get; set; } + + /// + /// 备注 + /// + [SugarColumn(Length = 500, IsNullable = true)] + public string? Remark { get; set; } + } +} diff --git a/Service/Implement/Inspection/StatisticsReportService.cs b/Service/Implement/Inspection/StatisticsReportService.cs index 2c22ab0..8e33536 100644 --- a/Service/Implement/Inspection/StatisticsReportService.cs +++ b/Service/Implement/Inspection/StatisticsReportService.cs @@ -1,4 +1,14 @@ +using Model; +using Model.Dto.Inspection; +using Model.Entity.Asset; +using Model.Entity.Inspection; +using ORM; using Service.Interface; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; namespace Service.Implement { @@ -7,6 +17,326 @@ namespace Service.Implement /// public class StatisticsReportService : IStatisticsReportService { - // TODO: 实现 统计报表 相关方法 + #region 设备统计报表 + + /// + /// 设备统计概览(总数/在用/闲置/报废等) + /// + public async Task> GetDeviceStatisticsAsync() + { + try + { + var query = SqlSugarContext.DbContext.Queryable() + .Where(x => x.IsDel == 0); + + var all = await query.ToListAsync(); + var total = all.Count; + + var dto = new DeviceStatisticsDto + { + Total = total, + InUse = all.Count(x => x.Status == EquipmentStatusEnum.InUse), + Idle = all.Count(x => x.Status == EquipmentStatusEnum.Idle), + Scrapped = all.Count(x => x.Status == EquipmentStatusEnum.Disposed || x.Status == EquipmentStatusEnum.PendingDisposal), + Repairing = all.Count(x => x.Status == EquipmentStatusEnum.Repairing), + Suspended = all.Count(x => x.Status == EquipmentStatusEnum.Suspended), + Other = all.Count(x => + x.Status != EquipmentStatusEnum.InUse && + x.Status != EquipmentStatusEnum.Idle && + x.Status != EquipmentStatusEnum.Disposed && + x.Status != EquipmentStatusEnum.PendingDisposal && + x.Status != EquipmentStatusEnum.Repairing && + x.Status != EquipmentStatusEnum.Suspended) + }; + + return Result.Success(dto); + } + catch (Exception ex) + { + return Result.Error("查询设备统计失败", ex); + } + } + + /// + /// 按分类统计设备数量 + /// + public async Task>> GetCategoryStatisticsAsync() + { + try + { + var list = await SqlSugarContext.DbContext.Queryable() + .LeftJoin((e, c) => e.CategoryId == c.Id) + .Where((e, c) => e.IsDel == 0) + .GroupBy((e, c) => new { c.Name }) + .Select((e, c) => new CategoryStatisticsDto + { + CategoryName = c.Name, + Count = SqlFunc.AggregateCount(e.Id) + }) + .ToListAsync(); + + return Result>.Success(list); + } + catch (Exception ex) + { + return Result>.Error("查询分类统计失败", ex); + } + } + + /// + /// 按部门统计设备数量 + /// + public async Task>> GetDepartmentStatisticsAsync() + { + try + { + var list = await SqlSugarContext.DbContext.Queryable() + .Where(x => x.IsDel == 0) + .GroupBy(x => x.Department) + .Select(x => new DepartmentStatisticsDto + { + Department = x.Department, + Count = SqlFunc.AggregateCount(x.Id) + }) + .OrderByDescending(x => x.Count) + .ToListAsync(); + + return Result>.Success(list); + } + catch (Exception ex) + { + return Result>.Error("查询部门统计失败", ex); + } + } + + #endregion + + #region 设备维护报表 + + /// + /// 设备维护统计(校准/保养及时率、超期数量等) + /// + public async Task> GetMaintenanceStatisticsAsync() + { + try + { + var all = await SqlSugarContext.DbContext.Queryable() + .Where(x => x.IsDel == 0) + .ToListAsync(); + + var total = all.Count; + var calNormal = all.Count(x => x.CalibrationStatus == ComplianceStatusEnum.Normal); + var calOverdue = all.Count(x => x.CalibrationStatus == ComplianceStatusEnum.Overdue); + var maintNormal = all.Count(x => x.MaintenanceStatus == ComplianceStatusEnum.Normal); + var maintOverdue = all.Count(x => x.MaintenanceStatus == ComplianceStatusEnum.Overdue); + + var dto = new MaintenanceStatisticsDto + { + Total = total, + CalibrationNormal = calNormal, + CalibrationOverdue = calOverdue, + MaintenanceNormal = maintNormal, + MaintenanceOverdue = maintOverdue, + CalibrationTimelyRate = total > 0 ? Math.Round((decimal)calNormal / total * 100, 1) : 0, + MaintenanceTimelyRate = total > 0 ? Math.Round((decimal)maintNormal / total * 100, 1) : 0 + }; + + return Result.Success(dto); + } + catch (Exception ex) + { + return Result.Error("查询维护统计失败", ex); + } + } + + /// + /// 维护超期设备明细 + /// + public async Task>> GetMaintenanceOverdueDetailsAsync() + { + try + { + var now = DateTime.Now; + var list = await SqlSugarContext.DbContext.Queryable() + .Where(x => x.IsDel == 0 && + (x.CalibrationStatus == ComplianceStatusEnum.Overdue || + x.MaintenanceStatus == ComplianceStatusEnum.Overdue)) + .ToListAsync(); + + var details = new List(); + foreach (var eq in list) + { + if (eq.CalibrationStatus == ComplianceStatusEnum.Overdue && eq.NextCalibrationDate.HasValue) + { + var days = (int)(now - eq.NextCalibrationDate.Value).TotalDays; + details.Add(new MaintenanceOverdueDetailDto + { + EquipmentCode = eq.Code, + EquipmentName = eq.Name, + Department = eq.Department, + OverdueType = "校准超期", + ExpireDate = eq.NextCalibrationDate, + OverdueDays = days > 0 ? days : 0 + }); + } + if (eq.MaintenanceStatus == ComplianceStatusEnum.Overdue && eq.NextMaintenanceDate.HasValue) + { + var days = (int)(now - eq.NextMaintenanceDate.Value).TotalDays; + details.Add(new MaintenanceOverdueDetailDto + { + EquipmentCode = eq.Code, + EquipmentName = eq.Name, + Department = eq.Department, + OverdueType = "保养超期", + ExpireDate = eq.NextMaintenanceDate, + OverdueDays = days > 0 ? days : 0 + }); + } + } + + return Result>.Success(details); + } + catch (Exception ex) + { + return Result>.Error("查询维护超期明细失败", ex); + } + } + + #endregion + + #region 维修类报表 + + /// + /// 维修统计概览 + /// + public async Task> GetRepairStatisticsAsync() + { + try + { + var all = await SqlSugarContext.DbContext.Queryable() + .Where(x => x.IsDel == 0) + .ToListAsync(); + + var total = all.Count; + var completed = all.Count(x => x.RepairStatus == "已完成"); + var inProgress = all.Count(x => x.RepairStatus == "维修中"); + var pending = all.Count(x => x.RepairStatus == "待维修"); + + var totalHours = all.Where(x => x.RepairHours.HasValue).Sum(x => x.RepairHours ?? 0); + var completedWithHours = all.Where(x => x.RepairHours.HasValue && x.RepairStatus == "已完成").ToList(); + var avgHours = completedWithHours.Count > 0 + ? Math.Round(completedWithHours.Sum(x => x.RepairHours ?? 0) / completedWithHours.Count, 1) + : 0; + + var totalCost = all.Where(x => x.RepairCost.HasValue).Sum(x => x.RepairCost ?? 0); + + var dto = new RepairStatisticsDto + { + Total = total, + Completed = completed, + InProgress = inProgress, + Pending = pending, + AvgRepairHours = avgHours, + TotalRepairHours = totalHours, + TotalRepairCost = totalCost + }; + + return Result.Success(dto); + } + catch (Exception ex) + { + return Result.Error("查询维修统计失败", ex); + } + } + + /// + /// 故障类型分布 + /// + public async Task>> GetFaultTypeDistributionAsync() + { + try + { + var list = await SqlSugarContext.DbContext.Queryable() + .Where(x => x.IsDel == 0) + .GroupBy(x => x.FaultType) + .Select(x => new FaultTypeDistributionDto + { + FaultType = x.FaultType, + Count = SqlFunc.AggregateCount(x.Id) + }) + .OrderByDescending(x => x.Count) + .ToListAsync(); + + return Result>.Success(list); + } + catch (Exception ex) + { + return Result>.Error("查询故障类型分布失败", ex); + } + } + + /// + /// 维修工时分析 + /// + public async Task>> GetRepairHoursAnalysisAsync() + { + try + { + var list = await SqlSugarContext.DbContext.Queryable() + .Where(x => x.IsDel == 0 && x.RepairHours.HasValue) + .GroupBy(x => x.FaultType) + .Select(x => new RepairHoursAnalysisDto + { + FaultType = x.FaultType, + AvgHours = SqlFunc.AggregateAvg(x.RepairHours) ?? 0, + MaxHours = SqlFunc.AggregateMax(x.RepairHours) ?? 0, + MinHours = SqlFunc.AggregateMin(x.RepairHours) ?? 0, + Count = SqlFunc.AggregateCount(x.Id) + }) + .OrderByDescending(x => x.AvgHours) + .ToListAsync(); + + return Result>.Success(list); + } + catch (Exception ex) + { + return Result>.Error("查询维修工时分析失败", ex); + } + } + + /// + /// 维修记录明细列表 + /// + public async Task>> GetRepairRecordDetailsAsync() + { + try + { + var list = await SqlSugarContext.DbContext.Queryable() + .Where(x => x.IsDel == 0) + .OrderByDescending(x => x.CreateTime) + .Select(x => new RepairRecordDetailDto + { + EquipmentCode = x.EquipmentCode, + EquipmentName = x.EquipmentName, + FaultType = x.FaultType, + FaultDescription = x.FaultDescription, + ReportDate = x.ReportDate, + RepairEndDate = x.RepairEndDate, + RepairHours = x.RepairHours, + RepairPerson = x.RepairPerson, + RepairCost = x.RepairCost, + RepairStatus = x.RepairStatus + }) + .ToListAsync(); + + return Result>.Success(list); + } + catch (Exception ex) + { + return Result>.Error("查询维修记录明细失败", ex); + } + } + + #endregion } } diff --git a/Service/Interface/Inspection/IStatisticsReportService.cs b/Service/Interface/Inspection/IStatisticsReportService.cs index f81ce8e..a00e179 100644 --- a/Service/Interface/Inspection/IStatisticsReportService.cs +++ b/Service/Interface/Inspection/IStatisticsReportService.cs @@ -1,3 +1,8 @@ +using Model; +using Model.Dto.Inspection; +using System.Collections.Generic; +using System.Threading.Tasks; + namespace Service.Interface { /// @@ -5,6 +10,49 @@ namespace Service.Interface /// public interface IStatisticsReportService { - // TODO: 定义 统计报表 相关方法 + /// + /// 设备统计概览(总数/在用/闲置/报废等) + /// + Task> GetDeviceStatisticsAsync(); + + /// + /// 按分类统计设备数量 + /// + Task>> GetCategoryStatisticsAsync(); + + /// + /// 按部门统计设备数量 + /// + Task>> GetDepartmentStatisticsAsync(); + + /// + /// 设备维护统计(校准/保养及时率、超期数量等) + /// + Task> GetMaintenanceStatisticsAsync(); + + /// + /// 维护超期设备明细 + /// + Task>> GetMaintenanceOverdueDetailsAsync(); + + /// + /// 维修统计概览 + /// + Task> GetRepairStatisticsAsync(); + + /// + /// 故障类型分布 + /// + Task>> GetFaultTypeDistributionAsync(); + + /// + /// 维修工时分析 + /// + Task>> GetRepairHoursAnalysisAsync(); + + /// + /// 维修记录明细列表 + /// + Task>> GetRepairRecordDetailsAsync(); } }