odf_new/server/ZR.Service/Business/OdfCableFaultsService.cs
zpc a5039edcbb
Some checks are pending
continuous-integration/drone/push Build is running
feat: 添加表显里程矫正功能和故障频次管理
- 在故障添加页面新增表显里程矫正输入框
- 在故障详情页面显示表显里程矫正信息
- 实现故障频次增加功能,允许用户通过按钮增加故障发生频次
- 更新后端服务以支持故障频次的增减和相关数据的返回
2026-03-28 23:17:34 +08:00

353 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Infrastructure;
using Infrastructure.Attribute;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using ZR.Model;
using ZR.Model.Business;
using ZR.Model.Business.Dto;
using ZR.Model.Dto;
using ZR.Repository;
using ZR.Service.Business.IBusinessService;
namespace ZR.Service.Business
{
/// <summary>
/// 干线故障Service业务层处理
/// </summary>
[AppService(ServiceType = typeof(IOdfCableFaultsService), ServiceLifetime = LifeTime.Transient)]
public class OdfCableFaultsService : BaseService<OdfCableFaults>, IOdfCableFaultsService
{
/// <summary>
/// 按 CableId 分页查询故障列表,联查光缆名称,按 FaultTime DESC 排序
/// </summary>
public PagedInfo<dynamic> GetList(OdfCableFaultsQueryDto parm)
{
var predicate = QueryExp(parm);
var total = 0;
var list = Queryable()
.Where(predicate.ToExpression())
.LeftJoin<OdfCables>((f, c) => f.CableId == c.Id)
.Select((f, c) => new
{
f.Id,
f.CableId,
f.FaultTime,
f.Personnel,
f.FaultReason,
f.Mileage,
f.MileageCorrection,
f.Location,
f.Latitude,
f.Longitude,
f.Remark,
f.CreatedAt,
CableName = c.CableName
})
.MergeTable()
.OrderByDescending(it => it.FaultTime)
.ToPageList(parm.PageNum, parm.PageSize, ref total);
return new PagedInfo<dynamic>
{
Result = list.Cast<dynamic>().ToList(),
TotalNum = total,
PageIndex = parm.PageNum,
PageSize = parm.PageSize
};
}
/// <summary>
/// 查询故障详情,联查光缆名称和图片列表
/// </summary>
public object GetDetail(int id)
{
var fault = Queryable()
.LeftJoin<OdfCables>((f, c) => f.CableId == c.Id)
.Where((f, c) => f.Id == id)
.Select((f, c) => new
{
f.Id,
f.CableId,
f.FaultTime,
f.Personnel,
f.FaultReason,
f.Mileage,
f.MileageCorrection,
f.Location,
f.Latitude,
f.Longitude,
f.Remark,
f.UserId,
f.CreatedAt,
CableName = c.CableName
})
.First();
if (fault == null)
{
throw new CustomException("故障记录不存在");
}
// 查询关联图片列表
var images = Context.Queryable<OdfCableFaultImages>()
.Where(img => img.FaultId == id)
.OrderBy(img => img.Id)
.Select(img => new { img.Id, img.ImageUrl })
.ToList();
var faultTimes = Context.Queryable<OdfCableFaultTimes>()
.Where(t => t.FaultId == id)
.OrderBy(t => t.FaultTime)
.Select(t => new { t.Id, t.FaultTime })
.ToList();
return new
{
fault.Id,
fault.CableId,
fault.FaultTime,
fault.Personnel,
fault.FaultReason,
fault.Mileage,
fault.MileageCorrection,
fault.Location,
fault.Latitude,
fault.Longitude,
fault.Remark,
fault.CableName,
fault.CreatedAt,
FaultCount = Context.Queryable<OdfCableFaults>().Where(f => f.Id == id).First()?.FaultCount ?? 1,
FaultTimes = faultTimes,
Images = images
};
}
/// <summary>
/// 新增故障(含图片上传)
/// </summary>
public async Task<int> AddFault(OdfCableFaultAddDto dto)
{
// 校验 CableId 存在
var cable = Context.Queryable<OdfCables>()
.Where(c => c.Id == dto.CableId)
.First();
if (cable == null)
{
throw new CustomException("光缆不存在");
}
// 校验至少 1 张图片
if (dto.Images == null || dto.Images.Length == 0)
{
throw new CustomException("请至少上传一张图片");
}
// 插入故障记录
var model = new OdfCableFaults
{
CableId = dto.CableId,
FaultTime = DateTime.Parse(dto.FaultTime),
Personnel = dto.Personnel,
FaultReason = dto.FaultReason,
Mileage = dto.Mileage,
MileageCorrection = dto.MileageCorrection,
Location = dto.Location,
Latitude = dto.Latitude,
Longitude = dto.Longitude,
Remark = dto.Remark,
UserId = dto.UserId,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
var faultEntity = Insertable(model).ExecuteReturnEntity();
int faultId = faultEntity.Id;
// 保存图片文件并插入图片记录
IWebHostEnvironment webHostEnvironment = App.WebHostEnvironment;
string webRootPath = webHostEnvironment.WebRootPath;
string uploadDir = Path.Combine("uploads", "fault");
string fullDir = Path.Combine(webRootPath, uploadDir);
if (!Directory.Exists(fullDir))
{
Directory.CreateDirectory(fullDir);
}
foreach (var image in dto.Images)
{
string fileExt = Path.GetExtension(image.FileName);
string fileName = $"{DateTime.Now:yyyyMMdd}_{Guid.NewGuid():N}{fileExt}";
string filePath = Path.Combine(fullDir, fileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await image.CopyToAsync(stream);
}
string imageUrl = $"/{uploadDir}/{fileName}".Replace("\\", "/");
// 拼接完整URL前端可直接使用
var request = App.HttpContext?.Request;
if (request != null)
{
imageUrl = $"{request.Scheme}://{request.Host}{imageUrl}";
}
var imageRecord = new OdfCableFaultImages
{
FaultId = faultId,
ImageUrl = imageUrl,
CreatedAt = DateTime.Now
};
Context.Insertable(imageRecord).ExecuteCommand();
}
return faultId;
}
/// <summary>
/// 删除故障记录并级联删除关联图片
/// </summary>
public int Delete(int id)
{
// 先删除关联图片记录
Context.Deleteable<OdfCableFaultImages>()
.Where(img => img.FaultId == id)
.ExecuteCommand();
// 再删除故障记录
return base.Delete(id);
}
/// <summary>
/// 导出故障列表
/// </summary>
public PagedInfo<dynamic> ExportList(OdfCableFaultsQueryDto parm)
{
parm.PageNum = 1;
parm.PageSize = 100000;
return GetList(parm);
}
/// <summary>
/// 增加故障频次FaultCount+1同时插入一条时间记录
/// </summary>
public object IncrementFaultCount(int id)
{
var fault = GetFirst(f => f.Id == id);
if (fault == null)
{
throw new CustomException("故障记录不存在");
}
fault.FaultCount += 1;
fault.UpdatedAt = DateTime.Now;
Update(fault, true);
var now = DateTime.Now;
var timeRecord = new OdfCableFaultTimes
{
FaultId = id,
FaultTime = now,
CreatedAt = now
};
Context.Insertable(timeRecord).ExecuteCommand();
var faultTimes = Context.Queryable<OdfCableFaultTimes>()
.Where(t => t.FaultId == id)
.OrderBy(t => t.FaultTime)
.Select(t => new { t.Id, t.FaultTime })
.ToList();
return new
{
FaultCount = fault.FaultCount,
FaultTimes = faultTimes
};
}
/// <summary>
/// 查询表达式
/// </summary>
private static Expressionable<OdfCableFaults> QueryExp(OdfCableFaultsQueryDto parm)
{
var predicate = Expressionable.Create<OdfCableFaults>();
predicate = predicate.AndIF(parm.CableId != null, it => it.CableId == parm.CableId);
predicate = predicate.AndIF(parm.BeginFaultTime != null, it => it.FaultTime >= parm.BeginFaultTime);
predicate = predicate.AndIF(parm.EndFaultTime != null, it => it.FaultTime <= parm.EndFaultTime);
predicate = predicate.AndIF(!string.IsNullOrEmpty(parm.FaultReason), it => it.FaultReason.Contains(parm.FaultReason));
return predicate;
}
/// <summary>
/// 批量导入故障(导出的文件可直接导入)
/// </summary>
public (int successCount, int errorCount, string errorMsg) ImportFaults(List<OdfCableFaultImportDto> list)
{
int successCount = 0;
int errorCount = 0;
var errorMsgs = new List<string>();
// 预查所有光缆用于按名称匹配CableId
var cableNames = list.Where(x => !string.IsNullOrWhiteSpace(x.CableName)).Select(x => x.CableName.Trim()).Distinct().ToList();
var cables = Context.Queryable<OdfCables>().Where(c => cableNames.Contains(c.CableName)).ToList();
for (int i = 0; i < list.Count; i++)
{
var item = list[i];
int rowNum = i + 2; // Excel行号第1行是表头
try
{
// 确定CableId优先用光缆编号其次按所属光缆名称匹配
int? cableId = item.CableId;
if ((cableId == null || cableId == 0) && !string.IsNullOrWhiteSpace(item.CableName))
{
var cable = cables.Find(c => c.CableName == item.CableName.Trim());
if (cable != null)
{
cableId = cable.Id;
}
}
if (cableId == null || cableId == 0)
{
errorCount++;
errorMsgs.Add($"第{rowNum}行:未找到匹配的光缆");
continue;
}
var model = new OdfCableFaults
{
CableId = cableId.Value,
FaultTime = item.FaultTime ?? DateTime.Now,
Personnel = item.Personnel,
FaultReason = item.FaultReason,
Mileage = item.Mileage,
Location = item.Location,
Latitude = item.Latitude ?? 0,
Longitude = item.Longitude ?? 0,
Remark = item.Remark,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
Insertable(model).ExecuteCommand();
successCount++;
}
catch (Exception)
{
errorCount++;
errorMsgs.Add($"第{rowNum}行:导入失败");
}
}
string errorMsg = errorMsgs.Count > 0 ? string.Join("", errorMsgs.Take(10)) : "";
return (successCount, errorCount, errorMsg);
}
}
}