using Backend.Models; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using NPOI.HSSF.UserModel; using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; using Repository.BackendRepository.Interface; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.Json; using System.Threading.Tasks; namespace Backend.Controllers { public class DeviceImportController : MybaseController { private readonly IBackendRepository backendRepository; private readonly IDeviceImportRepository deviceImportRepository; public DeviceImportController(IBackendRepository backendRepository, IDeviceImportRepository deviceImportRepository) { this.backendRepository = backendRepository; this.deviceImportRepository = deviceImportRepository; } public IActionResult Index() { return View(); } /// /// 設備匯入列表 /// /// [HttpPost] public async Task>> RawDataList() { ApiResult> apiResult = new ApiResult>(); List deviceImports = new List(); try { deviceImports = await backendRepository.GetAllAsync("device_import_temp", null, null, "device_result DESC,created_at DESC"); apiResult.Code = "0000"; apiResult.Data = deviceImports; } catch (Exception exception) { apiResult.Code = "9999"; Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message); } return apiResult; } [HttpPost] public async Task> ImportRawDataFile(IFormFile[] import_files) { ApiResult apiResult = new ApiResult(); try { Dictionary workbooks = new Dictionary(); //List workbooks = new List(); #region 檢驗各檔案是否正確 foreach (var import_file in import_files) { IWorkbook workbook; var filename_ext = Path.GetExtension(import_file.FileName).ToLower(); if (filename_ext == ".xls") { workbook = new HSSFWorkbook(import_file.OpenReadStream()); workbooks.Add($"{import_file.FileName}", workbook); } else if (filename_ext == ".xlsx") { workbook = new XSSFWorkbook(import_file.OpenReadStream()); workbooks.Add($"{import_file.FileName}", workbook); } else { workbook = null; } if (workbook == null) { apiResult.Code = "9998"; apiResult.Msg = $"{import_file.FileName}該檔案失效,請重新操作。"; return apiResult; } } #endregion List> deviceImports = new List>(); //抓取系統類別(以供驗證判斷) var sWhere = @$"deleted = 0 AND system_type = @system_type"; var system_category_param = new { system_type = "device_system_category_layer3" }; var system_categories_layer3 = await backendRepository.GetAllAsync("variable", sWhere, system_category_param); var disaster_param = new { system_type = "disaster" }; var disasters = await backendRepository.GetAllAsync("variable", sWhere, disaster_param); var temp_building = ""; //預設的棟別(檢查用,整份excel需相同),目前只針對單一檔案情況,如後續有需再改成多檔情況。 #region 抓取每個檔案的資料 foreach (var keyValuePair in workbooks) { IWorkbook workbook = keyValuePair.Value; var total_sheet = workbook.NumberOfSheets; for (var sheet_num = 2; sheet_num < total_sheet - 1; sheet_num++) { var tags_name_index = -1; var system_category_index = -1; //系統類別 var disaster_index = -1; //緊急應變程序(等同災害類別) var sheet = workbook.GetSheetAt(sheet_num); //表頭 IRow header = sheet.GetRow(sheet.FirstRowNum); List columns = new List(); if (header != null) { for (int i = 0; i < header.LastCellNum; i++) { ICell cell = header.GetCell(i); if (cell != null) { var header_str = cell.ToString().ToLower(); if (!string.IsNullOrEmpty(header_str) && header_str == "tags name") { tags_name_index = i; } if (!string.IsNullOrEmpty(header_str) && header_str == "系統類別") { system_category_index = i; } if (!string.IsNullOrEmpty(header_str) && header_str == "系統程式軟體") { IRow sub_header = sheet.GetRow(1); for (int j = 0; j < sub_header.LastCellNum; j++) { ICell sub_cell = sub_header.GetCell(j); var sub_header_str = sub_cell.ToString().ToLower(); if (!string.IsNullOrEmpty(sub_header_str) && sub_header_str == "緊急應變程序") { disaster_index = j; } } } if (tags_name_index > 0 && system_category_index > 0 && disaster_index > 0) { break; } } } } //資料 if (tags_name_index < 0 || system_category_index < 0 || disaster_index < 0) { List errMsg = new List(); var result = $@"查無[{keyValuePair.Key}]在[{workbook.GetSheetName(sheet_num)}]分頁的"; if (tags_name_index < 0) { errMsg.Add($@"[tags name]"); } if (system_category_index < 0) { errMsg.Add($@"[系統類別]"); } if (disaster_index < 0) { errMsg.Add($@"[緊急應變程序]"); } Dictionary deviceImport = new Dictionary() { { "@device_number", null}, { "@device_system_category_layer3", null}, { "@device_disaster", null}, { "@device_result", result + String.Join("、", errMsg)} }; deviceImports.Add(deviceImport); } else { for (var i = sheet.FirstRowNum + 2; i < sheet.LastRowNum; i++) { IRow row = sheet.GetRow(i); if (row != null) { List errMsg = new List(); Dictionary deviceImport = new Dictionary(); ICell tags_name_cell = row.GetCell(tags_name_index); if (tags_name_cell != null) { var tempData = tags_name_cell.ToString().Trim(); var system_category = string.Empty; var disaster = string.Empty; if (!string.IsNullOrEmpty(tempData)) { var arr_tempData = tempData.Split('_'); #region tags name驗證條件 if (string.IsNullOrEmpty(temp_building)) //抓第一筆當作該excel 比對棟別的依據 { temp_building = arr_tempData[0]; } else { if (temp_building != arr_tempData[0]) { errMsg.Add("資料棟別錯誤"); } } if (arr_tempData.Length != 5) { errMsg.Add("資料格式錯誤"); } if (arr_tempData[arr_tempData.Length - 1].Contains('~')) { errMsg.Add("設備流水號格式錯誤"); } if (arr_tempData[arr_tempData.Length - 1].Split('~').Length > 2) { errMsg.Add("設備流水號格式錯誤"); } #endregion #region 系統類別驗證條件 ICell system_category_cell = row.GetCell(system_category_index); if (system_category_cell == null) { errMsg.Add(@"該設備的[系統類別]未填值"); } else { system_category = system_category_cell.ToString().Trim(); if (!string.IsNullOrEmpty(system_category)) { var exist = system_categories_layer3.Exists(x => x.system_value == system_category); if (!exist) { errMsg.Add(@"該設備的[系統類別]不存在"); } } else { errMsg.Add(@"該設備的[系統類別]未填值"); } } #endregion #region 緊急應變程序驗證條件 ICell disaster_cell = row.GetCell(disaster_index); if (disaster_cell == null) { errMsg.Add(@"該設備的[緊急應變程序]未填值"); } else { disaster = disaster_cell.ToString().Trim(); if (!string.IsNullOrEmpty(disaster)) { if (disaster != "0") { var exist = disasters.Exists(x => x.system_value == disaster); if (!exist) { errMsg.Add(@"該設備的[緊急應變程序]不存在"); } } } else { errMsg.Add(@"該設備的[緊急應變程序]未填值"); } } #endregion if (errMsg.Count > 0) { deviceImport.Add("@device_number", tempData); deviceImport.Add("@device_system_category_layer3", system_category); deviceImport.Add("@device_disaster", disaster); deviceImport.Add("@device_result", String.Join(", ", errMsg)); } else { deviceImport.Add("@device_number", tempData); deviceImport.Add("@device_system_category_layer3", system_category); deviceImport.Add("@device_disaster", disaster); deviceImport.Add("@device_result", null); } deviceImports.Add(deviceImport); } } } } } } } deviceImports = deviceImports.Distinct().ToList(); //先刪除整份資料表 var sql = @"IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[device_import_temp]') AND type in (N'U')) BEGIN DROP TABLE [dbo].[device_import_temp]; END CREATE TABLE [dbo].[device_import_temp]( [id] [int] IDENTITY(1,1) NOT NULL, [device_number] [nvarchar](255) NULL, [device_system_category_layer3] [varchar](50) NULL, [device_disaster] [varchar](50) NULL, [device_result] [nvarchar](255) NULL, [created_at] [datetime] NULL, CONSTRAINT [PK_device_import_temp] PRIMARY KEY CLUSTERED ( [id] ASC )) ALTER TABLE [dbo].[device_import_temp] ADD CONSTRAINT [DF_device_import_temp_device_disaster] DEFAULT ((0)) FOR [device_disaster] ALTER TABLE [dbo].[device_import_temp] ADD CONSTRAINT [DF_device_import_temp_created_at] DEFAULT (getdate()) FOR [created_at] "; await backendRepository.ExecuteSql(sql); //如有發現錯誤,直接insert 至 device_import_temp,不做後續處理 await backendRepository.AddMutiByCustomTable(deviceImports, "device_import_temp"); //var err = deviceImports.Where(x => x.ContainsKey("@device_result")).Select(x => x.Values).ToList(); var err = deviceImports.SelectMany(x => x).Where(x => x.Key == "@device_result" && x.Value != null).ToList(); if (err.Count > 0) { apiResult.Code = "9997"; apiResult.Msg = "資料內容有誤,請重新匯入。"; return apiResult; } else { //拆分每一份資料 List> deviceImportChecks = new List>(); //檢查OK的列表 foreach (var deviceImport in deviceImports) { object device_number = null; object device_system_category_layer3 = null; object device_disaster = null; deviceImport.TryGetValue("@device_number", out device_number); deviceImport.TryGetValue("@device_system_category_layer3", out device_system_category_layer3); deviceImport.TryGetValue("@device_disaster", out device_disaster); var arr_device_number = device_number.ToString().Split('_'); //抓出是否為組數的設備 var arr_device_number_final_col = arr_device_number[arr_device_number.Length - 1].Contains('~') ? arr_device_number[arr_device_number.Length - 1].Split('~') : null; if (arr_device_number_final_col != null && arr_device_number_final_col.Length > 0) { var start_num = Convert.ToInt32(arr_device_number_final_col[0].Trim()); var end_num = Convert.ToInt32(arr_device_number_final_col[1].Trim()); for (var i = start_num; i <= end_num; i++) { Dictionary deviceImportCheck = new Dictionary(); var pre_device_number = String.Join('_', arr_device_number, 0, 4); deviceImportCheck.Add("@device_building_tag", arr_device_number[0]); //設備區域 deviceImportCheck.Add("@device_system_tag", arr_device_number[1]); //設備系統別 deviceImportCheck.Add("@device_floor_tag", arr_device_number[2]); //設備樓層 deviceImportCheck.Add("@device_name_tag", arr_device_number[3]); //設備名稱 var pad = string.Empty; if(i < 10) { pad = i.ToString().PadLeft(2, '0'); } else { pad = i.ToString(); } deviceImportCheck.Add("@device_serial_tag", pad); //設備流水號 deviceImportCheck.Add("@device_number", pre_device_number + "_" + pad); //設備完整編號 deviceImportCheck.Add("@device_system_category_layer3", device_system_category_layer3.ToString()); //系統類別(第3層) deviceImportCheck.Add("@device_disaster", device_disaster.ToString()); //緊急應變程序 deviceImportCheck.Add("@is_correct", 0); //驗證是否正確 deviceImportChecks.Add(deviceImportCheck); } } else { Dictionary deviceImportCheck = new Dictionary(); var pre_device_number = String.Join('_', arr_device_number, 0, 3); deviceImportCheck.Add("@device_building_tag", arr_device_number[0]); //設備區域 deviceImportCheck.Add("@device_system_tag", arr_device_number[1]); //設備系統別 deviceImportCheck.Add("@device_floor_tag", arr_device_number[2]); //設備樓層 deviceImportCheck.Add("@device_name_tag", arr_device_number[3]); //設備名稱 deviceImportCheck.Add("@device_serial_tag", arr_device_number[4]); //設備流水號 deviceImportCheck.Add("@device_number", device_number); //設備完整編號 deviceImportCheck.Add("@device_system_category_layer3", device_system_category_layer3.ToString()); //系統類別(第3層) deviceImportCheck.Add("@device_disaster", device_disaster.ToString()); //緊急應變程序 deviceImportCheck.Add("@is_correct", 0); //驗證是否正確 deviceImportChecks.Add(deviceImportCheck); } } //針對棟別刪除檢查OK的設備查詢表 var sDeleteWhere = $@"device_building_tag = '{temp_building}'"; await backendRepository.PurgeOneByGuidWithCustomDBNameAndTable("device_import_ckeck_temp", sDeleteWhere); //var sql_check = @"IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[device_import_ckeck_temp]') AND type in (N'U')) // BEGIN // DROP TABLE [dbo].[device_import_ckeck_temp] // END // CREATE TABLE [dbo].[device_import_ckeck_temp]( // [id] [int] IDENTITY(1,1) NOT NULL, // [device_building_tag] [nvarchar](50) NULL, // [device_system_tag] [nvarchar](50) NULL, // [device_floor_tag] [nvarchar](50) NULL, // [device_kind] [nvarchar](50) NULL, // [device_name_tag] [nvarchar](50) NULL, // [device_serial_tag] [nvarchar](50) NULL, // [device_number] [nvarchar](255) NULL, // [device_system_category_layer3] [varchar](50) NULL, // [device_disaster] [varchar](50) NULL, // [created_at] [datetime] NULL, // CONSTRAINT [PK_device_import_ckeck_temp] PRIMARY KEY CLUSTERED // ( // [id] ASC // )) // ALTER TABLE [dbo].[device_import_ckeck_temp] ADD CONSTRAINT [DF_device_import_ckeck_temp_device_disaster] DEFAULT ((0)) FOR [device_disaster] // ALTER TABLE [dbo].[device_import_ckeck_temp] ADD CONSTRAINT [DF_device_import_ckeck_temp_created_at] DEFAULT (getdate()) FOR [created_at] // "; //await backendRepository.ExecuteSql(sql_check); //檢查正確才寫入至check_temp資料表 await backendRepository.AddMutiByCustomTable(deviceImportChecks, "device_import_ckeck_temp"); } apiResult.Code = "0000"; apiResult.Msg = "匯入成功"; #endregion } catch (Exception exception) { apiResult.Code = "9999"; apiResult.Msg = "系統內部錯誤,請聯絡管理者。"; Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message); } return apiResult; } /// /// 取得設備檢核上方過濾選單 /// /// [HttpPost] public async Task>> GetRawDataCheckFilter() { ApiResult> apiResult = new ApiResult>(); try { string sql = $@" SELECT ct.device_building_tag, ct.device_system_tag, ct.device_system_category_layer3 FROM device_import_ckeck_temp ct GROUP BY ct.device_building_tag, ct.device_system_tag, ct.device_system_category_layer3 "; var deviceCheckFilterRawDatas = await backendRepository.GetAllAsync(sql); List deviceCheckFilters = new List(); var deviceCheckFilterRawData_Group_Building_tag = deviceCheckFilterRawDatas.GroupBy(x => x.Device_building_tag).ToList(); foreach (var deviceCheckFilterRawData_Building_tag in deviceCheckFilterRawData_Group_Building_tag) { DeviceCheckFilter deviceCheckFilter = new DeviceCheckFilter(); deviceCheckFilter.Building_tag = deviceCheckFilterRawData_Building_tag.Key; var sql_amount = @"SELECT COUNT(*) FROM device_import_ckeck_temp WHERE device_building_tag = @Device_building_tag"; deviceCheckFilter.Building_amount = await backendRepository.GetOneAsync(sql_amount, new { Device_building_tag = deviceCheckFilterRawData_Building_tag.Key }); deviceCheckFilter.System_tags = new List(); var deviceCheckFilterRawData_Group_System_tag = deviceCheckFilterRawData_Building_tag.GroupBy(x => x.Device_system_tag).ToList(); foreach (var deviceCheckFilterRawData_System_tag in deviceCheckFilterRawData_Group_System_tag) { DeviceCheckSystemTag deviceCheckSystemTag = new DeviceCheckSystemTag(); deviceCheckSystemTag.System_tag = deviceCheckFilterRawData_System_tag.Key; deviceCheckSystemTag.System_categories = new List(); var deviceCheckFilterRawData_Group_System_category = deviceCheckFilterRawData_System_tag.GroupBy(x => x.Device_system_category_layer3).ToList(); foreach (var deviceCheckFilterRawData_System_category in deviceCheckFilterRawData_Group_System_category) { deviceCheckSystemTag.System_categories.Add(deviceCheckFilterRawData_System_category.Key); } deviceCheckFilter.System_tags.Add(deviceCheckSystemTag); } deviceCheckFilters.Add(deviceCheckFilter); } apiResult.Code = "0000"; apiResult.Data = deviceCheckFilters; } catch (Exception exception) { apiResult.Code = "9999"; apiResult.Msg = "系統內部錯誤,請聯絡管理者。"; Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message); } return apiResult; } /// /// 資料檢核表格 /// /// [HttpPost] public async Task> DeviceCheckTableList(PostDeviceCheckFilter post) { ApiResult apiResult = new ApiResult(); try { string sWhere = ""; string sSubTableWhere = " WHERE {0}.device_building_tag = @Device_building_tag"; if (post.Abnormal == "all") { //異常分類 為全選的時候,才可以指定選擇系統別、設備分類 if (post.System_tag != "all") { sSubTableWhere += " AND {0}.device_system_tag = @Device_system_tag"; } if (post.System_category != "all") { sSubTableWhere += " AND {0}.device_system_category_layer3 = @Device_system_category_layer3"; } } else { sWhere += @"WHERE ct.device_number IS NULL AND d.device_number IS NOT NULL -- OR ct.device_system_category_layer3 != d.device_system_category_layer3 -- OR ct.disaster_system_value != d.device_disaster"; } string sql_temp = $@" SELECT ct.device_number AS check_temp_device_number, ct.device_system_category_layer3 AS check_temp_device_system_category_layer3, ct.system_category_system_key AS check_temp_device_system_category_layer3_key, ct.disaster_system_value AS check_temp_disaster, ct.disaster_system_key AS check_temp_disaster_key, d.device_number, d.device_system_category_layer3 AS device_system_category_layer3, d.system_key AS device_system_category_layer3_key, d.device_disaster, d.device_disaster_type_text, d.device_coordinate, CASE WHEN ct.device_number = d.device_number THEN 0 ELSE 1 END AS compare_device_number, CASE WHEN ct.device_system_category_layer3 = d.device_system_category_layer3 THEN 0 ELSE 1 END AS compare_system_category_layer3, CASE WHEN ct.disaster_system_value = d.device_disaster THEN 0 ELSE 1 END AS compare_device_disaster FROM ( SELECT ct.* , v.system_type AS system_category_system_type, v.system_key AS system_category_system_key, v.system_value AS system_category_system_value, v2.system_type AS disaster_system_type, v2.system_key AS disaster_system_key, v2.system_value AS disaster_system_value FROM device_import_ckeck_temp ct LEFT JOIN variable v ON v.system_type = 'device_system_category_layer3' AND ct.device_system_category_layer3 = v.system_value LEFT JOIN variable v2 ON v2.system_type = 'disaster' AND v2.system_value = ct.device_disaster {{0}} ) ct FULL JOIN ( SELECT d.* , v.system_type, v.system_key, v.system_value, (SELECT STRING_AGG( ISNULL(system_value, ' '), ',') FROM device_disaster dd JOIN variable v ON v.deleted = 0 AND v.system_type = 'disaster' AND v.system_value = dd.device_system_value WHERE dd.device_guid = d.device_guid ) AS device_disaster, (SELECT STRING_AGG( ISNULL(system_key, ' '), ',') FROM device_disaster dd JOIN variable v ON v.deleted = 0 AND v.system_type = 'disaster' AND v.system_value = dd.device_system_value WHERE dd.device_guid = d.device_guid ) AS device_disaster_type_text FROM device d LEFT JOIN variable v ON v.system_type = 'device_system_category_layer3' AND d.device_system_category_layer3 = v.system_value {{1}} AND d.deleted = 0 )d ON ct.device_number = d.device_number {{2}} ORDER BY d.device_number DESC "; var sql = string.Format(sql_temp, string.Format(sSubTableWhere, "ct"), string.Format(sSubTableWhere, "d"), sWhere); var param = new { Device_building_tag = post.Building_tag, Device_system_tag = post.System_tag, Device_system_category_layer3 = post.System_category }; var deviceCheckTableList = await backendRepository.GetAllAsync(sql, param); sSubTableWhere = " WHERE {0}.device_building_tag = @Device_building_tag"; sWhere = @"WHERE ct.device_number IS NULL AND d.device_number IS NOT NULL -- OR ct.device_system_category_layer3 != d.device_system_category_layer3 -- OR ct.disaster_system_value != d.device_disaster"; var sql_abnormal_amount = string.Format(sql_temp, string.Format(sSubTableWhere, "ct"), string.Format(sSubTableWhere, "d") + " AND d.deleted = 0", sWhere); var abnormal = await backendRepository.GetAllAsync(sql_abnormal_amount, param); var abnormal_amount = abnormal.Count(); DeviceCheck deviceCheck = new DeviceCheck(); deviceCheck.DeviceCheckAmount = abnormal_amount; deviceCheck.DeviceCheckTableList = deviceCheckTableList; apiResult.Code = "0000"; apiResult.Data = deviceCheck; } catch (Exception exception) { apiResult.Code = "9999"; apiResult.Msg = "系統內部錯誤,請聯絡管理者。"; Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message); } return apiResult; } /// /// 資料檢核表格 /// /// [HttpPost] public async Task> DeviceCheckReplace(PostDeviceCheckFilter post) { ApiResult apiResult = new ApiResult(); try { //將該棟別的資料更換為正確 string sql_update_correct = @"UPDATE device_import_ckeck_temp SET is_correct = 1 WHERE device_building_tag = @Device_building_tag"; var param = new { Device_building_tag = post.Building_tag }; await backendRepository.ExecuteSql(sql_update_correct, param); //找出當前在device裡面有的,以供後續取代用 string sql = @" SELECT ct.*, d.device_guid FROM ( SELECT * FROM device_import_ckeck_temp ct WHERE ct.device_number IN ( SELECT d.device_number FROM device d WHERE d.deleted = 0 AND d.device_building_tag = @Device_building_tag ) ) ct LEFT JOIN device d ON d.deleted = 0 AND ct.device_number = d.device_number"; var check_temp_replaces = await backendRepository.GetAllAsync(sql, param); foreach (var check_temp_replace in check_temp_replaces) { Dictionary device_replace = new Dictionary() { {"@device_system_category_layer3", check_temp_replace.Device_system_category_layer3}, }; List> device_disaster_dicts = new List>() { { new Dictionary() { { "@device_guid", check_temp_replace.Device_guid }, { "@device_system_value", check_temp_replace.Device_disaster } } } }; await deviceImportRepository.ReplaceOneDeviceInfo(check_temp_replace.Device_guid, device_replace, device_disaster_dicts); } apiResult.Code = "0000"; apiResult.Msg = "取代成功"; } catch (Exception exception) { apiResult.Code = "9999"; apiResult.Msg = "系統內部錯誤,請聯絡管理者。"; Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message); } return apiResult; } } }