using ClosedXML.Excel; using IronPython.Hosting; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; using Microsoft.IdentityModel.Tokens; using MySqlConnector; using NLog; using System.Data; using System.Text; using ViewModels; using WebAPIServer.Common; using WebAPIServer.Models; namespace WebAPIServer.Controllers { [Route("api/[controller]/[action]")] [ApiController] public class TestLogsController : ControllerBase { /// /// 每个步骤返回多条测试记录 /// [HttpPost()] [Authorize()] public ReturnInfo GetLogListBySN([FromBody] TestLogsQueryModel T) { ReturnInfo returnInfo = new(); try { using (var q = new UtsManageContext()) { using IDbConnection conn = q.Database.GetDbConnection(); conn.Open(); List tl = []; foreach (var item in T.DbList) { string tableName = $"{T.DBName}.tbl_{item.ProId}_{item.Step}_testlog"; // 修改点1:移除了MAX聚合函数,查询所有匹配记录 string sql = $"SELECT * FROM {tableName} WHERE DUT_SN = '{T.SnCode}'"; using MySqlCommand command = new(sql, (MySqlConnection)conn); using MySqlDataAdapter adapter = new(command); DataSet ds = new(); adapter.Fill(ds); if (ds.Tables.Count == 0 || ds.Tables[0].Rows.Count == 0) { // 没有数据时仍创建空对象(保持原有结构) tl.Add(new ReturnTestLogs { Step = item.Step, ID = "", StartTime = "", UsedTime = "", FailSteps = "", FailMsg = "", TestResult = "", RObj = new List>() }); } else { // 修改点2:处理所有行数据 foreach (DataRow row in ds.Tables[0].Rows) { var log = new ReturnTestLogs { Step = item.Step, ID = row["ID"]?.ToString() ?? "", StartTime = row["StartTime"]?.ToString() ?? "", UsedTime = row["UsedTime"]?.ToString() ?? "", FailSteps = row["FailSteps"]?.ToString() ?? "", FailMsg = row["FailMsg"]?.ToString() ?? "", TestResult = row["TestResult"]?.ToString() ?? "", RObj = [] }; // 修改点3:每行数据单独创建字典 var rowData = new Dictionary(); foreach (DataColumn col in ds.Tables[0].Columns) { rowData[col.ColumnName] = row[col]?.ToString() ?? ""; } log.RObj.Add(rowData); tl.Add(log); } } } returnInfo.isok = true; returnInfo.response = tl; } } catch (Exception ex) { returnInfo.isok = false; returnInfo.message = ex.Message; } return returnInfo; } /// /// 测试总表Excel /// /// /// [HttpPost()] [Authorize()] public IActionResult GetTestSummaryTableExcel([FromBody] TestLogExportRequest request) { try { string? dbName = request.dbName; string? tbName = request.tbName; string? startDate = request.StartTime; string? endDate = request.EndTime; string? testResult = request.TestResult; List? failSteps = request.FailSteps; string? columnList = request.ColumnList; string distinctType = request.DistinctType ?? "None"; // 获取去重类型 if (string.IsNullOrEmpty(startDate) || string.IsNullOrEmpty(endDate)) { return BadRequest("开始时间或结束时间不能为空"); } using (var q = new UtsManageContext()) { using IDbConnection conn = q.Database.GetDbConnection(); if (conn.State != ConnectionState.Open) { conn.Open(); } // 构建查询 StringBuilder dataQuery = new StringBuilder(); if (distinctType == "None") { // 不去重的情况 dataQuery.Append("SELECT ID, ServiceID AS SID, StartTime, DUT_SN, TestResult, Failsteps"); if (!string.IsNullOrEmpty(columnList)) { dataQuery.Append(columnList); } dataQuery.Append($" FROM {dbName}.{tbName} "); dataQuery.Append($"WHERE StartTime BETWEEN '{startDate} 00:00:00' AND '{endDate} 23:59:59' "); } else { // 去重的情况 string orderDirection = distinctType == "Latest" ? "DESC" : "ASC"; dataQuery.Append("SELECT ID, ServiceID AS SID, StartTime, DUT_SN, TestResult, Failsteps"); if (!string.IsNullOrEmpty(columnList)) { dataQuery.Append(columnList); } dataQuery.Append(" FROM ("); dataQuery.Append("SELECT *, ROW_NUMBER() OVER (PARTITION BY DUT_SN ORDER BY StartTime "); dataQuery.Append(orderDirection); dataQuery.Append($") AS rn FROM {dbName}.{tbName} "); dataQuery.Append($"WHERE StartTime BETWEEN '{startDate} 00:00:00' AND '{endDate} 23:59:59' "); } if (!string.IsNullOrEmpty(testResult)) { dataQuery.Append($"AND TestResult = '{testResult}' "); } if (failSteps != null && failSteps.Count > 0) { string failStepsCondition = string.Join(",", failSteps.Select(f => $"'{MySqlHelper.EscapeString(f)}'")); dataQuery.Append($"AND Failsteps IN ({failStepsCondition}) "); } if (distinctType != "None") { dataQuery.Append(") t WHERE rn = 1 "); } dataQuery.Append("ORDER BY StartTime DESC"); // 执行数据查询 IDbCommand dataCmd = conn.CreateCommand(); dataCmd.CommandText = dataQuery.ToString(); MySqlCommand command = dataCmd as MySqlCommand; MySqlDataAdapter adapter = new(command); DataSet ds = new(); adapter.Fill(ds); DataTable dt = ds.Tables[0]; // 使用ClosedXML创建Excel using (var workbook = new XLWorkbook()) { var worksheet = workbook.Worksheets.Add(dt, "Sheet"); // 设置标题行样式 var headerRow = worksheet.Row(1); headerRow.Style.Font.Bold = true; // 自动调整列宽 worksheet.Columns().AdjustToContents(); using (var stream = new MemoryStream()) { workbook.SaveAs(stream); var content = stream.ToArray(); return File(content, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", $"{tbName}_Export.xlsx"); } } } } catch (Exception ex) { return StatusCode(500, $"导出失败: {ex.Message}"); } } public ReturnInfoPage GetTestSummaryTable([FromBody] TestLog T) { ReturnInfoPage returnInfo = new(); string? dbName = T.dbName; string? tbName = T.tbName; string? StartTime = T.StartTime; string? EndTime = T.EndTime; string? TestResult = T.TestResult; List? FailSteps = T.FailSteps; string? ColumnList = T.ColumnList; int page = T.Page; int pageSize = T.PageSize; string distinctType = T.DistinctType ?? "None"; // 获取去重类型,默认为不去重 if (string.IsNullOrEmpty(StartTime) || string.IsNullOrEmpty(EndTime)) { returnInfo.isok = false; returnInfo.message = "开始时间或结束时间不能为空"; return returnInfo; } try { using (var q = new UtsManageContext()) { using IDbConnection conn = q.Database.GetDbConnection(); if (conn.State != ConnectionState.Open) { conn.Open(); } // 1. 构建基础查询(总数查询) StringBuilder baseQuery = new StringBuilder(); if (distinctType == "None") { // 不去重的情况 baseQuery.Append($"SELECT COUNT(*) AS Total FROM {dbName}.{tbName} "); baseQuery.Append($"WHERE StartTime BETWEEN '{StartTime} 00:00:00' AND '{EndTime} 23:59:59' "); } else { // 去重的情况,需要计算去重后的总数 baseQuery.Append($"SELECT COUNT(DISTINCT DUT_SN) AS Total FROM {dbName}.{tbName} "); baseQuery.Append($"WHERE StartTime BETWEEN '{StartTime} 00:00:00' AND '{EndTime} 23:59:59' "); } if (!string.IsNullOrEmpty(TestResult)) { baseQuery.Append($"AND TestResult = '{TestResult}' "); } if (FailSteps != null && FailSteps.Count > 0) { // 使用IN语句查询多个失败步骤 string failStepsCondition = string.Join(",", FailSteps.Select(f => $"'{MySqlHelper.EscapeString(f)}'")); baseQuery.Append($"AND Failsteps IN ({failStepsCondition}) "); } // 执行总数查询 IDbCommand countCmd = conn.CreateCommand(); countCmd.CommandText = baseQuery.ToString(); long totalCount = Convert.ToInt64(countCmd.ExecuteScalar()); // 2. 计算总通过数 long totalPassCount = 0; StringBuilder totalPassQuery = new StringBuilder(); if (distinctType == "None") { // 不去重的情况:统计所有通过的记录 totalPassQuery.Append($"SELECT COUNT(*) FROM {dbName}.{tbName} "); totalPassQuery.Append($"WHERE StartTime BETWEEN '{StartTime} 00:00:00' AND '{EndTime} 23:59:59' "); totalPassQuery.Append($"AND TestResult = '1'"); if (!string.IsNullOrEmpty(TestResult)) { totalPassQuery.Append($"AND TestResult = '{TestResult}' "); } if (FailSteps != null && FailSteps.Count > 0) { // 使用IN语句查询多个失败步骤 string failStepsCondition = string.Join(",", FailSteps.Select(f => $"'{MySqlHelper.EscapeString(f)}'")); totalPassQuery.Append($"AND Failsteps IN ({failStepsCondition}) "); } } else { // 去重的情况:统计去重后记录中通过的数量 string orderDirection = distinctType == "Latest" ? "DESC" : "ASC"; totalPassQuery.Append($"SELECT COUNT(*) FROM ("); totalPassQuery.Append($"SELECT *, ROW_NUMBER() OVER (PARTITION BY DUT_SN ORDER BY StartTime {orderDirection}) AS rn "); totalPassQuery.Append($"FROM {dbName}.{tbName} "); totalPassQuery.Append($"WHERE StartTime BETWEEN '{StartTime} 00:00:00' AND '{EndTime} 23:59:59' "); if (!string.IsNullOrEmpty(TestResult)) { totalPassQuery.Append($"AND TestResult = '{TestResult}' "); } if (FailSteps != null && FailSteps.Count > 0) { // 使用IN语句查询多个失败步骤 string failStepsCondition = string.Join(",", FailSteps.Select(f => $"'{MySqlHelper.EscapeString(f)}'")); totalPassQuery.Append($"AND Failsteps IN ({failStepsCondition}) "); } totalPassQuery.Append($") as t WHERE rn = 1 AND TestResult = '1'"); } IDbCommand totalPassCmd = conn.CreateCommand(); totalPassCmd.CommandText = totalPassQuery.ToString(); totalPassCount = Convert.ToInt64(totalPassCmd.ExecuteScalar()); // 3. 计算通过率 double passRate = totalCount > 0 ? Math.Round((double)totalPassCount / totalCount * 100, 2) : 0; // 4. 构建数据查询 StringBuilder dataQuery = new StringBuilder(); if (distinctType == "None") { // 不去重的情况 dataQuery.Append("SELECT ID, ServiceID AS SID, StartTime, DUT_SN, TestResult, Failsteps"); if (!string.IsNullOrEmpty(ColumnList)) { dataQuery.Append(ColumnList); } dataQuery.Append($" FROM {dbName}.{tbName} "); dataQuery.Append($"WHERE StartTime BETWEEN '{StartTime} 00:00:00' AND '{EndTime} 23:59:59' "); } else { // 去重的情况 string orderDirection = distinctType == "Latest" ? "DESC" : "ASC"; dataQuery.Append("SELECT ID, ServiceID AS SID, StartTime, DUT_SN, TestResult, Failsteps"); if (!string.IsNullOrEmpty(ColumnList)) { dataQuery.Append(ColumnList); } dataQuery.Append(" FROM ("); dataQuery.Append("SELECT *, ROW_NUMBER() OVER (PARTITION BY DUT_SN ORDER BY StartTime "); dataQuery.Append(orderDirection); dataQuery.Append($") AS rn FROM {dbName}.{tbName} "); dataQuery.Append($"WHERE StartTime BETWEEN '{StartTime} 00:00:00' AND '{EndTime} 23:59:59' "); if (!string.IsNullOrEmpty(TestResult)) { dataQuery.Append($"AND TestResult = '{TestResult}' "); } if (FailSteps != null && FailSteps.Count > 0) { // 使用IN语句查询多个失败步骤 string failStepsCondition = string.Join(",", FailSteps.Select(f => $"'{MySqlHelper.EscapeString(f)}'")); dataQuery.Append($"AND Failsteps IN ({failStepsCondition}) "); } dataQuery.Append(") t WHERE rn = 1 "); } if (distinctType == "None") { // 不去重的情况下添加条件 if (!string.IsNullOrEmpty(TestResult)) { dataQuery.Append($"AND TestResult = '{TestResult}' "); } if (FailSteps != null && FailSteps.Count > 0) { // 使用IN语句查询多个失败步骤 string failStepsCondition = string.Join(",", FailSteps.Select(f => $"'{MySqlHelper.EscapeString(f)}'")); dataQuery.Append($"AND Failsteps IN ({failStepsCondition}) "); } } // 添加排序和分页 dataQuery.Append($"ORDER BY StartTime DESC LIMIT {pageSize} OFFSET {(page - 1) * pageSize}"); // 执行数据查询 IDbCommand dataCmd = conn.CreateCommand(); dataCmd.CommandText = dataQuery.ToString(); MySqlCommand command = dataCmd as MySqlCommand; MySqlDataAdapter adapter = new(command); DataSet ds = new(); adapter.Fill(ds); DataTable dt = ds.Tables[0]; // 处理结果 List> list = new List>(); foreach (DataRow item in dt.Rows) { Dictionary dic = new Dictionary(); foreach (DataColumn col in dt.Columns) { dic.Add(col.ColumnName, item[col]?.ToString() ?? ""); } list.Add(dic); } returnInfo.isok = true; returnInfo.total = totalCount; returnInfo.response = list; returnInfo.passCount = totalPassCount; // 修改字段名 returnInfo.passRate = passRate; } } catch (Exception ex) { returnInfo.isok = false; returnInfo.message = ex.Message; } return returnInfo; } } public class TestLogExportRequest { public string? dbName { get; set; } public string? tbName { get; set; } public string StartTime { get; set; } public string EndTime { get; set; } public string? TestResult { get; set; } public List? FailSteps { get; set; } public string? ColumnList { get; set; } public string DistinctType { get; set; } = "None"; // 新增字段:None-不去重, Latest-保留最新, Earliest-保留最早 } public class ReturnTestLogs { public string Step { get; set; } public string ID { get; set; } public string StartTime { get; set; } public string UsedTime { get; set; } public string FailSteps { get; set; } public string FailMsg { get; set; } public string TestResult { get; set; } public List> RObj { get; set; } } public class TestLogsQueryModel { public string DBName { get; set; } public string SnCode { get; set; } public List DbList { get; set; } } public class DBL { public string ProId { get; set; } public string Step { get; set; } } public class TestLog : TestLogExportRequest { public int Page { get; set; } public int PageSize { get; set; } } // 扩展ReturnInfoPage类以包含一次通过数和通过率 public class ReturnInfoPage { public bool isok { get; set; } public string message { get; set; } public long total { get; set; } public object response { get; set; } public long passCount { get; set; } public double passRate { get; set; } } }