using Microsoft.AspNetCore.Mvc; using MiniExcelLibs; using ZR.Model.Business.Dto; using ZR.Service.Business.IBusinessService; //创建时间:2025-09-21 namespace ZR.Admin.WebApi.Controllers.Business { /// /// 干线故障管理 /// [Route("business/OdfCableFaults")] public class OdfCableFaultsController : BaseController { /// /// 干线故障接口 /// private readonly IOdfCableFaultsService _OdfCableFaultsService; public OdfCableFaultsController(IOdfCableFaultsService OdfCableFaultsService) { _OdfCableFaultsService = OdfCableFaultsService; } /// /// 故障列表分页查询 /// /// /// [HttpGet("list")] [ActionPermissionFilter(Permission = "odfcablefaults:list")] public IActionResult GetList([FromQuery] OdfCableFaultsQueryDto parm) { var response = _OdfCableFaultsService.GetList(parm); return SUCCESS(response); } /// /// 故障详情(含图片) /// /// /// [HttpGet("{id}")] [ActionPermissionFilter(Permission = "odfcablefaults:query")] public IActionResult GetDetail(int id) { var response = _OdfCableFaultsService.GetDetail(id); return SUCCESS(response); } /// /// 新增故障(图片已上传至COS,提交COS URL) /// /// [HttpPost("add")] [ActionPermissionFilter(Permission = "odfcablefaults:add")] [Log(Title = "干线故障", BusinessType = BusinessType.INSERT)] public async Task Add([FromBody] OdfCableFaultAddDto dto) { dto.UserId = HttpContext.GetUId(); var response = await _OdfCableFaultsService.AddFault(dto); return ToResponse(response); } /// /// 增加故障频次 /// [HttpPost("incrementFaultCount/{id}")] [ActionPermissionFilter(Permission = "odfcablefaults:edit")] [Log(Title = "干线故障", BusinessType = BusinessType.UPDATE)] public IActionResult IncrementFaultCount(int id) { var response = _OdfCableFaultsService.IncrementFaultCount(id); return SUCCESS(response); } /// /// 更新表显里程矫正 /// [HttpPost("updateMileageCorrection/{id}")] [ActionPermissionFilter(Permission = "odfcablefaults:edit")] [Log(Title = "干线故障", BusinessType = BusinessType.UPDATE)] public IActionResult UpdateMileageCorrection(int id, [FromBody] MileageCorrectionDto dto) { var response = _OdfCableFaultsService.UpdateMileageCorrection(id, dto.MileageCorrection); return ToResponse(response); } /// /// 删除故障并级联删除图片(支持单个/批量) /// /// [HttpPost("delete/{ids}")] [ActionPermissionFilter(Permission = "odfcablefaults:delete")] [Log(Title = "干线故障", BusinessType = BusinessType.DELETE)] public IActionResult Delete(string ids) { var idList = ids.Split(',', StringSplitOptions.RemoveEmptyEntries) .Select(s => int.TryParse(s.Trim(), out var v) ? v : 0) .Where(v => v > 0) .ToList(); if (idList.Count == 0) { return ToResponse(ResultCode.FAIL, "请选择要删除的数据"); } int total = 0; foreach (var id in idList) { total += _OdfCableFaultsService.Delete(id); } return ToResponse(total); } /// /// 导出故障列表 /// /// [Log(Title = "干线故障", BusinessType = BusinessType.EXPORT, IsSaveResponseData = false)] [HttpGet("export")] [ActionPermissionFilter(Permission = "odfcablefaults:export")] public IActionResult Export([FromQuery] OdfCableFaultsQueryDto parm) { var list = _OdfCableFaultsService.ExportList(parm); if (list == null || list.Count <= 0) { return ToResponse(ResultCode.FAIL, "没有要导出的数据"); } // 批量查询频次时间记录 var faultIds = list.Select(item => item.Id).ToList(); var allFaultTimes = _OdfCableFaultsService.GetFaultTimesByFaultIds(faultIds); // 填充计算字段 foreach (var item in list) { // 拼接所有故障时间(首次 + 增加的频次时间) var times = new List(); times.Add(item.FaultTime.ToString("yyyy-MM-dd HH:mm:ss")); if (allFaultTimes.ContainsKey(item.Id)) { times.AddRange(allFaultTimes[item.Id].Select(t => t.ToString("yyyy-MM-dd HH:mm:ss"))); } times.Sort(); item.FaultTimesDisplay = string.Join("\n", times); // 表显故障里程 = 原始里程 + 矫正值 item.DisplayMileage = CalcDisplayMileage(item.Mileage, item.MileageCorrection); } var result = ExportExcelMini(list, "故障列表", "故障列表"); return ExportExcel(result.Item2, result.Item1); } /// /// 批量导入故障 /// [HttpPost("importData")] [Log(Title = "干线故障导入", BusinessType = BusinessType.IMPORT, IsSaveRequestData = false)] [ActionPermissionFilter(Permission = "odfcablefaults:import")] public IActionResult ImportData([FromForm(Name = "file")] IFormFile formFile) { if (formFile == null || formFile.Length <= 0) { return ToResponse(ResultCode.FAIL, "请选择要导入的文件"); } var logger = NLog.LogManager.GetCurrentClassLogger(); List list = new(); using (var stream = formFile.OpenReadStream()) { // 使用动态查询读取原始数据,避免MiniExcel强类型映射丢失单元格内多行文本 var rows = stream.Query(useHeaderRow: true, startCell: "A1").ToList(); logger.Info($"[导入调试] 共读取到 {rows.Count} 行数据"); int rowIndex = 0; foreach (var row in rows) { rowIndex++; var dict = (IDictionary)row; // 打印所有列名和值,用于调试 foreach (var kv in dict) { var valType = kv.Value?.GetType()?.Name ?? "null"; var valStr = kv.Value?.ToString() ?? "(null)"; // 转义换行符以便在日志中可见 var valEscaped = valStr.Replace("\r", "\\r").Replace("\n", "\\n"); logger.Info($"[导入调试] 行{rowIndex} 列[{kv.Key}] 类型={valType} 值=[{valEscaped}]"); } var dto = new OdfCableFaultImportDto(); if (dict.TryGetValue("编号", out var idVal) && idVal != null && int.TryParse(idVal.ToString(), out var id)) dto.Id = id; if (dict.TryGetValue("光缆编号", out var cableIdVal) && cableIdVal != null && int.TryParse(cableIdVal.ToString(), out var cableId)) dto.CableId = cableId; // 故障时间:保留原始文本,支持单元格内多行时间 if (dict.TryGetValue("故障时间", out var ftVal) && ftVal != null) { if (ftVal is DateTime dt) { dto.FaultTime = dt.ToString("yyyy-MM-dd HH:mm:ss"); logger.Info($"[导入调试] 行{rowIndex} 故障时间是DateTime类型,值={dt:yyyy-MM-dd HH:mm:ss}"); } else { dto.FaultTime = ftVal.ToString(); var escaped = dto.FaultTime.Replace("\r", "\\r").Replace("\n", "\\n"); logger.Info($"[导入调试] 行{rowIndex} 故障时间是{ftVal.GetType().Name}类型,值=[{escaped}]"); } } else { logger.Info($"[导入调试] 行{rowIndex} 未找到故障时间列"); } if (dict.TryGetValue("故障发生频次", out var fcVal) && fcVal != null && int.TryParse(fcVal.ToString(), out var fc)) dto.FaultCount = fc; if (dict.TryGetValue("人员", out var pVal) && pVal != null) dto.Personnel = pVal.ToString(); if (dict.TryGetValue("故障原因", out var frVal) && frVal != null) dto.FaultReason = frVal.ToString(); if (dict.TryGetValue("表显故障里程", out var mVal) && mVal != null) dto.Mileage = mVal.ToString(); if (dict.TryGetValue("表显里程矫正", out var mcVal) && mcVal != null) dto.MileageCorrection = mcVal.ToString(); if (dict.TryGetValue("地点", out var locVal) && locVal != null) dto.Location = locVal.ToString(); if (dict.TryGetValue("纬度", out var latVal) && latVal != null && decimal.TryParse(latVal.ToString(), out var lat)) dto.Latitude = lat; if (dict.TryGetValue("经度", out var lngVal) && lngVal != null && decimal.TryParse(lngVal.ToString(), out var lng)) dto.Longitude = lng; if (dict.TryGetValue("备注", out var rmkVal) && rmkVal != null) dto.Remark = rmkVal.ToString(); if (dict.TryGetValue("创建时间", out var caVal) && caVal != null && DateTime.TryParse(caVal.ToString(), out var ca)) dto.CreatedAt = ca; if (dict.TryGetValue("所属光缆", out var cnVal) && cnVal != null) dto.CableName = cnVal.ToString(); list.Add(dto); } } if (list.Count <= 0) { return ToResponse(ResultCode.FAIL, "导入数据为空"); } var (successCount, errorCount, errorMsg) = _OdfCableFaultsService.ImportFaults(list); var msg = $"导入成功{successCount}条,失败{errorCount}条"; if (!string.IsNullOrEmpty(errorMsg)) { msg += $"。{errorMsg}"; } return SUCCESS(msg); } /// /// 干线故障导入模板下载 /// [HttpGet("importTemplate")] [Log(Title = "干线故障模板", BusinessType = BusinessType.EXPORT, IsSaveResponseData = false)] [AllowAnonymous] public IActionResult ImportTemplateExcel() { var result = DownloadImportTemplate(new List() { }, "OdfCableFaults"); return ExportExcel(result.Item2, result.Item1); } /// /// 计算表显故障里程(自动加上表显里程矫正) /// private static string CalcDisplayMileage(string mileage, string mileageCorrection) { if (decimal.TryParse(mileage, out var m) && decimal.TryParse(mileageCorrection, out var c)) { return (m + c).ToString(); } return mileage ?? ""; } } }