using FrontendWebApi.Models; using Microsoft.AspNetCore.Mvc; using Repository.BackendRepository.Interface; using Repository.FrontendRepository.Interface; using System.Collections.Generic; using System.Threading.Tasks; using System; using System.IO; using static FrontendWebApi.Models.Bill; using Microsoft.Extensions.Logging; using System.Text.Json; using Newtonsoft.Json.Linq; using System.Security.Cryptography; using WkHtmlToPdfDotNet.Contracts; using WkHtmlToPdfDotNet; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Configuration; using Serilog.Core; using static FrontendWebApi.ApiControllers.TenantBillController; using System.Reflection; using Microsoft.CodeAnalysis.CSharp.Syntax; using System.Data.SqlTypes; using System.Linq; // For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 namespace FrontendWebApi.ApiControllers { //[Route("api/[controller]")] //[ApiController] //public class TenantBillController public class TenantBillController : MyBaseApiController { private readonly IBackendRepository backendRepository; private readonly ILogger _logger; private readonly IConfiguration Configuration; private IWebHostEnvironment _webHostEnvironment; private readonly IConverter _converter; const string TenantListtable = "archive_electric_meter_tenant_list"; const string TenantBilltable = "archive_electric_meter_tenant_bill"; public TenantBillController(IBackendRepository backendRepository, IFrontendRepository frontendRepository, ILogger logger, IConfiguration configuration, IWebHostEnvironment webHostEnvironment, IConverter converter) { this.backendRepository = backendRepository; this._logger = logger; Configuration = configuration; _webHostEnvironment = webHostEnvironment; _converter = converter; } [HttpPost] public async Task>> GetTenantList() { ApiResult> apiResult = new ApiResult>(); List tenantList = new List(); try { var sqlString = $"SELECT tenant_guid,list_id,tenant_name,bill_perKWH,bill_perRCV " + $"from {TenantListtable} order by created_at"; tenantList = await backendRepository.GetAllAsync(sqlString); apiResult.Code = "0000"; apiResult.Data = tenantList; } catch (Exception exception) { apiResult.Code = "9999"; apiResult.Msg = "讀取不到用戶資料。"; Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message); } return apiResult; } [HttpPost] public async Task> AddOneTenantList([FromBody] TenantList tl) { ApiResult apiResult = new ApiResult(); try { var tenant_guid = Guid.NewGuid(); var tenant_name = tl.tenant_name; var bill_perKWH = tl.bill_perKWH; var bill_perRCV = tl.bill_perRCV; var created_at = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); var sqlString = $"INSERT INTO {TenantListtable} (tenant_guid,list_id,tenant_name, bill_perKWH, bill_perRCV, created_by,created_at) " + $"VALUES ('{tenant_guid}','{tl.list_id}','{tenant_name}', {bill_perKWH}, {bill_perRCV},'{tl.created_by}', '{created_at}')"; await backendRepository.ExecuteSql(sqlString); apiResult.Code = "0000"; apiResult.Msg = "新增成功"; } catch (Exception exception) { apiResult.Code = "9999"; apiResult.Msg = "新增用戶失敗。"; if (exception.Message.Contains($" for key 'PRIMARY'")) { apiResult.Code = "0001"; apiResult.Msg = "已有相同用戶。"; } Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message); } return apiResult; } [HttpPost] public async Task> UpdateOneTenantList([FromBody] TenantList tl) { ApiResult apiResult = new ApiResult(); try { var updated_at = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); var sqlString = $"UPDATE {TenantListtable} SET " + $"`tenant_name` = '{tl.tenant_name}', " + $"`bill_perKWH` = {tl.bill_perKWH}, " + $"`bill_perRCV` = {tl.bill_perRCV}, " + $"`updated_at` = '{updated_at}' " + $"WHERE `tenant_guid` = '{tl.tenant_guid}'"; await backendRepository.ExecuteSql(sqlString); apiResult.Code = "0000"; apiResult.Msg = "修改用戶成功"; } catch (Exception exception) { apiResult.Code = "9999"; apiResult.Msg = "修改失敗。"; if (exception.Message.Contains($"a foreign key constraint")) { apiResult.Code = "0001"; apiResult.Msg = "水電報表仍有該用戶,無法修改名稱。"; } Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message); } return apiResult; } [HttpPost] public async Task> DelOneTenantList([FromBody] TenantList tl) { ApiResult apiResult = new ApiResult(); try { var sqlString = $"delete from {TenantListtable} WHERE tenant_guid = '{tl.tenant_guid}' "; await backendRepository.ExecuteSql(sqlString); apiResult.Code = "0000"; apiResult.Msg = "刪除成功"; } catch (Exception exception) { apiResult.Code = "9999"; apiResult.Msg = "刪除用戶失敗。"; if (exception.Message.Contains($"a foreign key constraint")) { apiResult.Code = "0001"; apiResult.Msg = "水電報表仍有該用戶,無法刪除。"; } Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message); } return apiResult; } [HttpPost] public async Task>> GetTenantBill([FromBody] TenantBill tb) { ApiResult> apiResult = new ApiResult>(); List tenantBill = new List(); try { string tableType = tb.tableType; string building_tag = tb.building_tag; string ElecOrWater = tableType == "elec" ? "E4" : "W1"; string sqlString = null; #region 檢查設備是否沒被刪除且正在連接 string checkDataSql = $@"SELECT device_number,device_name_tag,full_name FROM device WHERE device_name_tag = 'E4' and deleted = 0 and is_link = 1 or device_name_tag = 'W1' and deleted = 0 and is_link = 1; "; var existData = await backendRepository.GetAllAsync(checkDataSql); string checkDataSql2 = $@"select * from archive_electric_meter_tenant_bill; "; var existData2 = await backendRepository.GetAllAsync(checkDataSql2); var commonValues = existData.Select(a => a.device_number) .Intersect(existData2.Select(b => b.device_number)) .ToList(); if (commonValues.Any()) { string updateSql = $@"UPDATE archive_electric_meter_tenant_bill SET deleted = 0 WHERE device_number IN ('{string.Join("', '", commonValues)}')"; await backendRepository.ExecuteSql(updateSql); } var newValues = existData.Select(a => a.device_number) .Except(existData2.Select(b => b.device_number)) .ToList(); if (newValues.Any()) { string insertSql = $@"INSERT INTO archive_electric_meter_tenant_bill (device_number, device_name_tag,full_name, created_at) SELECT device_number, device_name_tag,full_name, NOW() FROM device WHERE device_number IN ('{string.Join("', '", newValues)}')"; await backendRepository.ExecuteSql(insertSql); } var deletedValues = existData2.Select(a => a.device_number) .Except(existData.Select(b => b.device_number)) .ToList(); if (deletedValues.Any()) { string updateSql = $@"UPDATE archive_electric_meter_tenant_bill SET deleted = 1 WHERE device_number IN ('{string.Join("', '", deletedValues)}')"; await backendRepository.ExecuteSql(updateSql); } #endregion if (building_tag == "D2") { sqlString = $"SELECT bill_id,a.device_number,a.full_name,start_timestamp,end_timestamp,result,bill,tenant_name,tenant_guid " + $"from {TenantBilltable} a join device b on a.device_number =b.device_number " + $"where device_building_tag = 'D1' and a.device_name_tag = '{ElecOrWater}' and a.deleted = 0 || device_building_tag = 'D2' and a.device_name_tag = '{ElecOrWater}' and a.deleted = 0"; } else { sqlString = $"SELECT bill_id,a.device_number,a.full_name,start_timestamp,end_timestamp,result,bill,tenant_name,tenant_guid " + $"from {TenantBilltable} a join device b on a.device_number =b.device_number " + $"where device_building_tag = '{building_tag}' and a.device_name_tag = '{ElecOrWater}' and a.deleted = 0"; } tenantBill = await backendRepository.GetAllAsync(sqlString); apiResult.Code = "0000"; apiResult.Data = tenantBill; } catch (Exception exception) { apiResult.Code = "9999"; apiResult.Msg = "讀取水電參考報表失敗。"; Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message); } return apiResult; } [HttpPost] public async Task> UpdateTenantBill([FromBody] TenantBill tb) { ApiResult apiResult = new ApiResult(); try { string bill_per = tb.tableType == "elec" ? "bill_perKWH" : "bill_perRCV"; var updated_at = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); var start_timestamp = tb.start_timestamp; var end_timestamp = tb.end_timestamp; string sqlString = null; string result = "0"; if (tb.start_timestamp == "" || tb.end_timestamp == "" || tb.tenant_guid == "" || tb.tenant_guid == null) { sqlString = $@"UPDATE {TenantBilltable} set tenant_name = (SELECT tenant_name from archive_electric_meter_tenant_list WHERE tenant_guid = '{tb.tenant_guid}'), start_timestamp = '{start_timestamp}',end_timestamp = '{end_timestamp}' , result= 0, bill = 0, updated_at = '{updated_at}', tenant_guid = '{tb.tenant_guid}' WHERE device_number = '{tb.device_number}'"; await backendRepository.ExecuteSql(sqlString); apiResult.Code = "0000"; apiResult.Msg = "資料填寫不完整"; return apiResult; } else { List howManyMonth = new List(); List existMonth = new List(); DateTime date1 = DateTime.Parse(start_timestamp); DateTime date2 = DateTime.Parse(end_timestamp); int monthsApart = ((date2.Year - date1.Year) * 12) + date2.Month - date1.Month; // 找出資料庫存在的水電月份表 string checkTabelSql = $@"SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA like 'ibms_dome_%' and TABLE_NAME like 'archive_electric_water_meter_day_%'"; var existTable = await backendRepository.GetAllAsync(checkTabelSql); for (var i = 0; i <= monthsApart; i++) { howManyMonth.Add($@"archive_electric_water_meter_day_{date1.AddMonths(i).ToString("yyyyMM")}"); } foreach (var item in existTable) { foreach (var data in howManyMonth) { if (item == data) { existMonth.Add(data); } } } if (existMonth.Count == 0) { sqlString = $@"UPDATE {TenantBilltable} set tenant_name = (SELECT tenant_name from archive_electric_meter_tenant_list WHERE tenant_guid = '{tb.tenant_guid}'), start_timestamp = '{start_timestamp}',end_timestamp = '{end_timestamp}' , result= 0, bill = 0, updated_at = '{updated_at}', tenant_guid = '{tb.tenant_guid}' WHERE device_number = '{tb.device_number}'"; } else { string month = $@" SELECT start_timestamp,device_number, sub_result FROM {existMonth[0]} WHERE device_number = '{tb.device_number}'"; for (var i = 1; i < existMonth.Count; i++) { month += $@"UNION ALL SELECT start_timestamp,device_number, sub_result FROM {existMonth[i]} WHERE device_number = '{tb.device_number}' "; } string checkDataSql = $@"(SELECT sum(sub_result) FROM ( {month} ) combined_result WHERE start_timestamp BETWEEN '{start_timestamp}' and '{end_timestamp}') "; var existData = await backendRepository.GetAllAsync(checkDataSql); if (existData.Count != 0) { result = $@"(SELECT ROUND(sum(sub_result)) FROM ( {month} ) combined_result WHERE start_timestamp BETWEEN '{start_timestamp}' and '{end_timestamp}') "; } sqlString = $@"UPDATE {TenantBilltable} set tenant_name = (SELECT tenant_name from archive_electric_meter_tenant_list WHERE tenant_guid = '{tb.tenant_guid}'), start_timestamp = '{start_timestamp}',end_timestamp = '{end_timestamp}' , result= {result}, bill = ROUND(result *(SELECT {bill_per} from {TenantListtable} WHERE tenant_guid = '{tb.tenant_guid}') ), updated_at = '{updated_at}', tenant_guid = '{tb.tenant_guid}' WHERE device_number = '{tb.device_number}'"; } await backendRepository.ExecuteSql(sqlString); } } catch (Exception exception) { apiResult.Code = "9999"; apiResult.Msg = "修改水電參考報表失敗。"; Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message); return apiResult; } apiResult = await CheckDay(tb); return apiResult; } [HttpPost] public async Task> CheckDay([FromBody] TenantBill tb) { ApiResult apiResult = new ApiResult(); if (tb.start_timestamp != "" && tb.end_timestamp != "" && tb.tenant_guid != "") { List tenantBill = new List(); try { string sqlString = $@"select * from {TenantBilltable} WHERE tenant_guid= '{tb.tenant_guid}' and deleted = 0"; tenantBill = await backendRepository.GetAllAsync(sqlString); if (tenantBill.Count > 1) { foreach (TenantBill t in tenantBill) { if (t.start_timestamp == tb.start_timestamp && t.end_timestamp == tb.end_timestamp) { continue; } else { apiResult.Code = "0001"; apiResult.Msg = "該用戶設備的起訖日期不一致,是否將所有設備起訖日期調為一致?"; return apiResult; } } apiResult.Code = "0000"; apiResult.Msg = "修改成功且該用戶的所有設備日期一致"; } } catch (Exception exception) { apiResult.Code = "9999"; apiResult.Msg = "確認日期失敗。"; Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message); } } apiResult.Code = "0000"; apiResult.Msg = "修改成功且不執行CheckDay"; return apiResult; } [HttpPost] public async Task> ChangeDay([FromBody] TenantBill tb) { ApiResult apiResult = new ApiResult(); List tenantBill = new List(); try { string sqlString = $@"select * from {TenantBilltable} WHERE tenant_guid= '{tb.tenant_guid}' and deleted = 0"; tenantBill = await backendRepository.GetAllAsync(sqlString); foreach (TenantBill t in tenantBill) { if (t.start_timestamp == tb.start_timestamp && t.end_timestamp == tb.end_timestamp) { continue; } else { t.start_timestamp = tb.start_timestamp; t.end_timestamp = tb.end_timestamp; t.tableType = t.device_name_tag == "E4" ? "elec": "water"; await UpdateTenantBill(t); } } apiResult.Code = "0000"; apiResult.Msg = "修改成功"; } catch (Exception exception) { apiResult.Code = "9999"; apiResult.Msg = "更換日期失敗。"; Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message); } return apiResult; } [HttpPost] public async Task OutputTenantBill([FromBody] TenantBill tb) { List outputBill = new List(); try { List buildings = tb.building_tag_list; string building_tag = ""; foreach (var item in buildings) { if (item == buildings[0]) { building_tag = item == "D2" ? $@"device_building_tag = 'D1' and a.deleted = 0 || device_building_tag = '{item}' and a.deleted = 0" : $@"device_building_tag = '{item}' and a.deleted = 0" ; } else { building_tag += item == "D2" ? $@"|| device_building_tag = 'D1' and a.deleted = 0 || device_building_tag = '{item}' and a.deleted = 0" : $@"|| device_building_tag = '{item}' and a.deleted = 0"; } } string checkDataSql = $@"select * from archive_electric_meter_tenant_bill a join device c on a.device_number = c.device_number WHERE tenant_name is not null and tenant_guid is not null and tenant_name != '' and tenant_guid !='' AND ({building_tag})"; var existData = await backendRepository.GetAllAsync(checkDataSql); if (existData.Count != 0) { string sqlString = $@"SELECT tenant_name, start_timestamp, end_timestamp, bill_perKWH, bill_perRCV, elec_result, water_result, elec_bill, water_bill, (elec_bill + water_bill) AS total_bill FROM ( SELECT a.tenant_name, MIN(NULLIF(a.start_timestamp, '')) AS start_timestamp, MAX(NULLIF(a.end_timestamp, '')) AS end_timestamp, bill_perKWH, bill_perRCV, SUM(CASE WHEN a.device_name_tag = 'E4' THEN result ELSE 0 END) AS elec_result, SUM(CASE WHEN a.device_name_tag = 'W1' THEN result ELSE 0 END) AS water_result, ROUND(bill_perKWH * SUM(CASE WHEN a.device_name_tag = 'E4' THEN result ELSE 0 END)) AS elec_bill, ROUND(bill_perRCV * SUM(CASE WHEN a.device_name_tag = 'W1' THEN result ELSE 0 END)) AS water_bill FROM archive_electric_meter_tenant_bill a JOIN archive_electric_meter_tenant_list b ON a.tenant_guid = b.tenant_guid JOIN device c ON a.device_number = c.device_number WHERE {building_tag} GROUP BY a.tenant_name ) AS subquery_alias;"; outputBill = await backendRepository.GetAllAsync(sqlString); string filePath = CreateOutputForm(outputBill); byte[] file = System.IO.File.ReadAllBytes(filePath); return new FileContentResult(file, "application/pdf") { FileDownloadName = "水電報表.pdf" }; } else { var data = new { Code = "0001",Msg = "還沒有選擇用戶,無法匯出檔案。"}; return StatusCode(400, data); } } catch (Exception exception) { Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message); } return new FileContentResult(new byte[0], "application/pdf") { FileDownloadName = "Empty.pdf" }; } public string CreateOutputForm(List outputBill) { try { string htmlStr = this.getOutputFormHtmlStr(outputBill); string filepath = Configuration.GetValue("FilePath:OutputForm"); if (!Directory.Exists(filepath)) { Directory.CreateDirectory(filepath); _logger.LogInformation("file CreateOutputForm path: " + filepath); } var nowString = DateTime.UtcNow.AddHours(8).ToString("yyyyMMdd_HHmmss"); //Random r1 = new System.Random(); //string r2 = r1.Next(0, 999).ToString("0000"); //string r2 = RandomNumberGenerator.GetInt32(0, 999).ToString("0000"); filepath += $"{nowString}_水電報表.pdf"; var doc = new HtmlToPdfDocument() { GlobalSettings = { ColorMode = ColorMode.Color, //Orientation = Orientation.Landscape, PaperSize = PaperKind.A4, Out = filepath, Margins = new MarginSettings { Unit = Unit.Millimeters, Top = 10, Bottom = 10, Right = 0, Left = 0 }, }, Objects = { new ObjectSettings() { HtmlContent=htmlStr, WebSettings = { DefaultEncoding = "utf-8"}, FooterSettings = new FooterSettings() { Center = "第 [page] 頁 共 [topage] 頁", FontName = "DFKai-sb", }, LoadSettings = new LoadSettings() { JSDelay = 1000, StopSlowScript = false, BlockLocalFileAccess = false, DebugJavascript = true } //HeaderSettings = { FontSize = 9, Right = "Page [page] of [toPage]", Line = true, Spacing = 2.812 } } } }; // _converter.Warning += ConvertWarning; _converter.Convert(doc); //IntPtr converter = _converter.CreateConverter(doc); //_converter.Tools.DestroyConverter(); return filepath; } catch (Exception ex) { throw new Exception(ex.ToString()); } } private string getOutputFormHtmlStr(List outputBill) { try { string path = Configuration.GetValue("FilePath:OutputFormTemplate"); string image = Configuration.GetValue("FilePath:Image"); //string path = _webHostEnvironment.ContentRootPath + "\\StaticFiles\\import.html"; if (!System.IO.File.Exists(path)) { return ""; } //string cssroot = _webHostEnvironment.ContentRootPath + "\\StaticFiles\\css\\"; string htmlStr = System.IO.File.ReadAllText(path); //string vendorscss = System.IO.File.ReadAllText(cssroot + "vendors.bundle.css"); //string appBundelCss = System.IO.File.ReadAllText(cssroot + "app.bundle.css"); //string skinmasterCss = System.IO.File.ReadAllText(cssroot + "skins\\skin-master.css"); string bill = ""; foreach (var item in outputBill) { bill += $"
" + $"
" + $" \"Taipei " + $"

水電費用明細

" + $"
" + $"
" + $"

費用資訊

" + $"

用戶: {item.tenant_name}

" + $"

起訖時間: {item.start_timestamp} ~ {item.end_timestamp}

" + $" " + $" " + $" " + $" " + $" " + $" " + $" " + $" " + $" " + $" " + $" " + $"
用電量: {item.elec_result}度單價: {item.bill_perKWH}元/度電費總計: {item.elec_bill}元
用水量: {item.water_result}度單價: {item.bill_perRCV}元/度水費總計: {item.water_bill}元
" + $"

總計(費用):各電/水量加總 * 單價(四捨五入)

" + $"
" + $"
" + $"
" + $"

總計金額

" + $"
" + $" {item.total_bill}元 " + $"
" + $"
" + $"
" + $"
"; } htmlStr = htmlStr.Replace("{{bill}}", bill); return htmlStr; } catch (Exception ex) { throw new Exception(ex.ToString()); } } } }