feat(hjgl): 完善检测结果导入与焊接质量管理
通过批量导入、标准化打印和材料导出,减少检测结果及材料信息的人工整理,确保检测单、委托状态和焊口质量资料保持一致。
This commit is contained in:
@@ -0,0 +1,496 @@
|
||||
using BLL;
|
||||
using MiniExcelLibs;
|
||||
using Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
namespace FineUIPro.Web.HJGL.NDT
|
||||
{
|
||||
public partial class NDTBatchUnitImport : PageBase
|
||||
{
|
||||
private static readonly string[] TemplateColumns =
|
||||
{
|
||||
"序号", "委托编号", "管线号", "检件焊口号", "焊工号", "检测总数",
|
||||
"合格数", "是否合格", "评定级别", "缺陷", "返修位置", "备注",
|
||||
"检测日期", "报告日期"
|
||||
};
|
||||
|
||||
private string UnitWorkId
|
||||
{
|
||||
get { return Convert.ToString(ViewState["UnitWorkId"]); }
|
||||
set { ViewState["UnitWorkId"] = value; }
|
||||
}
|
||||
|
||||
private string NDTType
|
||||
{
|
||||
get { return Convert.ToString(ViewState["NDTType"]); }
|
||||
set { ViewState["NDTType"] = value; }
|
||||
}
|
||||
|
||||
private string ImportCacheKey
|
||||
{
|
||||
get { return Convert.ToString(ViewState["ImportCacheKey"]); }
|
||||
set { ViewState["ImportCacheKey"] = value; }
|
||||
}
|
||||
|
||||
private List<UnitWorkNDEImportItem> ImportItems
|
||||
{
|
||||
get
|
||||
{
|
||||
return Session[ImportCacheKey] as List<UnitWorkNDEImportItem>
|
||||
?? new List<UnitWorkNDEImportItem>();
|
||||
}
|
||||
set { Session[ImportCacheKey] = value; }
|
||||
}
|
||||
|
||||
protected void Page_Load(object sender, EventArgs e)
|
||||
{
|
||||
if (!IsPostBack)
|
||||
{
|
||||
UnitWorkId = Request.QueryString["unitWorkId"];
|
||||
NDTType = Request.QueryString["type"];
|
||||
ImportCacheKey = "NDTBatchUnitImport_" + Guid.NewGuid().ToString("N");
|
||||
ImportItems = new List<UnitWorkNDEImportItem>();
|
||||
|
||||
WBS_UnitWork unitWork = Funs.DB.WBS_UnitWork.FirstOrDefault(x =>
|
||||
x.UnitWorkId == UnitWorkId
|
||||
&& x.ProjectId == CurrUser.LoginProjectId
|
||||
&& x.SuperUnitWork == null);
|
||||
if (unitWork == null)
|
||||
{
|
||||
btnAudit.Enabled = false;
|
||||
btnSave.Enabled = false;
|
||||
btnDownLoad.Enabled = false;
|
||||
ShowNotify("单位工程不存在或不属于当前项目!", MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
lblUnitWork.Text = unitWork.UnitWorkCode + " " + unitWork.UnitWorkName;
|
||||
BindGrid();
|
||||
}
|
||||
}
|
||||
|
||||
protected void btnDownLoad_Click(object sender, EventArgs e)
|
||||
{
|
||||
string templatePath = Server.MapPath("~/" + Const.NDTBatchUnitImportTemplateUrl.Replace("\\", "/"));
|
||||
if (!File.Exists(templatePath))
|
||||
{
|
||||
ShowNotify("检测结果导入模板不存在,请联系管理员!", MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
FileInfo info = new FileInfo(templatePath);
|
||||
Response.ClearContent();
|
||||
Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode("检测结果导入模板.xlsx", System.Text.Encoding.UTF8));
|
||||
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
||||
Response.ContentEncoding = System.Text.Encoding.UTF8;
|
||||
Response.AddHeader("Content-Length", info.Length.ToString());
|
||||
Response.TransmitFile(templatePath, 0, info.Length);
|
||||
Response.Flush();
|
||||
Response.Close();
|
||||
}
|
||||
|
||||
protected void btnAudit_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!fuAttachUrl.HasFile)
|
||||
{
|
||||
ShowNotify("请选择要导入的 Excel 文件!", MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
string extension = Path.GetExtension(fuAttachUrl.FileName).ToLowerInvariant();
|
||||
if (extension != ".xlsx")
|
||||
{
|
||||
ShowNotify("只能导入 .xlsx 文件!", MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
string rootPath = Server.MapPath("~/");
|
||||
string uploadDirectory = Path.Combine(rootPath, Const.ExcelUrl);
|
||||
if (!Directory.Exists(uploadDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(uploadDirectory);
|
||||
}
|
||||
|
||||
string filePath = Path.Combine(uploadDirectory, Funs.GetNewFileName() + extension);
|
||||
try
|
||||
{
|
||||
fuAttachUrl.PostedFile.SaveAs(filePath);
|
||||
DataTable table = MiniExcel.QueryAsDataTable(filePath, useHeaderRow: true);
|
||||
List<string> errors;
|
||||
List<UnitWorkNDEImportItem> importItems = ParseImportItems(table, out errors);
|
||||
if (errors.Count > 0)
|
||||
{
|
||||
ImportItems = new List<UnitWorkNDEImportItem>();
|
||||
BindGrid();
|
||||
ShowErrors(errors);
|
||||
return;
|
||||
}
|
||||
|
||||
ImportItems = importItems;
|
||||
BindGrid();
|
||||
ShowNotify("审核完成,共 " + importItems.Count + " 条,请确认后提交导入!", MessageBoxIcon.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ImportItems = new List<UnitWorkNDEImportItem>();
|
||||
BindGrid();
|
||||
ShowNotify("读取导入文件失败:" + ex.Message, MessageBoxIcon.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
File.Delete(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void btnSave_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!CommonService.GetAllButtonPowerList(CurrUser.LoginProjectId, CurrUser.PersonId, Const.HJGL_NDTBatchMenuId, Const.BtnSave))
|
||||
{
|
||||
ShowNotify("您没有这个权限,请与管理员联系!", MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
List<UnitWorkNDEImportItem> importItems = ImportItems;
|
||||
if (importItems.Count == 0)
|
||||
{
|
||||
ShowNotify("请先审核要导入的文件!", MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Batch_NDEImportService.Import(CurrUser.LoginProjectId, UnitWorkId, CurrUser.PersonId, importItems);
|
||||
Session.Remove(ImportCacheKey);
|
||||
ShowNotify("成功导入 " + importItems.Count + " 条检测结果!", MessageBoxIcon.Success);
|
||||
PageContext.RegisterStartupScript(ActiveWindow.GetHidePostBackReference());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ShowNotify("导入失败:" + ex.Message, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
protected void Grid1_PageIndexChange(object sender, GridPageEventArgs e)
|
||||
{
|
||||
Grid1.PageIndex = e.NewPageIndex;
|
||||
BindGrid();
|
||||
}
|
||||
|
||||
private List<UnitWorkNDEImportItem> ParseImportItems(DataTable table, out List<string> errors)
|
||||
{
|
||||
errors = new List<string>();
|
||||
List<UnitWorkNDEImportItem> result = new List<UnitWorkNDEImportItem>();
|
||||
if (table == null || table.Rows.Count == 0)
|
||||
{
|
||||
errors.Add("导入数据为空!");
|
||||
return result;
|
||||
}
|
||||
|
||||
List<string> missingColumns = TemplateColumns.Where(x => !table.Columns.Contains(x)).ToList();
|
||||
if (missingColumns.Count > 0)
|
||||
{
|
||||
errors.Add("导入 Excel 格式错误,缺少列:" + string.Join("、", missingColumns));
|
||||
return result;
|
||||
}
|
||||
|
||||
List<View_Batch_BatchTrustItem> trustItems = GetUnitWorkTrustItems();
|
||||
Dictionary<string, View_Batch_BatchTrust> trustMap = Funs.DB.View_Batch_BatchTrust
|
||||
.Where(x => x.ProjectId == CurrUser.LoginProjectId && x.UnitWorkId == UnitWorkId)
|
||||
.ToList()
|
||||
.Where(IsCurrentNDTType)
|
||||
.GroupBy(x => Normalize(x.TrustBatchCode))
|
||||
.ToDictionary(x => x.Key, x => x.First());
|
||||
HashSet<string> importedTrustBatchIds = new HashSet<string>(Funs.DB.HJGL_Batch_NDE
|
||||
.Where(x => x.ProjectId == CurrUser.LoginProjectId && x.UnitWorkId == UnitWorkId)
|
||||
.Select(x => x.TrustBatchId)
|
||||
.ToList());
|
||||
Dictionary<string, Base_Defect> defectMap = Funs.DB.Base_Defect
|
||||
.ToList()
|
||||
.GroupBy(x => Normalize(x.DefectName))
|
||||
.ToDictionary(x => x.Key, x => x.First());
|
||||
HashSet<string> rowKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
for (int index = 0; index < table.Rows.Count; index++)
|
||||
{
|
||||
DataRow row = table.Rows[index];
|
||||
int excelRowNumber = index + 2;
|
||||
string trustBatchCode = GetCellValue(row, "委托编号");
|
||||
string pipelineCode = GetCellValue(row, "管线号");
|
||||
string weldJointCode = GetCellValue(row, "检件焊口号");
|
||||
string totalFilmText = GetCellValue(row, "检测总数");
|
||||
string passFilmText = GetCellValue(row, "合格数");
|
||||
string filmDateText = GetCellValue(row, "检测日期");
|
||||
string reportDateText = GetCellValue(row, "报告日期");
|
||||
|
||||
if (string.IsNullOrEmpty(trustBatchCode)
|
||||
&& string.IsNullOrEmpty(pipelineCode)
|
||||
&& string.IsNullOrEmpty(weldJointCode)
|
||||
&& string.IsNullOrEmpty(totalFilmText)
|
||||
&& string.IsNullOrEmpty(passFilmText)
|
||||
&& string.IsNullOrEmpty(filmDateText)
|
||||
&& string.IsNullOrEmpty(reportDateText))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int errorCount = errors.Count;
|
||||
int sequence = index + 1;
|
||||
string sequenceText = GetCellValue(row, "序号");
|
||||
if (!string.IsNullOrEmpty(sequenceText)
|
||||
&& (!int.TryParse(sequenceText, out sequence) || sequence <= 0))
|
||||
{
|
||||
errors.Add("第" + excelRowNumber + "行:序号必须为大于 0 的整数");
|
||||
}
|
||||
|
||||
View_Batch_BatchTrust trust = null;
|
||||
if (string.IsNullOrEmpty(trustBatchCode) || !trustMap.TryGetValue(Normalize(trustBatchCode), out trust))
|
||||
{
|
||||
errors.Add("第" + excelRowNumber + "行:委托编号[" + trustBatchCode + "]不属于当前单位工程");
|
||||
}
|
||||
else if (importedTrustBatchIds.Contains(trust.TrustBatchId))
|
||||
{
|
||||
errors.Add("第" + excelRowNumber + "行:委托编号[" + trustBatchCode + "]已存在检测结果,不能重复导入");
|
||||
}
|
||||
|
||||
View_Batch_BatchTrustItem trustItem = null;
|
||||
if (trust != null)
|
||||
{
|
||||
trustItem = trustItems.FirstOrDefault(x =>
|
||||
x.TrustBatchId == trust.TrustBatchId
|
||||
&& Normalize(x.PipelineCode) == Normalize(pipelineCode)
|
||||
&& Normalize(x.WeldJointCode) == Normalize(weldJointCode));
|
||||
if (trustItem == null)
|
||||
{
|
||||
errors.Add("第" + excelRowNumber + "行:管线[" + pipelineCode + "]焊口[" + weldJointCode + "]不存在于该委托单");
|
||||
}
|
||||
}
|
||||
|
||||
string welderCode = GetCellValue(row, "焊工号");
|
||||
if (trustItem != null && !string.IsNullOrEmpty(welderCode)
|
||||
&& Normalize(trustItem.WelderCode) != Normalize(welderCode))
|
||||
{
|
||||
errors.Add("第" + excelRowNumber + "行:焊工号[" + welderCode + "]与委托明细不一致");
|
||||
}
|
||||
|
||||
int totalFilm;
|
||||
if (!int.TryParse(totalFilmText, out totalFilm) || totalFilm <= 0)
|
||||
{
|
||||
errors.Add("第" + excelRowNumber + "行:检测总数必须为大于 0 的整数");
|
||||
}
|
||||
|
||||
int passFilm;
|
||||
if (!int.TryParse(passFilmText, out passFilm) || passFilm < 0)
|
||||
{
|
||||
errors.Add("第" + excelRowNumber + "行:合格数必须为不小于 0 的整数");
|
||||
}
|
||||
else if (totalFilm > 0 && passFilm > totalFilm)
|
||||
{
|
||||
errors.Add("第" + excelRowNumber + "行:合格数不能大于检测总数");
|
||||
}
|
||||
|
||||
string checkResultText = GetCellValue(row, "是否合格");
|
||||
string checkResult = ConvertCheckResult(checkResultText);
|
||||
if (string.IsNullOrEmpty(checkResult))
|
||||
{
|
||||
errors.Add("第" + excelRowNumber + "行:是否合格只能填写“合格”或“不合格”");
|
||||
}
|
||||
|
||||
string judgeGrade = ConvertJudgeGrade(GetCellValue(row, "评定级别"));
|
||||
if (judgeGrade == null)
|
||||
{
|
||||
errors.Add("第" + excelRowNumber + "行:评定级别只能填写Ⅰ、Ⅱ、Ⅲ、Ⅳ、Ⅴ或1-5");
|
||||
}
|
||||
|
||||
string defectText = GetCellValue(row, "缺陷");
|
||||
List<string> defectIds = new List<string>();
|
||||
if (!string.IsNullOrEmpty(defectText))
|
||||
{
|
||||
foreach (string defectName in SplitDefects(defectText))
|
||||
{
|
||||
Base_Defect defect;
|
||||
if (!defectMap.TryGetValue(Normalize(defectName), out defect))
|
||||
{
|
||||
errors.Add("第" + excelRowNumber + "行:缺陷[" + defectName + "]不存在");
|
||||
}
|
||||
else
|
||||
{
|
||||
defectIds.Add(Convert.ToString(defect.DefectId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string rowKey = Normalize(trustBatchCode) + "|" + Normalize(pipelineCode) + "|" + Normalize(weldJointCode);
|
||||
if (!rowKeys.Add(rowKey))
|
||||
{
|
||||
errors.Add("第" + excelRowNumber + "行:同一委托、管线和焊口在文件中重复");
|
||||
}
|
||||
|
||||
// 两个日期均为非必填列;兼容 Excel 日期单元格、序列值和常见日期文本。
|
||||
DateTime? filmDate = ParseOptionalDate(row, "检测日期", excelRowNumber, errors);
|
||||
DateTime? reportDate = ParseOptionalDate(row, "报告日期", excelRowNumber, errors);
|
||||
|
||||
if (errors.Count == errorCount && trust != null && trustItem != null)
|
||||
{
|
||||
result.Add(new UnitWorkNDEImportItem
|
||||
{
|
||||
RowNumber = sequence,
|
||||
TrustBatchId = trust.TrustBatchId,
|
||||
TrustBatchCode = trust.TrustBatchCode,
|
||||
TrustBatchItemId = trustItem.TrustBatchItemId,
|
||||
PipelineCode = trustItem.PipelineCode,
|
||||
WeldJointCode = trustItem.WeldJointCode,
|
||||
WelderCode = trustItem.WelderCode,
|
||||
TotalFilm = totalFilm,
|
||||
PassFilm = passFilm,
|
||||
CheckResult = checkResult,
|
||||
CheckResultText = checkResultText == "是" ? "合格" : checkResultText == "否" ? "不合格" : checkResultText,
|
||||
JudgeGrade = judgeGrade,
|
||||
CheckDefects = string.Join(",", defectIds.Distinct()),
|
||||
CheckDefectsText = defectText,
|
||||
RepairLocation = GetCellValue(row, "返修位置"),
|
||||
Remark = GetCellValue(row, "备注"),
|
||||
FilmDate = filmDate,
|
||||
ReportDate = reportDate
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (result.Count == 0 && errors.Count == 0)
|
||||
{
|
||||
errors.Add("导入数据为空!");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<View_Batch_BatchTrustItem> GetUnitWorkTrustItems()
|
||||
{
|
||||
return Funs.DB.View_Batch_BatchTrustItem
|
||||
.Where(x => x.ProjectId == CurrUser.LoginProjectId && x.UnitWorkId == UnitWorkId && x.PipelineCode !=null)
|
||||
.ToList()
|
||||
.Where(IsCurrentNDTType)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private bool IsCurrentNDTType(View_Batch_BatchTrustItem item)
|
||||
{
|
||||
return NDTType == "R" ? item.TrustType == "R" : string.IsNullOrEmpty(item.TrustType);
|
||||
}
|
||||
|
||||
private bool IsCurrentNDTType(View_Batch_BatchTrust item)
|
||||
{
|
||||
return NDTType == "R" ? item.TrustType == "R" : string.IsNullOrEmpty(item.TrustType);
|
||||
}
|
||||
|
||||
private void BindGrid()
|
||||
{
|
||||
List<UnitWorkNDEImportItem> items = ImportItems;
|
||||
Grid1.RecordCount = items.Count;
|
||||
Grid1.DataSource = items.Skip(Grid1.PageIndex * Grid1.PageSize).Take(Grid1.PageSize).ToList();
|
||||
Grid1.DataBind();
|
||||
}
|
||||
|
||||
private void ShowErrors(IEnumerable<string> errors)
|
||||
{
|
||||
Alert alert = new Alert
|
||||
{
|
||||
Message = string.Join("<br/>", errors.Distinct().Select(HttpUtility.HtmlEncode)),
|
||||
Target = Target.Self
|
||||
};
|
||||
alert.Show();
|
||||
}
|
||||
|
||||
private static string GetCellValue(DataRow row, string columnName)
|
||||
{
|
||||
return Convert.ToString(row[columnName]).Replace("\n", "").Replace("\t", "").Replace("\r", "").Trim();
|
||||
}
|
||||
|
||||
private static DateTime? ParseOptionalDate(DataRow row, string columnName, int excelRowNumber, ICollection<string> errors)
|
||||
{
|
||||
object rawValue = row[columnName];
|
||||
string value = Convert.ToString(rawValue).Trim();
|
||||
if (rawValue == null || rawValue == DBNull.Value || string.IsNullOrEmpty(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (rawValue is DateTime)
|
||||
{
|
||||
return ((DateTime)rawValue).Date;
|
||||
}
|
||||
|
||||
double oaDate;
|
||||
if (double.TryParse(value, out oaDate))
|
||||
{
|
||||
try
|
||||
{
|
||||
return DateTime.FromOADate(oaDate).Date;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// 无效序列值继续按日期文本处理,最终给出统一提示。
|
||||
}
|
||||
}
|
||||
|
||||
DateTime date;
|
||||
if (DateTime.TryParse(value, out date))
|
||||
{
|
||||
return date.Date;
|
||||
}
|
||||
|
||||
errors.Add("第" + excelRowNumber + "行:" + columnName + "格式错误,应填写有效日期");
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string Normalize(string value)
|
||||
{
|
||||
return (value ?? string.Empty).Replace(" ", string.Empty).Trim().ToUpperInvariant();
|
||||
}
|
||||
|
||||
private static string ConvertCheckResult(string value)
|
||||
{
|
||||
if (value == "合格" || value == "是")
|
||||
{
|
||||
return "1";
|
||||
}
|
||||
if (value == "不合格" || value == "否")
|
||||
{
|
||||
return "2";
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private static string ConvertJudgeGrade(string value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case "": return string.Empty;
|
||||
case "Ⅰ":
|
||||
case "1": return "Ⅰ";
|
||||
case "Ⅱ":
|
||||
case "2": return "Ⅱ";
|
||||
case "Ⅲ":
|
||||
case "3": return "Ⅲ";
|
||||
case "Ⅳ":
|
||||
case "4": return "Ⅳ";
|
||||
case "Ⅴ":
|
||||
case "5": return "Ⅴ";
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<string> SplitDefects(string value)
|
||||
{
|
||||
return value.Split(new[] { ',', ',', '、', ';', ';' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim())
|
||||
.Where(x => !string.IsNullOrEmpty(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user