odf_new/server/ZR.Service/Business/OdfCableFaultsService.cs
2026-04-06 14:14:48 +08:00

478 lines
18 KiB
C#
Raw Permalink 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 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,
f.FaultCount,
CableName = c.CableName
})
.MergeTable()
.OrderByDescending(it => it.FaultTime)
.ToPageList(parm.PageNum, parm.PageSize, ref total);
// 批量查询频次时间记录
var faultIds = list.Select(x => x.Id).ToList();
var allFaultTimes = GetFaultTimesByFaultIds(faultIds);
var resultList = list.Select(item =>
{
var times = new List<string>();
if (allFaultTimes.ContainsKey(item.Id))
{
times = allFaultTimes[item.Id].Select(t => t.ToString("yyyy-MM-dd HH:mm:ss")).ToList();
}
return new
{
item.Id,
item.CableId,
item.FaultTime,
item.Personnel,
item.FaultReason,
item.Mileage,
item.MileageCorrection,
item.Location,
item.Latitude,
item.Longitude,
item.Remark,
item.CreatedAt,
item.FaultCount,
item.CableName,
FaultTimes = times
};
}).ToList();
return new PagedInfo<dynamic>
{
Result = resultList.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>
/// 新增故障(图片已上传至 COS仅保存 URL
/// </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.ImageUrls == null || dto.ImageUrls.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;
// 插入图片记录COS URL
foreach (var imageUrl in dto.ImageUrls)
{
if (string.IsNullOrWhiteSpace(imageUrl)) continue;
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<OdfCableFaultTimes>()
.Where(t => t.FaultId == id)
.ExecuteCommand();
// 删除关联图片记录
Context.Deleteable<OdfCableFaultImages>()
.Where(img => img.FaultId == id)
.ExecuteCommand();
// 删除故障记录
return base.Delete(id);
}
/// <summary>
/// 导出故障列表
/// </summary>
public List<OdfCableFaultExportDto> ExportList(OdfCableFaultsQueryDto parm)
{
var predicate = QueryExp(parm);
return Queryable()
.Where(predicate.ToExpression())
.LeftJoin<OdfCables>((f, c) => f.CableId == c.Id)
.Select((f, c) => new OdfCableFaultExportDto
{
Id = f.Id,
CableId = f.CableId,
FaultTime = f.FaultTime,
Personnel = f.Personnel,
FaultReason = f.FaultReason,
Mileage = f.Mileage,
MileageCorrection = f.MileageCorrection,
Location = f.Location,
Latitude = f.Latitude,
Longitude = f.Longitude,
Remark = f.Remark,
CreatedAt = f.CreatedAt,
FaultCount = f.FaultCount,
CableName = c.CableName
})
.MergeTable()
.OrderByDescending(it => it.FaultTime)
.ToList();
}
/// <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>
/// 批量查询故障频次时间记录按故障ID分组
/// </summary>
public Dictionary<int, List<DateTime>> GetFaultTimesByFaultIds(List<int> faultIds)
{
if (faultIds == null || faultIds.Count == 0)
return new Dictionary<int, List<DateTime>>();
var records = Context.Queryable<OdfCableFaultTimes>()
.Where(t => faultIds.Contains(t.FaultId))
.OrderBy(t => t.FaultTime)
.ToList();
return records
.GroupBy(t => t.FaultId)
.ToDictionary(g => g.Key, g => g.Select(t => t.FaultTime).ToList());
}
/// <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)
{
var logger = NLog.LogManager.GetCurrentClassLogger();
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;
}
// 解析故障时间:导出时可能是多行时间拼接(\n分隔需解析所有有效时间
var allTimes = new List<DateTime>();
if (!string.IsNullOrWhiteSpace(item.FaultTime))
{
var rawFaultTime = item.FaultTime;
var escapedRaw = rawFaultTime.Replace("\r", "\\r").Replace("\n", "\\n");
logger.Info($"[导入调试] 行{rowNum} FaultTime原始值=[{escapedRaw}] 长度={rawFaultTime.Length}");
// 打印每个字符的Unicode编码排查隐藏字符
var charCodes = string.Join(",", rawFaultTime.Select(c => $"U+{(int)c:X4}"));
logger.Info($"[导入调试] 行{rowNum} FaultTime字符编码=[{charCodes}]");
var timeParts = item.FaultTime.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
logger.Info($"[导入调试] 行{rowNum} Split后得到 {timeParts.Length} 段");
for (int k = 0; k < timeParts.Length; k++)
{
logger.Info($"[导入调试] 行{rowNum} 时间段[{k}]=[{timeParts[k].Trim()}]");
if (DateTime.TryParse(timeParts[k].Trim(), out var parsed))
{
allTimes.Add(parsed);
logger.Info($"[导入调试] 行{rowNum} 时间段[{k}] 解析成功={parsed:yyyy-MM-dd HH:mm:ss}");
}
else
{
logger.Info($"[导入调试] 行{rowNum} 时间段[{k}] 解析失败");
}
}
}
else
{
logger.Info($"[导入调试] 行{rowNum} FaultTime为空");
}
logger.Info($"[导入调试] 行{rowNum} 共解析到 {allTimes.Count} 个有效时间");
// 按时间排序,第一个作为主故障时间
allTimes.Sort();
DateTime faultTime = allTimes.Count > 0 ? allTimes[0] : DateTime.Now;
// FaultCount以解析到的时间数量为准若只有1个或0个则取导入值或默认1
int faultCount = allTimes.Count > 1 ? allTimes.Count : (item.FaultCount ?? 1);
var model = new OdfCableFaults
{
CableId = cableId.Value,
FaultTime = faultTime,
Personnel = item.Personnel,
FaultReason = item.FaultReason,
Mileage = item.Mileage,
MileageCorrection = item.MileageCorrection,
Location = item.Location,
Latitude = item.Latitude ?? 0,
Longitude = item.Longitude ?? 0,
Remark = item.Remark,
FaultCount = faultCount,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
var faultEntity = Insertable(model).ExecuteReturnEntity();
// 将第2个及之后的时间插入频次时间记录表
if (allTimes.Count > 1)
{
for (int j = 1; j < allTimes.Count; j++)
{
var timeRecord = new OdfCableFaultTimes
{
FaultId = faultEntity.Id,
FaultTime = allTimes[j],
CreatedAt = DateTime.Now
};
Context.Insertable(timeRecord).ExecuteCommand();
}
}
successCount++;
}
catch (Exception)
{
errorCount++;
errorMsgs.Add($"第{rowNum}行:导入失败");
}
}
string errorMsg = errorMsgs.Count > 0 ? string.Join("", errorMsgs.Take(10)) : "";
return (successCount, errorCount, errorMsg);
}
/// <summary>
/// 更新表显里程矫正
/// </summary>
public int UpdateMileageCorrection(int id, string mileageCorrection)
{
var fault = GetFirst(f => f.Id == id);
if (fault == null)
{
throw new CustomException("故障记录不存在");
}
fault.MileageCorrection = mileageCorrection;
fault.UpdatedAt = DateTime.Now;
return Update(fault, true);
}
}
}