調整同步流程

This commit is contained in:
dev02 2025-06-09 12:07:40 +08:00
commit 130ecba50f
14 changed files with 1569 additions and 887 deletions

View File

@ -266,7 +266,119 @@ namespace Backend.Models
#endregion #endregion
#region weather api
public class Root3
{
public string Success { get; set; }
public Records3 Records { get; set; }
}
public class Records3
{
public string DatasetDescription { get; set; }
public string LocationsName { get; set; }
public string Dataid { get; set; }
public List<Locations3> Locations { get; set; }
}
public class Locations3
{
public List<Location3> Location { get; set; }
}
public class Location3
{
public string LocationName { get; set; }
public string Geocode { get; set; }
public string Latitude { get; set; }
public string Longitude { get; set; }
public List<WeatherElement3> WeatherElement { get; set; }
}
public class WeatherElement3
{
public string ElementName { get; set; }
public List<Time> Time { get; set; }
}
public class Time
{
public string DataTime { get; set; }
public string StartTime { get; set; }
public string EndTime { get; set; }
public List<ElementValue> ElementValue { get; set; }
}
public class ElementValue
{
/// <summary>
/// 溫度
/// </summary>
public string Temperature { get; set; }
/// <summary>
/// 露點溫度
/// </summary>
public string DewPoint { get; set; }
/// <summary>
/// 相對溼度
/// </summary>
public string RelativeHumidity { get; set; }
/// <summary>
/// 體感溫度
/// </summary>
public string ApparentTemperature { get; set; }
/// <summary>
/// 舒適度指數
/// </summary>
public string ComfortIndex { get; set; }
/// <summary>
/// 舒適度指數描述
/// </summary>
public string ComfortIndexDescription { get; set; }
/// <summary>
/// 風速
/// </summary>
public string WindSpeed { get; set; }
/// <summary>
/// 蒲福氏風級
/// </summary>
public string BeaufortScale { get; set; }
/// <summary>
/// 風向
/// </summary>
public string WindDirection { get; set; }
/// <summary>
/// 降雨機率
/// </summary>
public string ProbabilityOfPrecipitation { get; set; }
/// <summary>
/// 天氣現象
/// </summary>
public string Weather { get; set; }
/// <summary>
/// 天氣現象代號
/// </summary>
public string WeatherCode { get; set; }
/// <summary>
/// 天氣預報綜合描述
/// </summary>
public string WeatherDescription { get; set; }
}
public enum ElementName
{
,
,
,
,
,
,
,
,
,
}
#endregion

View File

@ -121,7 +121,7 @@ namespace BackendWorkerService
//); //);
//#endregion //#endregion
#region ( 5 ) #region ( 5 )()
services.AddSingleton<ParkingJob>(); services.AddSingleton<ParkingJob>();
services.AddSingleton( services.AddSingleton(
new JobSchedule(jobType: typeof(ParkingJob), cronExpression: configuration.GetValue<string>("BackgroundServiceCron:ParkingJob")) new JobSchedule(jobType: typeof(ParkingJob), cronExpression: configuration.GetValue<string>("BackgroundServiceCron:ParkingJob"))
@ -142,7 +142,7 @@ namespace BackendWorkerService
); );
#endregion #endregion
#region API #region API()
services.AddSingleton<Quartz.Jobs.WeatherAPIJob>(); services.AddSingleton<Quartz.Jobs.WeatherAPIJob>();
services.AddSingleton( services.AddSingleton(
new JobSchedule(jobType: typeof(Quartz.Jobs.WeatherAPIJob), cronExpression: configuration.GetValue<string>("BackgroundServiceCron:WeatherAPIJob")) new JobSchedule(jobType: typeof(Quartz.Jobs.WeatherAPIJob), cronExpression: configuration.GetValue<string>("BackgroundServiceCron:WeatherAPIJob"))

View File

@ -355,8 +355,8 @@ namespace BackendWorkerService.Quartz.Jobs
row.TryGetValue("@start_timestamp", out var yyyymmData); row.TryGetValue("@start_timestamp", out var yyyymmData);
dbDateName = System.DateTime.Parse(yyyymmData.ToString()).ToString("yyyyMM"); dbDateName = System.DateTime.Parse(yyyymmData.ToString()).ToString("yyyyMM");
var sql = $@"CREATE TABLE IF NOT EXISTS `archive_electric_water_meter_day_{dbDateName}` ( var sql = $@"CREATE TABLE IF NOT EXISTS `archive_electric_water_meter_day_{dbDateName}` (
`device_number` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, `device_number` varchar(50) NOT NULL,
`point` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, `point` varchar(20) NOT NULL,
`start_timestamp` datetime(6) NOT NULL, `start_timestamp` datetime(6) NOT NULL,
`end_timestamp` datetime(6) NULL DEFAULT NULL, `end_timestamp` datetime(6) NULL DEFAULT NULL,
`count_rawdata` int(11) NULL DEFAULT NULL, `count_rawdata` int(11) NULL DEFAULT NULL,
@ -367,11 +367,11 @@ namespace BackendWorkerService.Quartz.Jobs
`sum_rawdata` decimal(15, 3) NULL DEFAULT NULL, `sum_rawdata` decimal(15, 3) NULL DEFAULT NULL,
`is_complete` tinyint(3) UNSIGNED NULL DEFAULT NULL COMMENT '0: 1:', `is_complete` tinyint(3) UNSIGNED NULL DEFAULT NULL COMMENT '0: 1:',
`repeat_times` int(11) NULL DEFAULT 0 COMMENT '', `repeat_times` int(11) NULL DEFAULT 0 COMMENT '',
`fail_reason` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '', `fail_reason` varchar(4000) NULL DEFAULT NULL COMMENT '',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime(6) NULL DEFAULT NULL, `updated_at` datetime(6) NULL DEFAULT NULL,
PRIMARY KEY (`device_number`, `point`, `start_timestamp`) USING BTREE PRIMARY KEY (`device_number`, `point`, `start_timestamp`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = DYNAMIC;
SET FOREIGN_KEY_CHECKS = 1; SET FOREIGN_KEY_CHECKS = 1;
UPDATE archive_electric_water_meter_day_{dbDateName} SET UPDATE archive_electric_water_meter_day_{dbDateName} SET
@ -524,8 +524,8 @@ namespace BackendWorkerService.Quartz.Jobs
dbDateName = System.DateTime.Parse(yyyymmData.ToString()).ToString("yyyyMM"); dbDateName = System.DateTime.Parse(yyyymmData.ToString()).ToString("yyyyMM");
var sql = $@" var sql = $@"
CREATE TABLE IF NOT EXISTS `archive_electric_water_meter_day_{dbDateName}` ( CREATE TABLE IF NOT EXISTS `archive_electric_water_meter_day_{dbDateName}` (
`device_number` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, `device_number` varchar(50) NOT NULL,
`point` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, `point` varchar(20) NOT NULL,
`start_timestamp` datetime(6) NOT NULL, `start_timestamp` datetime(6) NOT NULL,
`end_timestamp` datetime(6) NULL DEFAULT NULL, `end_timestamp` datetime(6) NULL DEFAULT NULL,
`count_rawdata` int(11) NULL DEFAULT NULL, `count_rawdata` int(11) NULL DEFAULT NULL,
@ -536,11 +536,11 @@ namespace BackendWorkerService.Quartz.Jobs
`sum_rawdata` decimal(15, 3) NULL DEFAULT NULL, `sum_rawdata` decimal(15, 3) NULL DEFAULT NULL,
`is_complete` tinyint(3) UNSIGNED NULL DEFAULT NULL COMMENT '0: 1:', `is_complete` tinyint(3) UNSIGNED NULL DEFAULT NULL COMMENT '0: 1:',
`repeat_times` int(11) NULL DEFAULT 0 COMMENT '', `repeat_times` int(11) NULL DEFAULT 0 COMMENT '',
`fail_reason` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '', `fail_reason` varchar(4000) NULL DEFAULT NULL COMMENT '',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime(6) NULL DEFAULT NULL, `updated_at` datetime(6) NULL DEFAULT NULL,
PRIMARY KEY (`device_number`, `point`, `start_timestamp`) USING BTREE PRIMARY KEY (`device_number`, `point`, `start_timestamp`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = DYNAMIC;
UPDATE archive_electric_water_meter_day_{dbDateName} SET UPDATE archive_electric_water_meter_day_{dbDateName} SET
count_rawdata = @count_rawdata, count_rawdata = @count_rawdata,
@ -1357,8 +1357,8 @@ namespace BackendWorkerService.Quartz.Jobs
{ {
var sql = $@" var sql = $@"
CREATE TABLE IF NOT EXISTS `archive_electric_water_meter_month` ( CREATE TABLE IF NOT EXISTS `archive_electric_water_meter_month` (
`device_number` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, `device_number` varchar(50) NOT NULL,
`point` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, `point` varchar(20) NOT NULL,
`start_timestamp` datetime(6) NOT NULL, `start_timestamp` datetime(6) NOT NULL,
`end_timestamp` datetime(6) NULL DEFAULT NULL, `end_timestamp` datetime(6) NULL DEFAULT NULL,
`count_rawdata` int(11) NULL DEFAULT NULL, `count_rawdata` int(11) NULL DEFAULT NULL,
@ -1369,11 +1369,11 @@ namespace BackendWorkerService.Quartz.Jobs
`sum_rawdata` decimal(15, 3) NULL DEFAULT NULL, `sum_rawdata` decimal(15, 3) NULL DEFAULT NULL,
`is_complete` tinyint(3) UNSIGNED NULL DEFAULT NULL COMMENT '0: 1:', `is_complete` tinyint(3) UNSIGNED NULL DEFAULT NULL COMMENT '0: 1:',
`repeat_times` int(11) NULL DEFAULT 0 COMMENT '', `repeat_times` int(11) NULL DEFAULT 0 COMMENT '',
`fail_reason` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '', `fail_reason` varchar(4000) NULL DEFAULT NULL COMMENT '',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime(6) NULL DEFAULT NULL, `updated_at` datetime(6) NULL DEFAULT NULL,
PRIMARY KEY (`device_number`, `point`, `start_timestamp`) USING BTREE PRIMARY KEY (`device_number`, `point`, `start_timestamp`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = DYNAMIC;
SET FOREIGN_KEY_CHECKS = 1; SET FOREIGN_KEY_CHECKS = 1;
UPDATE archive_electric_water_meter_month SET UPDATE archive_electric_water_meter_month SET
@ -1523,8 +1523,8 @@ namespace BackendWorkerService.Quartz.Jobs
{ {
var sql = $@" var sql = $@"
CREATE TABLE IF NOT EXISTS `archive_electric_water_meter_month` ( CREATE TABLE IF NOT EXISTS `archive_electric_water_meter_month` (
`device_number` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, `device_number` varchar(50) NOT NULL,
`point` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, `point` varchar(20) NOT NULL,
`start_timestamp` datetime(6) NOT NULL, `start_timestamp` datetime(6) NOT NULL,
`end_timestamp` datetime(6) NULL DEFAULT NULL, `end_timestamp` datetime(6) NULL DEFAULT NULL,
`count_rawdata` int(11) NULL DEFAULT NULL, `count_rawdata` int(11) NULL DEFAULT NULL,
@ -1535,11 +1535,11 @@ namespace BackendWorkerService.Quartz.Jobs
`sum_rawdata` decimal(15, 3) NULL DEFAULT NULL, `sum_rawdata` decimal(15, 3) NULL DEFAULT NULL,
`is_complete` tinyint(3) UNSIGNED NULL DEFAULT NULL COMMENT '0: 1:', `is_complete` tinyint(3) UNSIGNED NULL DEFAULT NULL COMMENT '0: 1:',
`repeat_times` int(11) NULL DEFAULT 0 COMMENT '', `repeat_times` int(11) NULL DEFAULT 0 COMMENT '',
`fail_reason` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '', `fail_reason` varchar(4000) NULL DEFAULT NULL COMMENT '',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime(6) NULL DEFAULT NULL, `updated_at` datetime(6) NULL DEFAULT NULL,
PRIMARY KEY (`device_number`, `point`, `start_timestamp`) USING BTREE PRIMARY KEY (`device_number`, `point`, `start_timestamp`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = DYNAMIC;
SET FOREIGN_KEY_CHECKS = 1; SET FOREIGN_KEY_CHECKS = 1;
UPDATE archive_electric_water_meter_month SET UPDATE archive_electric_water_meter_month SET

View File

@ -38,14 +38,13 @@ namespace BackendWorkerService.Quartz.Jobs
private readonly IBackendRepository backendRepository; private readonly IBackendRepository backendRepository;
private readonly ILogger<Task_Detail> loggers; private readonly ILogger<Task_Detail> loggers;
public LightScheduleJob(ILogger<LightScheduleJob> logger, public LightScheduleJob(ILogger<LightScheduleJob> logger,
IBackgroundServiceRepository backgroundServiceRepository, IBackendRepository backendRepository, ILogger<Task_Detail> loggers) IBackgroundServiceRepository backgroundServiceRepository, IBackendRepository backendRepository, ILogger<Task_Detail> loggers)
{ {
this.logger = logger; this.logger = logger;
this.backgroundServiceRepository = backgroundServiceRepository; this.backgroundServiceRepository = backgroundServiceRepository;
this.backendRepository = backendRepository; this.backendRepository = backendRepository;
this.loggers = loggers; this.loggers = loggers;
} }
public async Task Execute(IJobExecutionContext context) public async Task Execute(IJobExecutionContext context)
{ {
Task_Detail task_Detail = new Task_Detail(loggers, backendRepository); Task_Detail task_Detail = new Task_Detail(loggers, backendRepository);
@ -58,33 +57,13 @@ namespace BackendWorkerService.Quartz.Jobs
await task_Detail.InsertWorkTime("LightScheduleJob", "light_schedule"); await task_Detail.InsertWorkTime("LightScheduleJob", "light_schedule");
var TimeNow = DateTime.Now.ToString("dddd HH:mm"); var TimeNow = DateTime.Now.ToString("dddd HH:mm");
var schedule = await backendRepository.GetAllAsync<Schedule>("light_schedule","deleted = 0 and status = 1"); var schedule = await backendRepository.GetAllAsync<Schedule>("light_schedule", "deleted = 0 and status = 1");
string date = DateTime.Now.ToString("yyyy-MM-dd"); string date = DateTime.Now.ToString("yyyy-MM-dd");
foreach (var oneSchedule in schedule) foreach (var oneSchedule in schedule)
{ {
// 檢查執行log // 先檢查今日否需執行
string light_schedule_guid = oneSchedule.light_schedule_guid;
string sWhere = @$"light_schedule_guid = '{light_schedule_guid}' and date = '{date}'";
var schedule_log = await backendRepository.GetOneAsync<ScheduleLog>("light_schedule_log", sWhere);
string start_time = null;
string end_time = null;
if (schedule_log != null)
{
start_time = schedule_log.start_time;
end_time = schedule_log.end_time;
}
if (schedule_log == null)
{
Dictionary<string, object> log = new Dictionary<string, object>()
{
{ "@light_schedule_guid", light_schedule_guid},
{ "@date", date},
};
await backendRepository.AddOneByCustomTable(log, "light_schedule_log");
}
// 如果log有紀錄
var weeklistN = oneSchedule.week.Split(','); var weeklistN = oneSchedule.week.Split(',');
List<string> weeklist = new List<string>(); List<string> weeklist = new List<string>();
foreach (var weekN in weeklistN) foreach (var weekN in weeklistN)
@ -102,35 +81,53 @@ namespace BackendWorkerService.Quartz.Jobs
}; };
weeklist.Add(week); weeklist.Add(week);
} }
var Time = TimeNow.Split(" "); var Time = TimeNow.Split(" ");
string check = string.Empty; if (!weeklist.Contains(Time[0])) { continue; }
// 檢查起始執行
if (start_time == null && DateTime.Parse(Time[1]) >= DateTime.Parse(oneSchedule.start_time)) // 檢查執行log
{ string light_schedule_guid = oneSchedule.light_schedule_guid;
check = "<real val='true' />"; string sWhere = @$"light_schedule_guid = '{light_schedule_guid}' and date = '{date}'";
UpdatedNiagara(oneSchedule, check); var schedule_log = await backendRepository.GetOneAsync<ScheduleLog>("light_schedule_log", sWhere);
Dictionary<string, object> log = new Dictionary<string, object>() string start_time = null;
{ string end_time = null;
{ "@start_time", Time[1]}, if (schedule_log != null)
}; {
await backendRepository.UpdateOneByCustomTable(log, "light_schedule_log", $"light_schedule_guid = '{light_schedule_guid}' and date = '{date}'"); start_time = schedule_log.start_time;
logger.LogInformation($"【LightScheduleJob】【燈控排程開啟成功】排程名稱 :{oneSchedule.full_name}"); end_time = schedule_log.end_time;
} }
if (schedule_log == null)
{
Dictionary<string, object> log = new Dictionary<string, object>()
{
{ "@light_schedule_guid", light_schedule_guid },
{ "@date", date },
};
await backendRepository.AddOneByCustomTable(log, "light_schedule_log");
}
string check = string.Empty;
// 檢查起始執行
if (start_time == null && end_time == null && DateTime.Parse(Time[1]) >= DateTime.Parse(oneSchedule.start_time))
{
check = "true"; // 開啟
}
// 檢查結束執行 // 檢查結束執行
if (end_time == null && DateTime.Parse(Time[1]) >= DateTime.Parse(oneSchedule.end_time)) if (end_time == null && DateTime.Parse(Time[1]) >= DateTime.Parse(oneSchedule.end_time))
{ {
check = "<real val='false' />"; check = "false"; // 關閉
UpdatedNiagara(oneSchedule, check); }
Dictionary<string, object> log = new Dictionary<string, object>()
{ if (!string.IsNullOrEmpty(check))
{ "@end_time", Time[1]}, {
}; bool requestSuccess = await UpdatedNiagara(oneSchedule, check);
await backendRepository.UpdateOneByCustomTable(log, "light_schedule_log", $"light_schedule_guid = '{light_schedule_guid}' and date = '{date}'");
logger.LogInformation($"【LightScheduleJob】【燈控排程關閉成功】排程名稱 :{oneSchedule.full_name}");
} }
} }
await task_Detail.InsertWorkTime_End("LightScheduleJob", "light_schedule"); await task_Detail.InsertWorkTime_End("LightScheduleJob", "light_schedule");
} }
catch (Exception ex) catch (Exception ex)
@ -146,52 +143,279 @@ namespace BackendWorkerService.Quartz.Jobs
logger.LogError("【LightScheduleJob】【任務失敗】[Exception]{0}", exception.ToString()); logger.LogError("【LightScheduleJob】【任務失敗】[Exception]{0}", exception.ToString());
} }
} }
public async void UpdatedNiagara(Schedule oneSchedule, string check)
public async Task<bool> UpdatedNiagara(Schedule oneSchedule, string check)
{ {
try try
{ {
var deviceNumList = await backendRepository.GetAllAsync<string>(@$"select d.device_number from schedule_device sd join device d on sd.device_guid = d.device_guid // 取得排程所對應的設備號碼列表
where light_schedule_guid = '{oneSchedule.light_schedule_guid}' and is_link = 1"); var deviceNumList = await backendRepository.GetAllAsync<string>(@$"SELECT d.device_number
FROM schedule_device sd
JOIN device d ON sd.device_guid = d.device_guid
WHERE light_schedule_guid = '{oneSchedule.light_schedule_guid}' AND is_link = 1");
// 取得obix配置
var variableObix = await backendRepository.GetAllAsync<Backend.Models.KeyValue>(@$"SELECT system_value as Value, system_key as Name
FROM variable
WHERE deleted = 0 AND system_type = 'obixConfig'");
// 取得obix相關配置參數
string url = variableObix.FirstOrDefault(x => x.Name == "ApiBase")?.Value;
string account = variableObix.FirstOrDefault(x => x.Name == "UserName")?.Value;
string pass = variableObix.FirstOrDefault(x => x.Name == "Password")?.Value;
// 檢查是否有配置缺失
if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(account) || string.IsNullOrEmpty(pass))
{
logger.LogWarning("【LightScheduleJob】【obix配置缺失】請檢查obix配置");
return false;
}
// 準備HTTP請求的基本資訊
string authInfo = Convert.ToBase64String(Encoding.Default.GetBytes($"{account}:{pass}"));
// 構建每個設備的請求
List<string> batchRequests = new List<string>();
TagChangeFunction tagChange = new TagChangeFunction();
var variableObix = await backendRepository.GetAllAsync<Backend.Models.KeyValue>("SELECT system_value as Value, system_key as Name FROM variable WHERE deleted = 0 AND system_type = 'obixConfig'");
string url = variableObix.Where(x => x.Name == "ApiBase").Select(x => x.Value).FirstOrDefault();
string account = variableObix.Where(x => x.Name == "UserName").Select(x => x.Value).FirstOrDefault();
string pass = variableObix.Where(x => x.Name == "Password").Select(x => x.Value).FirstOrDefault();
foreach (var deviceNum in deviceNumList) foreach (var deviceNum in deviceNumList)
{ {
TagChangeFunction tagChange = new TagChangeFunction(); // 處理設備號碼分解到URL中
var d = tagChange.AddStringIfStartsWithDigit(deviceNum, "$3"); var d = tagChange.AddStringIfStartsWithDigit(deviceNum, "$3");
var html = $"{url}obix/config/Arena/" + $"{d[0]}/{d[1]}/{d[2]}/{d[3]}/{deviceNum}/SSC/set"; var uri = $"{url}obix/config/Arena/{d[0]}/{d[1]}/{d[2]}/{d[3]}/{deviceNum}/SSC/set";
string authInfo = account + ":" + pass;
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(html);
request.Method = "POST";
request.Accept = "application/json; charset=utf-8";
request.Headers["Authorization"] = "Basic " + authInfo;
byte[] byteArray = Encoding.UTF8.GetBytes(check);
using (Stream reqStream = request.GetRequestStream())
{
reqStream.Write(byteArray, 0, byteArray.Length);
}
var response = (HttpWebResponse)request.GetResponse();
string strResponse = "";
using (var sr = new StreamReader(response.GetResponseStream())) // 構建要發送的實體資料
{ string realData = $"<real name='in' val='{check}' />";
strResponse = sr.ReadToEnd();
} // 建立批次請求
// 只取err會取到override batchRequests.Add($"<uri is='obix:Invoke' val='{uri}'>" + realData + "</uri>");
if (strResponse.Contains("<err"))
{
logger.LogWarning($"【LightScheduleJob 】【set niagara light value fail】[排程 名稱]{oneSchedule.full_name},[設備 名稱]{deviceNum}");
}
} }
// 構建整體批次請求
var batchRequestData = $@"<list is='obix:BatchIn'>
{string.Join("", batchRequests)}
</list>";
// 發送批次請求
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url + "obix/batch");
request.Method = "POST";
request.Accept = "application/json; charset=utf-8";
request.Headers["Authorization"] = "Basic " + authInfo;
// 將所有設備的請求內容合併成一個批次請求
byte[] byteArray = Encoding.UTF8.GetBytes(batchRequestData);
using (Stream reqStream = request.GetRequestStream())
{
reqStream.Write(byteArray, 0, byteArray.Length);
}
// 發送請求並處理回應
var response = (HttpWebResponse)request.GetResponse();
string responseContent = string.Empty;
using (var sr = new StreamReader(response.GetResponseStream()))
{
responseContent = sr.ReadToEnd();
}
// 檢查回應中是否有錯誤
if (responseContent.Contains("<err"))
{
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(responseContent);
// 找到所有的 err 節點
XmlNodeList errNodes = xmlDocument.GetElementsByTagName("err");
// 如果有錯誤節點,進行處理
foreach (XmlNode errNode in errNodes)
{
logger.LogWarning($"【LightScheduleJob】【Niagara燈控設置失敗】排程名稱 :{oneSchedule.full_name} 顯示錯誤: {errNode}");
}
return false; // 如果有錯誤,返回 false
}
// 成功後記錄並更新結束時間
Dictionary<string, object> log = new Dictionary<string, object>();
string time = DateTime.Now.ToString("HH:mm");
if (check == "true")
{
log.Add("@start_time", time);
}
else
{
log.Add("@end_time", time);
}
await backendRepository.UpdateOneByCustomTable(log, "light_schedule_log", $"light_schedule_guid = '{oneSchedule.light_schedule_guid}' and date = '{DateTime.Now:yyyy-MM-dd}'");
logger.LogInformation($"【LightScheduleJob】【Niagara燈控設置成功】排程名稱 :{oneSchedule.full_name}");
return true;
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.LogError("【LightScheduleJob】" + "UpdatedNiagaraFail:" + ex.ToString()); logger.LogError("【LightScheduleJob】批次請求發送失敗:" + ex.ToString());
throw ex; return false;
} }
} }
//public async Task Execute(IJobExecutionContext context)
//{
// Task_Detail task_Detail = new Task_Detail(loggers, backendRepository);
// try
// {
// if (await task_Detail.GetNeedWorkTask("LightScheduleJob", "light_schedule"))
// {
// try
// {
// await task_Detail.InsertWorkTime("LightScheduleJob", "light_schedule");
// var TimeNow = DateTime.Now.ToString("dddd HH:mm");
// var schedule = await backendRepository.GetAllAsync<Schedule>("light_schedule","deleted = 0 and status = 1");
// string date = DateTime.Now.ToString("yyyy-MM-dd");
// foreach (var oneSchedule in schedule)
// {
// // 檢查執行log
// string light_schedule_guid = oneSchedule.light_schedule_guid;
// string sWhere = @$"light_schedule_guid = '{light_schedule_guid}' and date = '{date}'";
// var schedule_log = await backendRepository.GetOneAsync<ScheduleLog>("light_schedule_log", sWhere);
// string start_time = null;
// string end_time = null;
// if (schedule_log != null)
// {
// start_time = schedule_log.start_time;
// end_time = schedule_log.end_time;
// }
// if (schedule_log == null)
// {
// Dictionary<string, object> log = new Dictionary<string, object>()
// {
// { "@light_schedule_guid", light_schedule_guid},
// { "@date", date},
// };
// await backendRepository.AddOneByCustomTable(log, "light_schedule_log");
// }
// // 如果log有紀錄
// var weeklistN = oneSchedule.week.Split(',');
// List<string> weeklist = new List<string>();
// foreach (var weekN in weeklistN)
// {
// var week = weekN switch
// {
// "0" => "星期日",
// "1" => "星期一",
// "2" => "星期二",
// "3" => "星期三",
// "4" => "星期四",
// "5" => "星期五",
// "6" => "星期六",
// _ => ""
// };
// weeklist.Add(week);
// }
// var Time = TimeNow.Split(" ");
// string check = string.Empty;
// // 檢查起始執行
// if (start_time == null && DateTime.Parse(Time[1]) >= DateTime.Parse(oneSchedule.start_time))
// {
// check = "<real val='true' />";
// UpdatedNiagara(oneSchedule, check);
// Dictionary<string, object> log = new Dictionary<string, object>()
// {
// { "@start_time", Time[1]},
// };
// await backendRepository.UpdateOneByCustomTable(log, "light_schedule_log", $"light_schedule_guid = '{light_schedule_guid}' and date = '{date}'");
// logger.LogInformation($"【LightScheduleJob】【燈控排程開啟成功】排程名稱 :{oneSchedule.full_name}");
// }
// // 檢查結束執行
// if (end_time == null && DateTime.Parse(Time[1]) >= DateTime.Parse(oneSchedule.end_time))
// {
// check = "<real val='false' />";
// UpdatedNiagara(oneSchedule, check);
// Dictionary<string, object> log = new Dictionary<string, object>()
// {
// { "@end_time", Time[1]},
// };
// await backendRepository.UpdateOneByCustomTable(log, "light_schedule_log", $"light_schedule_guid = '{light_schedule_guid}' and date = '{date}'");
// logger.LogInformation($"【LightScheduleJob】【燈控排程關閉成功】排程名稱 :{oneSchedule.full_name}");
// }
// }
// await task_Detail.InsertWorkTime_End("LightScheduleJob", "light_schedule");
// }
// catch (Exception ex)
// {
// logger.LogInformation($"LightScheduleJob fail");
// await task_Detail.WorkFail("LightScheduleJob", "light_schedule", ex.Message.ToString());
// }
// }
// }
// catch (Exception exception)
// {
// logger.LogError("【LightScheduleJob】【任務失敗】");
// logger.LogError("【LightScheduleJob】【任務失敗】[Exception]{0}", exception.ToString());
// }
//}
//public async void UpdatedNiagara(Schedule oneSchedule, string check)
//{
// try
// {
// var deviceNumList = await backendRepository.GetAllAsync<string>(@$"select d.device_number from schedule_device sd join device d on sd.device_guid = d.device_guid
// where light_schedule_guid = '{oneSchedule.light_schedule_guid}' and is_link = 1");
// var variableObix = await backendRepository.GetAllAsync<Backend.Models.KeyValue>("SELECT system_value as Value, system_key as Name FROM variable WHERE deleted = 0 AND system_type = 'obixConfig'");
// string url = variableObix.Where(x => x.Name == "ApiBase").Select(x => x.Value).FirstOrDefault();
// string account = variableObix.Where(x => x.Name == "UserName").Select(x => x.Value).FirstOrDefault();
// string pass = variableObix.Where(x => x.Name == "Password").Select(x => x.Value).FirstOrDefault();
// foreach (var deviceNum in deviceNumList)
// {
// TagChangeFunction tagChange = new TagChangeFunction();
// var d = tagChange.AddStringIfStartsWithDigit(deviceNum, "$3");
// var html = $"{url}obix/config/Arena/" + $"{d[0]}/{d[1]}/{d[2]}/{d[3]}/{deviceNum}/SSC/set";
// string authInfo = account + ":" + pass;
// authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
// HttpWebRequest request = (HttpWebRequest)WebRequest.Create(html);
// request.Method = "POST";
// request.Accept = "application/json; charset=utf-8";
// request.Headers["Authorization"] = "Basic " + authInfo;
// byte[] byteArray = Encoding.UTF8.GetBytes(check);
// using (Stream reqStream = request.GetRequestStream())
// {
// reqStream.Write(byteArray, 0, byteArray.Length);
// }
// var response = (HttpWebResponse)request.GetResponse();
// string strResponse = "";
// using (var sr = new StreamReader(response.GetResponseStream()))
// {
// strResponse = sr.ReadToEnd();
// }
// // 只取err會取到override
// if (strResponse.Contains("<err"))
// {
// logger.LogWarning($"【LightScheduleJob 】【set niagara light value fail】[排程 名稱]{oneSchedule.full_name},[設備 名稱]{deviceNum}");
// }
// }
// }
// catch (Exception ex)
// {
// logger.LogError("【LightScheduleJob】" + "UpdatedNiagaraFail:" + ex.ToString());
// throw ex;
// }
//}
} }
} }

View File

@ -95,79 +95,33 @@ namespace BackendWorkerService.Quartz.Jobs
if (spaceResponseResult != null && spaceResponseResult.Code == "20000") if (spaceResponseResult != null && spaceResponseResult.Code == "20000")
{ {
List<string> batchRequests = new List<string>(); // 用來存儲批次請求的列表
foreach (var area in spaceResponseResult.Payload.Areas) foreach (var area in spaceResponseResult.Payload.Areas)
{ {
//找出對定的設備代碼 // 找出對應的設備代碼
var selectedMapping = parkingSapceMapping.Where(x => x.System_key == area.Name).FirstOrDefault(); var selectedMapping = parkingSapceMapping.FirstOrDefault(x => x.System_key == area.Name);
if (selectedMapping != null) if (selectedMapping != null)
{ {
item = area.Name; var name = area.Name; // 保存設備名稱
var tagName = selectedMapping.system_value; var tagName = selectedMapping.system_value;
var apiFormat = @"{0}obix/config/Arena/{1}/{2}/{3}/{4}/{5}/CV/set"; var apiFormat = @"{0}obix/config/Arena/{1}/{2}/{3}/{4}/{5}/CV/set";
var tagNameSplit = tagName.Split("_"); var tagNameSplit = tagName.Split("_");
var parames = new List<object>(); var parames = new List<object> { parkingConfig.ApiBase };
parames.Add(parkingConfig.ApiBase); parames.AddRange(tagNameSplit.Take(tagNameSplit.Length - 1)); // tag 前 4段
for (var i = 0; i < tagNameSplit.Length; i++) parames.Add(tagName); // 最後一段 完整 tag
{
if (i != tagNameSplit.Length - 1)
{
parames.Add(tagNameSplit[i]); // tag 前 4段
}
else
{
parames.Add(tagName); // 第五段 完整 tag
}
}
//logger.LogError(@$"【ParkingJob】【停車場剩餘車位】{apiFormat}");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(apiFormat, parames.ToArray()));
request.Method = "POST";
request.Headers.Add("Authorization", "Basic " + encoded);
request.PreAuthenticate = true;
request.Timeout = System.Threading.Timeout.Infinite;
string requestUri = string.Format(apiFormat, parames.ToArray());
// 構建要發送的實體資料
var real = $@"<real val='{area.Remain}' />"; var real = $@"<real val='{area.Remain}' />";
byte[] realByteArray = Encoding.UTF8.GetBytes(real); batchRequests.Add($"<uri is='obix:Invoke' val='{requestUri}'>" +
using (Stream reqStream = request.GetRequestStream()) $"<real name='in' val='{area.Remain}' />" +
{ $"</uri>");
reqStream.Write(realByteArray, 0, realByteArray.Length);
}
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
var responseContent = new StreamReader(response.GetResponseStream()).ReadToEnd();
XmlDocument xmlDocument = new XmlDocument(); // 進行日誌記錄
xmlDocument.LoadXml(responseContent); logger.LogInformation("【ParkingJob】【停車場剩餘車位】準備更新設備{0},剩餘車位數:{1}", name, area.Remain);
string json = JsonConvert.SerializeXmlNode(xmlDocument);
JObject jsonResult = (JObject)JsonConvert.DeserializeObject(json);
if (jsonResult.ContainsKey("err")) //抓取錯誤
{
logger.LogError("【ParkingJob】【停車場剩餘車位資訊】");
logger.LogError("【ParkingJob】【停車場剩餘車位資訊】[錯誤內容]{0}", json);
}
else
{
if (jsonResult.ContainsKey("real")) //表示可以讀取到內容
{
List<Dictionary<string, object>> ontimeRawDatas = new List<Dictionary<string, object>>();
var realList = jsonResult["real"];
var display = realList["@display"];
if (display != null)
{
var tempStrSplit = display.ToString().Split(" ");
if (tempStrSplit[0] != area.Remain.ToString())
{
logger.LogError("【ParkingJob】【停車場剩餘車位資訊】[修改失敗]{0}", display.ToString());
}
else
{
logger.LogInformation("【ParkingJob】【停車場剩餘車位資訊】[修改成功]{0}", display.ToString());
logger.LogInformation("【ParkingJob】【停車場剩餘車位資訊】[停車場資訊]{0}", item);
}
}
}
}
} }
else else
{ {
@ -175,11 +129,59 @@ namespace BackendWorkerService.Quartz.Jobs
} }
} }
if (batchRequests.Any())
{
// 建立批次請求 XML
var batchRequestXml = $@"<list is='obix:BatchIn'>
{string.Join("", batchRequests)}
</list>";
// 發送批次請求
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(parkingConfig.ApiBase + "obix/batch");
request.Method = "POST";
request.Headers.Add("Authorization", "Basic " + encoded);
request.PreAuthenticate = true;
request.Timeout = System.Threading.Timeout.Infinite;
byte[] requestData = Encoding.UTF8.GetBytes(batchRequestXml);
using (Stream reqStream = request.GetRequestStream())
{
reqStream.Write(requestData, 0, requestData.Length);
}
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
var responseContent = new StreamReader(response.GetResponseStream()).ReadToEnd();
// 檢查回應中是否有錯誤
if (responseContent.Contains("<err"))
{
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(responseContent);
// 找到所有的 err 節點
XmlNodeList errNodes = xmlDocument.GetElementsByTagName("err");
// 如果有錯誤節點,進行處理
foreach (XmlNode errNode in errNodes)
{
logger.LogWarning($"【ParkingJob】【停車場剩餘車位資訊】[錯誤內容]{errNode}");
}
}
else
{
logger.LogInformation("【ParkingJob】【停車場剩餘車位資訊】[批次請求成功]");
}
}
else
{
logger.LogWarning("【ParkingJob】【停車場剩餘車位資訊】[沒有需要發送的請求]");
}
} }
else else
{ {
logger.LogWarning("【ParkingJob】【停車場剩餘車位資訊】 - [查無資料]"); logger.LogWarning("【ParkingJob】【停車場剩餘車位資訊】 - [查無資料]");
} }
await task_Detail.InsertWorkTime_End("ParkingJob", "Parking", "執行成功停車場剩餘車位Job"); await task_Detail.InsertWorkTime_End("ParkingJob", "Parking", "執行成功停車場剩餘車位Job");
//logger.LogInformation("【ParkingJob】【執行成功停車場剩餘車位Job】"); //logger.LogInformation("【ParkingJob】【執行成功停車場剩餘車位Job】");
} }
@ -233,91 +235,92 @@ namespace BackendWorkerService.Quartz.Jobs
if (equipmentResponseResult != null && equipmentResponseResult.Code == "20000") if (equipmentResponseResult != null && equipmentResponseResult.Code == "20000")
{ {
List<string> batchRequests = new List<string>(); // 用來存儲批次請求的列表
foreach (var equipment in equipmentResponseResult.Payload) foreach (var equipment in equipmentResponseResult.Payload)
{ {
//找出對定的設備代碼 // 找出對應的設備代碼
var selectedMapping = parkingEquipmentMapping.Where(x => x.System_key == equipment.Id).FirstOrDefault(); var selectedMapping = parkingEquipmentMapping.FirstOrDefault(x => x.System_key == equipment.Id);
if (selectedMapping != null) if (selectedMapping != null)
{ {
item = equipment.Id; var name = equipment.Id; // 保存設備ID
var tagName = selectedMapping.system_value; var tagName = selectedMapping.system_value;
var apiFormat = @"{0}obix/config/Arena/{1}/{2}/{3}/{4}/{5}/ST/set"; var apiFormat = @"{0}obix/config/Arena/{1}/{2}/{3}/{4}/{5}/ST/set";
var tagNameSplit = tagName.Split("_"); var tagNameSplit = tagName.Split("_");
var parames = new List<object>(); var parames = new List<object> { parkingConfig.ApiBase };
parames.Add(parkingConfig.ApiBase); parames.AddRange(tagNameSplit.Take(tagNameSplit.Length - 1)); // tag 前 4段
for (var i = 0; i < tagNameSplit.Length; i++) parames.Add(tagName); // 最後一段 完整 tag
{
if (i != tagNameSplit.Length - 1)
{
parames.Add(tagNameSplit[i]);
}
else
{
parames.Add(tagName);
}
}
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(apiFormat, parames.ToArray())); string requestUri = string.Format(apiFormat, parames.ToArray());
request.Method = "POST";
request.Headers.Add("Authorization", "Basic " + encoded);
request.PreAuthenticate = true;
// 構建要發送的實體資料
var real = $@"<real val='{equipment.Alive.ToString().ToLower()}' />"; var real = $@"<real val='{equipment.Alive.ToString().ToLower()}' />";
byte[] realByteArray = Encoding.UTF8.GetBytes(real); batchRequests.Add($"<uri is='obix:Invoke' val='{requestUri}'>" +
using (Stream reqStream = request.GetRequestStream()) $"<real name='in' val='{equipment.Alive.ToString().ToLower()}' />" +
{ $"</uri>");
reqStream.Write(realByteArray, 0, realByteArray.Length);
}
HttpWebResponse response = (HttpWebResponse)request.GetResponse(); // 進行日誌記錄
var responseContent = new StreamReader(response.GetResponseStream()).ReadToEnd(); logger.LogInformation("【ParkingJob】【設備資訊】準備更新設備{0},設備狀態:{1}", name, equipment.Alive);
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(responseContent);
string json = JsonConvert.SerializeXmlNode(xmlDocument);
JObject jsonResult = (JObject)JsonConvert.DeserializeObject(json);
if (jsonResult.ContainsKey("err")) //抓取錯誤
{
logger.LogError("【ParkingJob】【設備資訊】");
logger.LogError("【ParkingJob】【設備資訊】[錯誤內容]{0}", json);
}
else
{
if (jsonResult.ContainsKey("bool")) //表示可以讀取到內容
{
List<Dictionary<string, object>> ontimeRawDatas = new List<Dictionary<string, object>>();
var realList = jsonResult["bool"];
var val = realList["@val"];
if (val != null)
{
var tempStrSplit = val.ToString();
if (tempStrSplit != equipment.Alive.ToString().ToLower())
{
logger.LogError("【ParkingJob】【設備資訊】[修改失敗]{0}", val.ToString());
}
else
{
logger.LogInformation("【ParkingJob】【設備資訊】[修改成功]{0}", val.ToString());
logger.LogInformation("【ParkingJob】【設備資訊】[設備資訊]{0}", item);
}
}
}
}
} }
else else
{ {
logger.LogWarning("【ParkingJob】【設備資訊】[查無該名稱對應表]{0}", equipment.Id); logger.LogWarning("【ParkingJob】【設備資訊】[查無該名稱對應表]{0}", equipment.Id);
} }
} }
if (batchRequests.Any())
{
// 建立批次請求 XML
var batchRequestXml = $@"<list is='obix:BatchIn'>
{string.Join("", batchRequests)}
</list>";
// 發送批次請求
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(parkingConfig.ApiBase + "obix/batch");
request.Method = "POST";
request.Headers.Add("Authorization", "Basic " + encoded);
request.PreAuthenticate = true;
byte[] requestData = Encoding.UTF8.GetBytes(batchRequestXml);
using (Stream reqStream = request.GetRequestStream())
{
reqStream.Write(requestData, 0, requestData.Length);
}
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
var responseContent = new StreamReader(response.GetResponseStream()).ReadToEnd();
// 檢查回應中是否有錯誤
if (responseContent.Contains("<err"))
{
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(responseContent);
// 找到所有的 err 節點
XmlNodeList errNodes = xmlDocument.GetElementsByTagName("err");
// 如果有錯誤節點,進行處理
foreach (XmlNode errNode in errNodes)
{
logger.LogWarning($"【ParkingJob】【設備資訊】[錯誤內容]{errNode}");
}
}
else
{
logger.LogInformation("【ParkingJob】【設備資訊】[批次請求成功]");
}
}
else
{
logger.LogWarning("【ParkingJob】【設備資訊】[沒有需要發送的請求]");
}
} }
else else
{ {
logger.LogWarning("【ParkingJob】【設備資訊】 - [查無資料]"); logger.LogWarning("【ParkingJob】【設備資訊】 - [查無資料]");
} }
await task_Detail.InsertWorkTime_End("ParkingJob", "Device", "執行成功設備資訊Job"); await task_Detail.InsertWorkTime_End("ParkingJob", "Device", "執行成功設備資訊Job");
//logger.LogInformation("【ParkingJob】【執行成功設備資訊Job】"); //logger.LogInformation("【ParkingJob】【執行成功設備資訊Job】");
} }

View File

@ -20,6 +20,7 @@ using BackendWorkerService.Services.Implement;
using RainApi; using RainApi;
using System.Globalization; using System.Globalization;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Xml.Linq;
namespace BackendWorkerService.Quartz.Jobs namespace BackendWorkerService.Quartz.Jobs
{ {
@ -128,7 +129,8 @@ namespace BackendWorkerService.Quartz.Jobs
HttpResponseMessage response = client.GetAsync(UVUri).Result; HttpResponseMessage response = client.GetAsync(UVUri).Result;
String jsonUVs = response.Content.ReadAsStringAsync().Result.ToString(); String jsonUVs = response.Content.ReadAsStringAsync().Result.ToString();
var observation = JsonConvert.DeserializeObject<Root>(jsonUVs); var observation = JsonConvert.DeserializeObject<Root3>(jsonUVs); // Use Root3 here
logger.LogInformation("【WeatherAPIJob】【取得成功氣象預報】"); logger.LogInformation("【WeatherAPIJob】【取得成功氣象預報】");
if (observation.Success != "true") if (observation.Success != "true")
@ -139,67 +141,35 @@ namespace BackendWorkerService.Quartz.Jobs
{ {
logger.LogInformation("【WeatherAPIJob】【開始存入氣象預報到資料庫】"); logger.LogInformation("【WeatherAPIJob】【開始存入氣象預報到資料庫】");
List<Dictionary<string, object>> WeatherAPIdbS = new List<Dictionary<string, object>>(); List<Dictionary<string, object>> WeatherAPIdbS = new List<Dictionary<string, object>>();
var Type_ALL = observation.Records.Locations[0].Location[0].WeatherElement; var Type_ALL = observation.Records.Locations[0].Location[1].WeatherElement; // Location[1]是信義區
foreach (var a in Type_ALL) foreach (var a in Type_ALL)
{ {
foreach (var b in a.Time) foreach (var b in a.Time)
{ {
if (a.ElementName == "PoP12h" || a.ElementName == "Wx")
{
if (Convert.ToDateTime(b.StartTime) > DateTime.Now.AddDays(1))
{
break;
}
}
else
{
if (Convert.ToDateTime(b.DataTime) > DateTime.Now.AddDays(1))
{
break;
}
}
Dictionary<string, object> WeatherAPIdb = new Dictionary<string, object>() Dictionary<string, object> WeatherAPIdb = new Dictionary<string, object>()
{
{ "@weather_type", a.ElementName},
{ "@data_no", DataNO},
{ "@get_value", b.ElementValue[0].Value},
{ "@measures", b.ElementValue[0].Measures},
{ "@created_by", "system"},
{ "@created_at", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")},
};
if (a.ElementName == "PoP12h" || a.ElementName == "Wx")
{
WeatherAPIdb.Add("@start_time", b.StartTime);
WeatherAPIdb.Add("@end_time", b.EndTime);
}
else
{
WeatherAPIdb.Add("@start_time", b.DataTime);
WeatherAPIdb.Add("@end_time", null);
}
WeatherAPIdbS.Add(WeatherAPIdb);
if (a.ElementName == "Wx")
{
Dictionary<string, object> TWeatherAPIdb = new Dictionary<string, object>()
{ {
{ "@weather_type", "WxV"}, { "@weather_type", a.ElementName},
{ "@data_no", DataNO}, { "@data_no", DataNO},
{ "@get_value", b.ElementValue[1].Value},
{ "@measures", b.ElementValue[1].Measures},
{ "@start_time", b.StartTime},
{ "@end_time", b.EndTime},
{ "@created_by", "system"}, { "@created_by", "system"},
{ "@created_at", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}, { "@created_at", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}
}; };
WeatherAPIdbS.Add(TWeatherAPIdb);
}
ProcessElementValue(a, b, WeatherAPIdb, WeatherAPIdbS);
} }
} }
await backendRepository.AddMutiByCustomTable(WeatherAPIdbS, "api_weateher");
string sql = @"
INSERT INTO api_weateher (weather_type, data_no, get_value, get_value2, created_by, created_at, start_time, end_time)
VALUES (@weather_type, @data_no, @get_value, @get_value2, @created_by, @created_at, @start_time, @end_time)
ON DUPLICATE KEY UPDATE
get_value = VALUES(get_value),
get_value2 = VALUES(get_value2);";
foreach (var item in WeatherAPIdbS)
{
await backendRepository.ExecuteSql(sql, item);
}
} }
await task_Detail.InsertWorkTime_End("WeatherAPI", "api_weateher"); await task_Detail.InsertWorkTime_End("WeatherAPI", "api_weateher");
@ -245,16 +215,16 @@ namespace BackendWorkerService.Quartz.Jobs
.FirstOrDefault(); .FirstOrDefault();
Dictionary<string, object> RainAPIdb = new Dictionary<string, object>() Dictionary<string, object> RainAPIdb = new Dictionary<string, object>()
{ {
{ "@msgType", observation.Alert.MsgType}, { "@msgType", observation.Alert.MsgType},
{ "@headline", taipeiInfo.Headline}, { "@headline", taipeiInfo.Headline},
{ "@severity_level", severity_level }, { "@severity_level", severity_level },
{ "@areaDesc", area}, { "@areaDesc", area},
{ "@onset", taipeiInfo.Onset.ToString("yyyy-MM-dd HH:mm:ss")}, { "@onset", taipeiInfo.Onset.ToString("yyyy-MM-dd HH:mm:ss")},
{ "@expires", taipeiInfo.Expires.ToString("yyyy-MM-dd HH:mm:ss")}, { "@expires", taipeiInfo.Expires.ToString("yyyy-MM-dd HH:mm:ss")},
{ "@created_by", "system"}, { "@created_by", "system"},
{ "@created_at", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}, { "@created_at", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")},
}; };
var id = await backendRepository.AddOneByCustomTableReturnId(RainAPIdb, "api_rain"); var id = await backendRepository.AddOneByCustomTableReturnId(RainAPIdb, "api_rain");
var val = RainValue(severity_level, taipeiInfo.Headline); var val = RainValue(severity_level, taipeiInfo.Headline);
@ -387,17 +357,17 @@ namespace BackendWorkerService.Quartz.Jobs
var area = taipeiInfo.Area.Where(a => a.AreaDesc == "臺北市").Select(x => x.AreaDesc).FirstOrDefault(); var area = taipeiInfo.Area.Where(a => a.AreaDesc == "臺北市").Select(x => x.AreaDesc).FirstOrDefault();
Dictionary<string, object> EarthquakeAPIdb = new Dictionary<string, object>() Dictionary<string, object> EarthquakeAPIdb = new Dictionary<string, object>()
{ {
{ "@msgType", observation.Alert.MsgType}, { "@msgType", observation.Alert.MsgType},
{ "@headline", taipeiInfo.Headline}, { "@headline", taipeiInfo.Headline},
{ "@areaDesc", area}, { "@areaDesc", area},
{ "@urgency",taipeiInfo.Urgency}, { "@urgency",taipeiInfo.Urgency},
{ "@severity",taipeiInfo.Severity}, { "@severity",taipeiInfo.Severity},
{ "@onset", taipeiInfo.Onset.ToString("yyyy-MM-dd HH:mm:ss")}, { "@onset", taipeiInfo.Onset.ToString("yyyy-MM-dd HH:mm:ss")},
{ "@expires", taipeiInfo.Expires.ToString("yyyy-MM-dd HH:mm:ss")}, { "@expires", taipeiInfo.Expires.ToString("yyyy-MM-dd HH:mm:ss")},
{ "@created_by", "system"}, { "@created_by", "system"},
{ "@created_at", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}, { "@created_at", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")},
}; };
var id = await backendRepository.AddOneByCustomTableReturnId(EarthquakeAPIdb, "api_typhoon"); var id = await backendRepository.AddOneByCustomTableReturnId(EarthquakeAPIdb, "api_typhoon");
if (taipeiInfo.Urgency != null && taipeiInfo.Urgency != "Expected") if (taipeiInfo.Urgency != null && taipeiInfo.Urgency != "Expected")
{ {
@ -531,16 +501,16 @@ namespace BackendWorkerService.Quartz.Jobs
if (NeedCallApi == 0) if (NeedCallApi == 0)
{ {
Dictionary<string, object> EarthquakeAPIdb = new Dictionary<string, object>() Dictionary<string, object> EarthquakeAPIdb = new Dictionary<string, object>()
{ {
{ "@earthquakeNo", eq.EarthquakeNo}, { "@earthquakeNo", eq.EarthquakeNo},
{ "@newEarthquake", 1}, { "@newEarthquake", 1},
{ "@originTime", DateTime.Parse(eq.EarthquakeInfo.OriginTime.ToString(), System.Globalization.CultureInfo.CurrentCulture)}, { "@originTime", DateTime.Parse(eq.EarthquakeInfo.OriginTime.ToString(), System.Globalization.CultureInfo.CurrentCulture)},
{ "@magnitudeValue", eq.EarthquakeInfo.EarthquakeMagnitude.MagnitudeValue}, { "@magnitudeValue", eq.EarthquakeInfo.EarthquakeMagnitude.MagnitudeValue},
{ "@areaName", exist.CountyName}, { "@areaName", exist.CountyName},
{ "@areaIntensity", exist.AreaIntensity}, { "@areaIntensity", exist.AreaIntensity},
{ "@created_by", "system"}, { "@created_by", "system"},
{ "@created_at", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}, { "@created_at", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")},
}; };
var Nag = Fetch_PostWithJSONFormat($@"{obixApiConfig.ApiBase}obix/config/Arena/D2/CWB/L110/CAP/D2_CWB_L110_CAP_GEO/ALMLEVL_LMI/set", Regex.Match(exist.AreaIntensity, @"\d+").ToString()); var Nag = Fetch_PostWithJSONFormat($@"{obixApiConfig.ApiBase}obix/config/Arena/D2/CWB/L110/CAP/D2_CWB_L110_CAP_GEO/ALMLEVL_LMI/set", Regex.Match(exist.AreaIntensity, @"\d+").ToString());
@ -660,44 +630,142 @@ namespace BackendWorkerService.Quartz.Jobs
await task_Detail.WorkFail("WeatherAPI", "api_earthquake", ex.Message.ToString()); await task_Detail.WorkFail("WeatherAPI", "api_earthquake", ex.Message.ToString());
} }
} }
// Niagara批次寫法
if (await task_Detail.GetNeedWorkTask("WeatherAPI", "set_weather")) if (await task_Detail.GetNeedWorkTask("WeatherAPI", "set_weather"))
{ {
try try
{ {
await task_Detail.InsertWorkTime("WeatherAPI", "set_weather"); await task_Detail.InsertWorkTime("WeatherAPI", "set_weather");
var sql = @$"SELECT var sql = @$"SELECT
id, id,
weather_type, weather_type,
get_value get_value
FROM api_weateher FROM api_weateher
where id in (select MAX(id) from api_weateher where start_time < NOW() group by weather_type) WHERE id IN (
order by start_time desc"; SELECT MAX(id)
FROM api_weateher
WHERE start_time < NOW()
GROUP BY weather_type
)
ORDER BY start_time DESC";
var types = await backendRepository.GetAllAsync<ShowWeather>(sql); var types = await backendRepository.GetAllAsync<ShowWeather>(sql);
var T = types.Where(a => a.weather_type == "T").FirstOrDefault(); var urlMapping = new Dictionary<string, string>
var RbT = Fetch_PostWithJSONFormat($@"{obixApiConfig.ApiBase}obix/config/Arena/D2/CWB/L110/OPD/D2_CWB_L110_OPD_element/T/set", T.get_value); {
UpdatedNiagara("api_weateher", RbT, T.id); { "溫度", "obix/config/Arena/D2/CWB/L110/OPD/D2_CWB_L110_OPD_element/T/set" },
{ "相對濕度", "obix/config/Arena/D2/CWB/L110/OPD/D2_CWB_L110_OPD_element/RH/set" },
{ "3小時降雨機率", "obix/config/Arena/D2/CWB/L110/OPD/D2_CWB_L110_OPD_element/PoP6h/set" },
{ "天氣現象", "obix/config/Arena/D2/CWB/L110/OPD/D2_CWB_L110_OPD_element/Wx/set" }
};
var RH = types.Where(a => a.weather_type == "RH").FirstOrDefault(); // 建立批次請求
var RHT = Fetch_PostWithJSONFormat($@"{obixApiConfig.ApiBase}obix/config/Arena/D2/CWB/L110/OPD/D2_CWB_L110_OPD_element/RH/set", RH.get_value); var batchRequests = types
UpdatedNiagara("api_weateher", RHT, RH.id); .Where(t => urlMapping.ContainsKey(t.weather_type))
.Select(t => (
url: obixApiConfig.ApiBase + urlMapping[t.weather_type],
value: t.get_value
))
.ToList();
// 呼叫批次請求
var batchResponse = await ProcessBatchRequestAsync(obixApiConfig, batchRequests);
// 解析回應 XML
var batchResponseXml = XDocument.Parse(batchResponse);
var listElement = batchResponseXml.Descendants().FirstOrDefault(e => e.Name.LocalName == "list");
if (listElement == null)
{
throw new Exception("Batch response XML does not contain a 'list' element.");
}
var childElements = listElement.Elements().ToList();
// 使用 urlMapping 的值反向找對應的 weather_type
for (int i = 0; i < childElements.Count; i++)
{
var tag = childElements[i].Name.LocalName;
var responseStr = childElements[i].ToString();
var currentRequest = batchRequests[i];
// 找到當前 URL 在 urlMapping 中的 key (weather_type)
var weatherTypeKey = urlMapping.FirstOrDefault(x => currentRequest.url.Contains(x.Value)).Key;
if (weatherTypeKey != null)
{
var weatherType = types.FirstOrDefault(t => t.weather_type == weatherTypeKey);
if (weatherType != null)
{
if (tag == "err")
{
UpdatedNiagara("api_weateher", responseStr, weatherType.id);
}
else
{
UpdatedNiagara("api_weateher", "success", weatherType.id);
}
}
else
{
logger.LogWarning($"No matching weather type found for key: {weatherTypeKey}");
}
}
else
{
logger.LogWarning($"No matching URL found for request: {currentRequest.url}");
}
}
var PoP12h = types.Where(a => a.weather_type == "PoP12h").FirstOrDefault();
var PoP12hT = Fetch_PostWithJSONFormat($@"{obixApiConfig.ApiBase}obix/config/Arena/D2/CWB/L110/OPD/D2_CWB_L110_OPD_element/PoP6h/set", PoP12h.get_value);
UpdatedNiagara("api_weateher", PoP12hT, PoP12h.id);
var Wx = types.Where(a => a.weather_type == "Wx").FirstOrDefault();
var WxT = Fetch_PostWithJSONFormat($@"{obixApiConfig.ApiBase}obix/config/Arena/D2/CWB/L110/OPD/D2_CWB_L110_OPD_element/Wx/set", Wx.get_value);
UpdatedNiagara("api_weateher", WxT, Wx.id);
await task_Detail.InsertWorkTime_End("WeatherAPI", "set_weather"); await task_Detail.InsertWorkTime_End("WeatherAPI", "set_weather");
logger.LogInformation($"set niagara weather value success"); logger.LogInformation($"Set Niagara weather value success");
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.LogInformation($"set niagara weather value fail"); logger.LogError($"Set Niagara weather value fail: {ex.Message}");
await task_Detail.WorkFail("WeatherAPI", "set_weather", ex.Message.ToString()); await task_Detail.WorkFail("WeatherAPI", "set_weather", ex.Message);
} }
} }
// Niagara單次寫法
//if (await task_Detail.GetNeedWorkTask("WeatherAPI", "set_weather"))
//{
// try
// {
// await task_Detail.InsertWorkTime("WeatherAPI", "set_weather");
// var sql = @$"SELECT
// id,
// weather_type,
// get_value
// FROM api_weateher
// where id in (select MAX(id) from api_weateher where start_time < NOW() group by weather_type)
// order by start_time desc";
// var types = await backendRepository.GetAllAsync<ShowWeather>(sql);
// var T = types.Where(a => a.weather_type == "T").FirstOrDefault();
// var RbT = Fetch_PostWithJSONFormat($@"{obixApiConfig.ApiBase}obix/config/Arena/D2/CWB/L110/OPD/D2_CWB_L110_OPD_element/T/set", T.get_value);
// UpdatedNiagara("api_weateher", RbT, T.id);
// var RH = types.Where(a => a.weather_type == "RH").FirstOrDefault();
// var RHT = Fetch_PostWithJSONFormat($@"{obixApiConfig.ApiBase}obix/config/Arena/D2/CWB/L110/OPD/D2_CWB_L110_OPD_element/RH/set", RH.get_value);
// UpdatedNiagara("api_weateher", RHT, RH.id);
// var PoP12h = types.Where(a => a.weather_type == "PoP12h").FirstOrDefault();
// var PoP12hT = Fetch_PostWithJSONFormat($@"{obixApiConfig.ApiBase}obix/config/Arena/D2/CWB/L110/OPD/D2_CWB_L110_OPD_element/PoP6h/set", PoP12h.get_value);
// UpdatedNiagara("api_weateher", PoP12hT, PoP12h.id);
// var Wx = types.Where(a => a.weather_type == "Wx").FirstOrDefault();
// var WxT = Fetch_PostWithJSONFormat($@"{obixApiConfig.ApiBase}obix/config/Arena/D2/CWB/L110/OPD/D2_CWB_L110_OPD_element/Wx/set", Wx.get_value);
// UpdatedNiagara("api_weateher", WxT, Wx.id);
// await task_Detail.InsertWorkTime_End("WeatherAPI", "set_weather");
// logger.LogInformation($"set niagara weather value success");
// }
// catch (Exception ex)
// {
// logger.LogInformation($"set niagara weather value fail");
// await task_Detail.WorkFail("WeatherAPI", "set_weather", ex.Message.ToString());
// }
//}
} }
catch (Exception exception) catch (Exception exception)
{ {
@ -737,6 +805,125 @@ namespace BackendWorkerService.Quartz.Jobs
return rint; return rint;
} }
public async Task<string> ProcessBatchRequestAsync(ObixApiConfig obixApiConfig, List<(string url, string value)> batchRequests)
{
try
{
var batchRequestData = new StringBuilder();
string authInfo = Convert.ToBase64String(Encoding.Default.GetBytes($"{obixApiConfig.UserName}:{obixApiConfig.Password}"));
// 組合批次請求的資料
foreach (var request in batchRequests)
{
var jsonData = $"<real name='in' val=\"{request.value}\" />";
batchRequestData.AppendLine($"<uri is='obix:Invoke' val='{request.url}'>" + jsonData + "</uri>");
}
// 最終的批次請求資料
var batchRequestXml = $@"<list is='obix:BatchIn'>{batchRequestData}</list>";
// 建立 HTTP 請求
var requestBacth = (HttpWebRequest)WebRequest.Create($"{obixApiConfig.ApiBase}obix/batch");
requestBacth.Method = "POST";
requestBacth.Headers.Add("Authorization", "Basic " + authInfo);
// 寫入請求資料
using (var streamWriter = new StreamWriter(requestBacth.GetRequestStream()))
{
streamWriter.Write(batchRequestXml);
}
// 獲取回應
using (var response = (HttpWebResponse)await requestBacth.GetResponseAsync())
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
return await streamReader.ReadToEndAsync();
}
}
catch (Exception)
{
return "BatchRequestg失敗";
}
}
//Helper Method to process Element Values
private void ProcessElementValue(WeatherElement3 a, Time b, Dictionary<string, object> WeatherAPIdb, List<Dictionary<string, object>> WeatherAPIdbS)
{
//Common parameters for element value
if (a.ElementName.Contains(ElementName..ToString()) || a.ElementName.Contains(ElementName..ToString()) || a.ElementName.Contains(ElementName..ToString()))
{
if (Convert.ToDateTime(b.StartTime) > DateTime.Now.AddDays(1))
{
return; // Skip to next element
}
WeatherAPIdb.Add("@start_time", Convert.ToDateTime(b.StartTime).ToString("yyyy-MM-dd HH:mm:ss"));
WeatherAPIdb.Add("@end_time", Convert.ToDateTime(b.EndTime).ToString("yyyy-MM-dd HH:mm:ss"));
}
else
{
if (Convert.ToDateTime(b.DataTime) > DateTime.Now.AddDays(1))
{
return; // Skip to next element
}
WeatherAPIdb.Add("@start_time", Convert.ToDateTime(b.DataTime).ToString("yyyy-MM-dd HH:mm:ss"));
WeatherAPIdb.Add("@end_time", null);
}
//Switch statement to handle different element names and their values
switch (a.ElementName)
{
case "溫度":
WeatherAPIdb.Add("@get_value", b.ElementValue[0].Temperature);
WeatherAPIdbS.Add(new Dictionary<string, object>(WeatherAPIdb));
break;
//case "露點溫度":
// WeatherAPIdb.Add("@get_value", b.ElementValue[0].DewPoint);
// WeatherAPIdbS.Add(new Dictionary<string, object>(WeatherAPIdb));
// break;
case "相對濕度":
WeatherAPIdb.Add("@get_value", b.ElementValue[0].RelativeHumidity);
WeatherAPIdbS.Add(new Dictionary<string, object>(WeatherAPIdb));
break;
//case "體感溫度":
// WeatherAPIdb.Add("@get_value", b.ElementValue[0].ApparentTemperature);
// WeatherAPIdbS.Add(new Dictionary<string, object>(WeatherAPIdb));
// break;
//case "舒適度指數":
// WeatherAPIdb.Add("@get_value", b.ElementValue[0].ComfortIndex);
// WeatherAPIdb.Add("@get_value2", b.ElementValue[0].ComfortIndexDescription);
// WeatherAPIdbS.Add(new Dictionary<string, object>(WeatherAPIdb));
// break;
//case "風速":
// WeatherAPIdb.Add("@get_value", b.ElementValue[0].WindSpeed);
// WeatherAPIdb.Add("@get_value2", b.ElementValue[0].BeaufortScale);
// WeatherAPIdbS.Add(new Dictionary<string, object>(WeatherAPIdb));
// break;
//case "風向":
// WeatherAPIdb.Add("@get_value", b.ElementValue[0].WindDirection);
// WeatherAPIdbS.Add(new Dictionary<string, object>(WeatherAPIdb));
// break;
case "3小時降雨機率":
WeatherAPIdb.Add("@get_value", b.ElementValue[0].ProbabilityOfPrecipitation);
WeatherAPIdbS.Add(new Dictionary<string, object>(WeatherAPIdb));
break;
case "天氣現象":
WeatherAPIdb.Add("@get_value", b.ElementValue[0].Weather);
WeatherAPIdb.Add("@get_value2", b.ElementValue[0].WeatherCode);
WeatherAPIdbS.Add(new Dictionary<string, object>(WeatherAPIdb));
break;
//case "天氣預報綜合描述":
// WeatherAPIdb.Add("@get_value", b.ElementValue[0].WeatherDescription);
// WeatherAPIdbS.Add(new Dictionary<string, object>(WeatherAPIdb));
// break;
}
}
public async void UpdatedNiagara(string DBTableName, string ResponseStr, int CheckNumId) public async void UpdatedNiagara(string DBTableName, string ResponseStr, int CheckNumId)
{ {
try try

View File

@ -47,27 +47,27 @@ namespace FrontendWebApi.ApiControllers
( (
SELECT w.get_value SELECT w.get_value
FROM api_weateher w FROM api_weateher w
WHERE w.weather_type = 'Wx' AND @DateNow BETWEEN w.start_time AND w.end_time ORDER BY created_at DESC, w.id desc WHERE w.weather_type = '' AND @DateNow BETWEEN w.start_time AND w.end_time ORDER BY created_at DESC, w.id desc
limit 1 limit 1
) AS WxText, ) AS WxText,
( (
SELECT wd.WeatherKey SELECT wd.WeatherKey
FROM api_weateher w FROM api_weateher w
LEFT JOIN weather_description wd ON w.get_value = wd.WeatherValue LEFT JOIN weather_description wd ON w.get_value2 = wd.WeatherValue
WHERE w.weather_type = 'WxV' AND @DateNow BETWEEN w.start_time AND w.end_time ORDER BY created_at DESC, w.id desc WHERE w.weather_type = '' AND @DateNow BETWEEN w.start_time AND w.end_time ORDER BY created_at DESC, w.id desc
limit 1 limit 1
) AS Wx, ) AS Wx,
( (
SELECT w.get_value SELECT w.get_value
FROM api_weateher w FROM api_weateher w
WHERE w.weather_type = 'T' AND @DateNow >= w.start_time WHERE w.weather_type = '' AND @DateNow BETWEEN w.start_time AND DATE_ADD(w.start_time, INTERVAL 1 HOUR)
order by created_at desc, id desc order by created_at desc, id desc
limit 1 limit 1
) AS Temp, ) AS Temp,
( (
SELECT w.get_value SELECT w.get_value
FROM api_weateher w FROM api_weateher w
WHERE w.weather_type = 'RH' AND @DateNow >= w.start_time WHERE w.weather_type = '' AND @DateNow BETWEEN w.start_time AND DATE_ADD(w.start_time, INTERVAL 1 HOUR)
order by created_at desc, id desc order by created_at desc, id desc
limit 1 limit 1
) AS RH ) AS RH

View File

@ -637,7 +637,7 @@ namespace FrontendWebApi.ApiControllers
and a.device_building_tag COLLATE utf8mb4_unicode_ci = b.device_building_tag and a.device_building_tag COLLATE utf8mb4_unicode_ci = b.device_building_tag
and a.device_name_tag COLLATE utf8mb4_unicode_ci = b.device_name_tag and a.device_name_tag COLLATE utf8mb4_unicode_ci = b.device_name_tag
join building c on c.building_tag = b.device_building_tag join building c on c.building_tag = b.device_building_tag
join import_niagara_item_history h on b.device_number = h.device_number and a.points = h.device_point_name -- join import_niagara_item_history h on b.device_number = h.device_number and a.points = h.device_point_name
join variable v1 on v1.system_value = b.device_system_tag and v1.deleted = 0 and v1.system_type = 'device_system_category_layer2' join variable v1 on v1.system_value = b.device_system_tag and v1.deleted = 0 and v1.system_type = 'device_system_category_layer2'
join variable v2 on v2.system_value = b.device_name_tag and v2.deleted = 0 and v2.system_type = 'device_system_category_layer3' join variable v2 on v2.system_value = b.device_name_tag and v2.deleted = 0 and v2.system_type = 'device_system_category_layer3'
where a.deleted = 0 and b.deleted = 0 and v2.deleted = 0 and v1.deleted = 0 where a.deleted = 0 and b.deleted = 0 and v2.deleted = 0 and v1.deleted = 0

View File

@ -24,6 +24,8 @@ using System.Threading.Tasks;
using Image = System.Drawing.Image; using Image = System.Drawing.Image;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Net; using System.Net;
using NPOI.POIFS.Crypt.Dsig;
using System.Text;
namespace FrontendWebApi.ApiControllers namespace FrontendWebApi.ApiControllers
{ {
@ -32,230 +34,386 @@ namespace FrontendWebApi.ApiControllers
public class OperationLogController : MyBaseApiController<OperationLogController> public class OperationLogController : MyBaseApiController<OperationLogController>
{ {
private readonly IBackendRepository backendRepository; private readonly IBackendRepository backendRepository;
private readonly IBackgroundServiceMsSqlRepository backgroundServiceMsSqlRepository;
private string operationFileSaveAsPath = ""; private string operationFileSaveAsPath = "";
public OperationLogController(IBackendRepository backendRepository) public OperationLogController(IBackendRepository backendRepository, IBackgroundServiceMsSqlRepository backgroundServiceMsSqlRepository)
{ {
this.backendRepository = backendRepository; this.backendRepository = backendRepository;
operationFileSaveAsPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "upload", "operation"); operationFileSaveAsPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "upload", "operation");
this.backgroundServiceMsSqlRepository = backgroundServiceMsSqlRepository;
} }
/// <summary>
/// 巨蛋操作紀錄api 1:系統紀錄;2:登入紀錄;3:燈控排程紀錄
/// </summary>
/// <param name="pageResult"></param>
/// <returns></returns>
[HttpPost] [HttpPost]
[Route("api/OperationLog/GetList")] [Route("api/OperationLog/GetLogList")]
public async Task<ActionResult<ApiResult<List<OperationLogOutput>>>> GetList([FromBody] PageResult<OperationLogInput> pageResult) public async Task<ApiResult<PageResult<List<OperationLogOutput>>>> GetLogList([FromBody] PageResult<OperationLogInput> pageResult)
{ {
ApiResult<List<OperationLogOutput>> apiResult = new ApiResult<List<OperationLogOutput>>(jwt_str); ApiResult<PageResult<List<OperationLogOutput>>> apiResult = new ApiResult<PageResult<List<OperationLogOutput>>>();
if (!jwtlife) List<OperationLogOutput> logList = new List<OperationLogOutput>();
{ int total = 0;
apiResult.Code = "5000"; string start_time = DateTime.Parse(pageResult.data?.start_time).ToString("yyyy-MM-dd");
return BadRequest(apiResult); string end_time = DateTime.Parse(pageResult.data?.end_time).ToString("yyyy-MM-dd"); ;
} string mark = pageResult.isEnable ? ";" : "\n";
try try
{ {
string pageQuery = pageResult.isEnable ? " LIMIT @pageSize OFFSET @skip" : ""; if (pageResult.data.type == 1 || pageResult.data.type == 2)
// 取得資料 {
var logList = await backendRepository.GetAllAsync<OperationLogOutput>($@" string building_tag = pageResult.data?.building_tag;
select ui.full_name as 'user_name' ,ol.* from operation_log ol string tableName = (building_tag == "D2" || building_tag == "D3") ? "D2_Pri" : $"{building_tag}";
LEFT JOIN userinfo ui on ui.userinfo_guid COLLATE utf8mb4_unicode_ci = ol.user_guid string sWhere = pageResult.data.type == 1 ? "OPERATION ='Invoked'" : "(OPERATION = 'Login' or OPERATION = 'Logout (Timeout)')";
WHERE ol.operation_type = @operation_type AND ol.building_tag = @building_tag AND string sWhere2 = pageResult.data.type == 1 ? ((building_tag == "D2" || building_tag == "D3") ?
ol.created_at >= @start_time AND ol.created_at <= @end_time "and (TARGET like '/Arena/D2%' or target like '/Arena/D3%')" : $"and TARGET like '/Arena/{building_tag}%'") : "";
{pageQuery}", string pageQuery = pageResult.isEnable ? "" : @"ORDER BY [TIMESTAMP] DESC -- 必須提供 ORDER BY 子句
new { OFFSET @skip ROWS
pageSize = pageResult.pageSize , FETCH NEXT @pageSize ROWS ONLY;";
skip = (pageResult.currentPage - 1) * pageResult.pageSize,
operation_type = pageResult.data?.operation_type,
building_tag = pageResult.data?.building_tag,
start_time = pageResult.data?.start_time,
end_time = pageResult.data?.end_time,
});
// 設定呈現紀錄內容 string sql = $@"SELECT DISTINCT
foreach (var log in logList) { [TIMESTAMP] created_at
if (log.parameter == null) { ,[OPERATION] action_name
continue; ,[TARGET] parameter
} ,[SLOTNAME]
switch (log.operation_type) { ,[OLDVALUE]
case 1: ,[VALUE]
var chaName = JsonConvert.DeserializeObject<ChangeName>(log.parameter); ,[USERNAME] user_name
if (chaName == null) continue; FROM [taipei_dome].[dbo].[ARENA_{tableName}_AUDITHISTORY]
log.content = chaName.TagName + "" + chaName.ChangeN; WHERE {sWhere}
break; AND TIMESTAMP >= @start_time AND TIMESTAMP < DATEADD(day, 1, @end_time)
case 2: {sWhere2}
var schedule = JsonConvert.DeserializeObject<SaveSchedule>(log.parameter); {pageQuery}";
if (schedule == null) continue; // 取得資料
var contentArr = new List<string>() { logList = await backgroundServiceMsSqlRepository.GetAllAsync<OperationLogOutput>(sql,
new
{
pageSize = pageResult.pageSize,
skip = (pageResult.currentPage - 1) * pageResult.pageSize,
operation_type = pageResult.data?.operation_type,
start_time = start_time,
end_time = end_time,
});
sql = @$"SELECT COUNT(*) AS TotalCount
FROM (
SELECT DISTINCT
[TIMESTAMP] created_at,
[OPERATION] action_name,
[TARGET] parameter,
[SLOTNAME],
[OLDVALUE],
[VALUE],
[USERNAME] user_name
FROM [taipei_dome].[dbo].[ARENA_{tableName}_AUDITHISTORY]
WHERE {sWhere}
AND TIMESTAMP >= @start_time AND TIMESTAMP < DATEADD(day, 1, @end_time)
{sWhere2}
) AS subquery;";
total = await backgroundServiceMsSqlRepository.GetOneAsync<int>(sql,
new
{
start_time = start_time,
end_time = end_time,
});
logList.ForEach(log => log.content = $"Target{log.parameter}{mark}Value{log.value}");
}
else if (pageResult.data.type == 3)
{
string pageQuery = pageResult.isEnable ? "" : "LIMIT @pageSize OFFSET @skip";
var sql = $@"SELECT ui.full_name AS user_name, ol.*
FROM operation_log ol
LEFT JOIN userinfo ui ON ui.userinfo_guid COLLATE utf8mb4_unicode_ci = ol.user_guid
WHERE ol.operation_type = @operation_type
AND ol.building_tag = @building_tag
AND ol.created_at >= @start_time
AND ol.created_at <= @end_time
{pageQuery}";
// 取得資料
logList = await backendRepository.GetAllAsync<OperationLogOutput>(sql,
new
{
pageSize = pageResult.pageSize,
skip = (pageResult.currentPage - 1) * pageResult.pageSize,
operation_type = pageResult.data?.operation_type,
building_tag = pageResult.data?.building_tag,
start_time = start_time,
end_time = end_time,
});
sql = $@"SELECT COUNT(*) AS total_count
FROM (
SELECT ui.full_name AS user_name, ol.*
FROM operation_log ol
LEFT JOIN userinfo ui ON ui.userinfo_guid COLLATE utf8mb4_unicode_ci = ol.user_guid
WHERE ol.operation_type = @operation_type
AND ol.building_tag = @building_tag
AND ol.created_at >= @start_time
AND ol.created_at <= @end_time
) AS subquery;";
total = await backendRepository.GetOneAsync<int>(sql,
new
{
operation_type = pageResult.data?.operation_type,
building_tag = pageResult.data?.building_tag,
start_time = start_time,
end_time = end_time,
});
// 設定呈現紀錄內容
foreach (var log in logList)
{
if (log.parameter == null)
{
continue;
}
switch (log.operation_type)
{
case 1:
var chaName = JsonConvert.DeserializeObject<ChangeName>(log.parameter);
if (chaName == null) continue;
log.content = chaName.TagName + "" + chaName.ChangeN;
break;
case 2:
var schedule = JsonConvert.DeserializeObject<SaveSchedule>(log.parameter);
if (schedule == null) continue;
var contentArr = new List<string>() {
"編號:" + schedule.light_schedule_guid, "編號:" + schedule.light_schedule_guid,
"名稱:" + schedule.full_name, "名稱:" + schedule.full_name,
}; };
if (log.action_name == "修改") { if (log.action_name == "修改")
contentArr.Add("修改內容:" + string.Join("、", schedule.changeNames)); {
} contentArr.Add("修改內容:" + string.Join("、", schedule.changeNames));
log.content = string.Join("\n",contentArr); }
break; log.content = string.Join(mark, contentArr);
break;
}
} }
} }
else
{
apiResult.Code = "5000";
apiResult.Msg = "無效參數";
return apiResult;
}
var result = new PageResult<List<OperationLogOutput>>
{
pageSize = pageResult.pageSize,
totalItem = total,
currentPage = pageResult.currentPage,
data = logList.OrderByDescending(log => log.created_at).ToList()
};
apiResult.Code = "0000"; apiResult.Code = "0000";
apiResult.Data = logList; apiResult.Data = result;
} }
catch (Exception exception) catch (Exception exception)
{ {
apiResult.Code = "9999"; apiResult.Code = "9999";
apiResult.Msg = "系統內部錯誤,請聯絡管理者。";
Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message); Logger.LogError("【" + controllerName + "/" + actionName + "】" + exception.Message);
return Ok(apiResult); return apiResult;
} }
return Ok(apiResult); return apiResult;
} }
[HttpPost] [HttpPost]
[Route("api/OperationLog/ExportList")] [Route("api/OperationLog/ExportList")]
public async Task<FileResult> ExportList([FromBody] OperationLogExportInput input) public async Task<IActionResult> ExportList([FromBody] OperationLogExportInput input)
{ {
List<OperationLogOutput> result = new List<OperationLogOutput>(); List<OperationLogOutput> result = new List<OperationLogOutput>();
if (input.isNiagara) //if (input.isNiagara)
//{
// result = input.exportList;
//}
//else
//{
// PageResult<OperationLogInput> pageResult = input.listInput;
// pageResult.isEnable = false;
// result = ((ApiResult<List<OperationLogOutput>>)((OkObjectResult)(await this.GetList(pageResult)).Result).Value).Data.ToList();
//}
input.listInput.isEnable = true;
result = GetLogList(input.listInput).Result.Data.data;
var FileName = $"{input.exportOpeTypeName}_{DateTime.Now}.csv";
// 生成CSV文件
string Csv;
string formatted_created_at = null;
if (result.Count > 0)
{ {
result = input.exportList; StringBuilder csv = new StringBuilder();
// 添加CSV標題行
csv.AppendLine("操作人,動作,內容,紀錄時間");
// 添加數據行
foreach (var item in result)
{
if (item.created_at.HasValue)
{
formatted_created_at = item.created_at.Value.ToString("yyyy/MM/dd HH:mm:ss");
}
csv.AppendLine($"{item.user_name},{item.action_name},{item.content},{formatted_created_at}");
}
Csv = csv.ToString();
using (var fileMemoryStream = new MemoryStream())
{
using (var streamWriter = new StreamWriter(fileMemoryStream, Encoding.UTF8))
{
streamWriter.Write(Csv);
}
return File(fileMemoryStream.ToArray(), "text/csv", FileName);
}
} }
else else
{ {
PageResult<OperationLogInput> pageResult = input.listInput; var msg = new { Code = "0003", Msg = "無資料可匯出。" };
pageResult.isEnable = false; return StatusCode(400, msg);
result = ((ApiResult<List<OperationLogOutput>>)((OkObjectResult)(await this.GetList(pageResult)).Result).Value).Data.ToList();
} }
var workbook = new XSSFWorkbook(); #region excel寫法 1,048,576
#region excel設定 #region excel設定
IFont font12 = workbook.CreateFont(); //var workbook = new XSSFWorkbook();
font12.FontName = "新細明體"; //IFont font12 = workbook.CreateFont();
font12.FontHeightInPoints = 12; //font12.FontName = "新細明體";
ICellStyle style12 = workbook.CreateCellStyle(); //font12.FontHeightInPoints = 12;
style12.SetFont(font12); //ICellStyle style12 = workbook.CreateCellStyle();
style12.Alignment = HorizontalAlignment.Center; //style12.SetFont(font12);
style12.VerticalAlignment = VerticalAlignment.Center; //style12.Alignment = HorizontalAlignment.Center;
IFont font12Times = workbook.CreateFont(); //style12.VerticalAlignment = VerticalAlignment.Center;
font12Times.FontName = "Times New Roman"; //IFont font12Times = workbook.CreateFont();
font12Times.FontHeightInPoints = 12; //font12Times.FontName = "Times New Roman";
IFont font18 = workbook.CreateFont(); //font12Times.FontHeightInPoints = 12;
font18.FontName = "新細明體"; //IFont font18 = workbook.CreateFont();
font18.FontHeightInPoints = 18; //font18.FontName = "新細明體";
font18.IsBold = true; //font18.FontHeightInPoints = 18;
ICellStyle styleTitle18 = workbook.CreateCellStyle(); //font18.IsBold = true;
styleTitle18.SetFont(font18); //ICellStyle styleTitle18 = workbook.CreateCellStyle();
styleTitle18.Alignment = HorizontalAlignment.Center; //styleTitle18.SetFont(font18);
styleTitle18.VerticalAlignment = VerticalAlignment.Center; //styleTitle18.Alignment = HorizontalAlignment.Center;
ICellStyle styleLeft12 = workbook.CreateCellStyle(); //styleTitle18.VerticalAlignment = VerticalAlignment.Center;
styleLeft12.SetFont(font12); //ICellStyle styleLeft12 = workbook.CreateCellStyle();
styleLeft12.Alignment = HorizontalAlignment.Left; //styleLeft12.SetFont(font12);
styleLeft12.VerticalAlignment = VerticalAlignment.Center; //styleLeft12.Alignment = HorizontalAlignment.Left;
ICellStyle styleLine12 = workbook.CreateCellStyle(); //styleLeft12.VerticalAlignment = VerticalAlignment.Center;
styleLine12.SetFont(font12); //ICellStyle styleLine12 = workbook.CreateCellStyle();
styleLine12.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center; //styleLine12.SetFont(font12);
styleLine12.VerticalAlignment = VerticalAlignment.Center; //styleLine12.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Center;
styleLine12.BorderTop = NPOI.SS.UserModel.BorderStyle.Thin; //styleLine12.VerticalAlignment = VerticalAlignment.Center;
styleLine12.BorderBottom = NPOI.SS.UserModel.BorderStyle.Thin; //styleLine12.BorderTop = NPOI.SS.UserModel.BorderStyle.Thin;
styleLine12.BorderRight = NPOI.SS.UserModel.BorderStyle.Thin; //styleLine12.BorderBottom = NPOI.SS.UserModel.BorderStyle.Thin;
styleLine12.BorderLeft = NPOI.SS.UserModel.BorderStyle.Thin; //styleLine12.BorderRight = NPOI.SS.UserModel.BorderStyle.Thin;
ICellStyle stylein12 = workbook.CreateCellStyle(); //styleLine12.BorderLeft = NPOI.SS.UserModel.BorderStyle.Thin;
stylein12.SetFont(font12Times); //ICellStyle stylein12 = workbook.CreateCellStyle();
stylein12.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Left; //stylein12.SetFont(font12Times);
stylein12.VerticalAlignment = VerticalAlignment.Center; //stylein12.Alignment = NPOI.SS.UserModel.HorizontalAlignment.Left;
stylein12.BorderTop = NPOI.SS.UserModel.BorderStyle.Thin; //stylein12.VerticalAlignment = VerticalAlignment.Center;
stylein12.BorderBottom = NPOI.SS.UserModel.BorderStyle.Thin; //stylein12.BorderTop = NPOI.SS.UserModel.BorderStyle.Thin;
stylein12.BorderRight = NPOI.SS.UserModel.BorderStyle.Thin; //stylein12.BorderBottom = NPOI.SS.UserModel.BorderStyle.Thin;
stylein12.BorderLeft = NPOI.SS.UserModel.BorderStyle.Thin; //stylein12.BorderRight = NPOI.SS.UserModel.BorderStyle.Thin;
stylein12.WrapText = true; //stylein12.BorderLeft = NPOI.SS.UserModel.BorderStyle.Thin;
//stylein12.WrapText = true;
#endregion #endregion
var sheet = workbook.CreateSheet(input.exportOpeTypeName); //var sheet = workbook.CreateSheet(input.exportOpeTypeName);
int RowPosition = 0; //int RowPosition = 0;
if (result.Count > 0) //if (result.Count > 0)
{ //{
#region set cell // #region set cell
int ri = 0; // int ri = 0;
IRow row = sheet.CreateRow(RowPosition); // IRow row = sheet.CreateRow(RowPosition);
if (!input.isNiagara) { // //if (!input.isNiagara)
sheet.SetColumnWidth(ri++, 1 * 160 * 12); // //{
} // // sheet.SetColumnWidth(ri++, 1 * 160 * 12);
sheet.SetColumnWidth(ri++, 2 * 160 * 12); // //}
sheet.SetColumnWidth(ri++, 2 * 160 * 12); // sheet.SetColumnWidth(ri++, 2 * 160 * 12);
sheet.SetColumnWidth(ri++, 8 * 160 * 12); // sheet.SetColumnWidth(ri++, 2 * 160 * 12);
sheet.SetColumnWidth(ri++, 4 * 160 * 12); // sheet.SetColumnWidth(ri++, 8 * 160 * 12);
// sheet.SetColumnWidth(ri++, 4 * 160 * 12);
int i = 0; // int i = 0;
ICell cell = row.CreateCell(i++); // ICell cell = row.CreateCell(i++);
if (!input.isNiagara) // //if (!input.isNiagara)
{ // //{
cell.SetCellValue("編號"); // // cell.SetCellValue("編號");
cell.CellStyle = styleLine12; // // cell.CellStyle = styleLine12;
cell = row.CreateCell(i++); // // cell = row.CreateCell(i++);
} // //}
cell.SetCellValue("操作人"); // cell.SetCellValue("操作人");
cell.CellStyle = styleLine12; // cell.CellStyle = styleLine12;
cell = row.CreateCell(i++); // cell = row.CreateCell(i++);
cell.SetCellValue("動作"); // cell.SetCellValue("動作");
cell.CellStyle = styleLine12; // cell.CellStyle = styleLine12;
cell = row.CreateCell(i++); // cell = row.CreateCell(i++);
cell.SetCellValue("內容"); // cell.SetCellValue("內容");
cell.CellStyle = styleLine12; // cell.CellStyle = styleLine12;
cell = row.CreateCell(i++); // cell = row.CreateCell(i++);
cell.SetCellValue("紀錄時間"); // cell.SetCellValue("紀錄時間");
cell.CellStyle = styleLine12; // cell.CellStyle = styleLine12;
#endregion // #endregion
foreach (var r in result) // foreach (var r in result)
{ // {
RowPosition += 1; // RowPosition += 1;
int k = 3; // int k = 3;
row = sheet.CreateRow(RowPosition); // row = sheet.CreateRow(RowPosition);
for (int j = 0; j <= i; j++) // for (int j = 0; j <= i; j++)
{ // {
int s = 0; // int s = 0;
cell = row.CreateCell(j); // cell = row.CreateCell(j);
if (!input.isNiagara && j == s++) // //if (!input.isNiagara && j == s++)
{ // //{
cell.SetCellValue(r.id); // // cell.SetCellValue(r.id);
} // //}
if (j == s++) // if (j == s++)
{ // {
cell.SetCellValue(r.user_name); // cell.SetCellValue(r.user_name);
} // }
if (j == s++) // if (j == s++)
{ // {
cell.SetCellValue(r.action_name); // cell.SetCellValue(r.action_name);
} // }
if (j == s++) // if (j == s++)
{ // {
cell.SetCellValue(r.content); // cell.SetCellValue(r.content);
} // }
if (j == s++)
{
cell.SetCellValue(r.created_at != null ? ((DateTime)r.created_at).ToString("yyyy-MM-dd HH:mm:ss") : null);
}
cell.CellStyle = style12; // if (j == s++)
} // {
} // cell.SetCellValue(r.created_at != null ? ((DateTime)r.created_at).ToString("yyyy-MM-dd HH:mm:ss") : null);
} // }
var ms = new NpoiMemoryStream // cell.CellStyle = style12;
{ // }
AllowClose = false // }
}; //}
workbook.Write(ms);
ms.Flush();
ms.Seek(0, SeekOrigin.Begin);
Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition"); //var ms = new NpoiMemoryStream
//{
// AllowClose = false
//};
//workbook.Write(ms);
//ms.Flush();
//ms.Seek(0, SeekOrigin.Begin);
//Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
//return File(ms, "application/vnd.ms-excel", $"操作紀錄_{input.exportOpeTypeName}.xlsx");
#endregion
return File(ms, "application/vnd.ms-excel", $"操作紀錄_{input.exportOpeTypeName}.xlsx");
} }
} }
} }

View File

@ -751,7 +751,6 @@ namespace FrontendWebApi.ApiControllers
var fileDateName = DateTime.Now.ToString("yyyy-MM-ddTHH-mm-ss"); var fileDateName = DateTime.Now.ToString("yyyy-MM-ddTHH-mm-ss");
var waterMeterFileName = $"水電報表_水錶_{fileDateName}.csv"; var waterMeterFileName = $"水電報表_水錶_{fileDateName}.csv";
var electricMeterFileName = $"水電報表_電錶_{fileDateName}.csv"; var electricMeterFileName = $"水電報表_電錶_{fileDateName}.csv";
var zipFileName = $"水電報表_{fileDateName}.zip";
try try
{ {
@ -790,7 +789,7 @@ namespace FrontendWebApi.ApiControllers
// 判斷是否有資料 // 判斷是否有資料
if (existMonth.Count == 0) if (existMonth.Count == 0)
{ {
var msg = new { Code = "0001", Msg = "還沒有選擇用戶,無法匯出檔案。" }; var msg = new { Code = "0001", Msg = "時間段無資料或無設置用戶。" };
return StatusCode(400, msg); return StatusCode(400, msg);
} }
else else
@ -862,12 +861,6 @@ namespace FrontendWebApi.ApiControllers
var waterMeterData = outputBillExcel.Where(x => x.device_name_tag == "W1" && x.building_tag == tb.building_tag).ToList(); var waterMeterData = outputBillExcel.Where(x => x.device_name_tag == "W1" && x.building_tag == tb.building_tag).ToList();
var electricMeterData = outputBillExcel.Where(x => x.device_name_tag == "E4" && x.building_tag == tb.building_tag).ToList(); var electricMeterData = outputBillExcel.Where(x => x.device_name_tag == "E4" && x.building_tag == tb.building_tag).ToList();
// 檢查是否有水錶或電錶數據
//if (waterMeterData.Count == 0 && electricMeterData.Count == 0)
//{
// var msg = new { Code = "0002", Msg = "該棟沒有可匯出的水錶或電錶數據。" };
// return StatusCode(400, msg);
//}
if (tb.tableType == "elec" && electricMeterData.Count == 0) if (tb.tableType == "elec" && electricMeterData.Count == 0)
{ {
@ -885,11 +878,11 @@ namespace FrontendWebApi.ApiControllers
string electricMeterCsv = null; string electricMeterCsv = null;
if (waterMeterData.Count > 0) if (waterMeterData.Count > 0)
{ {
waterMeterCsv = GenerateCsv(waterMeterData); waterMeterCsv = GenerateCsv(waterMeterData, "water");
} }
if (electricMeterData.Count > 0) if (electricMeterData.Count > 0)
{ {
electricMeterCsv = GenerateCsv(electricMeterData); electricMeterCsv = GenerateCsv(electricMeterData, "elec");
} }
// 返回CSV文件 // 返回CSV文件
@ -921,37 +914,6 @@ namespace FrontendWebApi.ApiControllers
return StatusCode(400, msg); return StatusCode(400, msg);
} }
//// 創建ZIP檔案
//using (var zipMemoryStream = new MemoryStream())
//{
// using (var archive = new ZipArchive(zipMemoryStream, ZipArchiveMode.Create, true))
// {
// if (waterMeterData.Count > 0)
// {
// var waterEntry = archive.CreateEntry(waterMeterFileName);
// using (var entryStream = waterEntry.Open())
// using (var streamWriter = new StreamWriter(entryStream, Encoding.UTF8))
// {
// streamWriter.Write(waterMeterCsv);
// }
// }
// if (electricMeterData.Count > 0)
// {
// var electricEntry = archive.CreateEntry(electricMeterFileName);
// using (var entryStream = electricEntry.Open())
// using (var streamWriter = new StreamWriter(entryStream, Encoding.UTF8))
// {
// streamWriter.Write(electricMeterCsv);
// }
// }
// }
// zipMemoryStream.Seek(0, SeekOrigin.Begin);
// return File(zipMemoryStream.ToArray(), "application/zip", zipFileName);
//}
} }
catch (Exception exception) catch (Exception exception)
{ {
@ -963,12 +925,19 @@ namespace FrontendWebApi.ApiControllers
} }
private string GenerateCsv(List<OutputBillExcel> data) private string GenerateCsv(List<OutputBillExcel> data, string type)
{ {
StringBuilder csv = new StringBuilder(); StringBuilder csv = new StringBuilder();
// 添加CSV標題行 // 添加CSV標題行
csv.AppendLine("用戶,設備代碼,設備名稱,日期,用電單價(元/度),當日用電(kWh),電費(元),起訖時間"); if (type == "elec")
{
csv.AppendLine("用戶,設備代碼,設備名稱,日期,用電單價(元/度),當日用電(kWh),電費(元),起訖時間");
}
else if (type == "water")
{
csv.AppendLine("用戶,設備代碼,設備名稱,日期,用水單價(元/度),當日用水(m³),水費(元),起訖時間");
}
// 添加數據行 // 添加數據行
foreach (var item in data) foreach (var item in data)

View File

@ -9,8 +9,8 @@ namespace FrontendWebApi.Models
{ {
public string WxText { get; set; } public string WxText { get; set; }
public string Wx { get; set; } public string Wx { get; set; }
public int Temp { get; set; } public int? Temp { get; set; }
public int RH { get; set; } public int? RH { get; set; }
} }
public class DefaultBuilding public class DefaultBuilding

View File

@ -168,6 +168,7 @@ namespace FrontendWebApi.Models
public string end_time { get; set; } public string end_time { get; set; }
public string building_tag{ get; set; } public string building_tag{ get; set; }
public short operation_type { get; set; } public short operation_type { get; set; }
public int type { get; set; } = 1; // log選項 1:系統紀錄;2:登入紀錄;3:燈控排程紀錄
} }
public class OperationLogExportInput { public class OperationLogExportInput {
public bool isNiagara { get; set; } public bool isNiagara { get; set; }

View File

@ -286,7 +286,7 @@ namespace FrontendWebApi.Models
public class BasePageResult { public class BasePageResult {
public int? pageSize { get; set; } = 10; public int? pageSize { get; set; } = 10;
public int? totalItem { get; set; } = 0;
public int? totalPage { get; set; } = 0; public int? totalPage { get; set; } = 0;
public int? currentPage { get; set; } = 0; public int? currentPage { get; set; } = 0;
} }
@ -295,6 +295,6 @@ namespace FrontendWebApi.Models
{ {
public T data { get; set; } public T data { get; set; }
public bool isEnable { get; set; } public bool isEnable { get; set; }
} }
} }

View File

@ -67,7 +67,7 @@ namespace Repository.BackendRepository.Implement
`device_full_name` varchar(100) DEFAULT NULL, `device_full_name` varchar(100) DEFAULT NULL,
`is_used` smallint(1) DEFAULT 0, `is_used` smallint(1) DEFAULT 0,
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;"; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;";
using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{ {
await conn.ExecuteAsync(sql); await conn.ExecuteAsync(sql);
@ -90,11 +90,11 @@ namespace Repository.BackendRepository.Implement
{ {
tag_name = x.Key.tag_name2, tag_name = x.Key.tag_name2,
displayName = x.Key.displayName2 displayName = x.Key.displayName2
}); });
// StringBuilder sb = new StringBuilder(); // StringBuilder sb = new StringBuilder();
bool isDome = false; //是否為巨蛋案 bool isDome = false; //是否為巨蛋案
int count = 0; int count = 0;
stopwatchSection = new Stopwatch(); stopwatchSection = new Stopwatch();
stopwatchSection.Start(); stopwatchSection.Start();
foreach (var row in ds2) foreach (var row in ds2)
@ -125,7 +125,7 @@ namespace Repository.BackendRepository.Implement
{ {
if (arrTag.Length == 5) if (arrTag.Length == 5)
{ {
isDome= true; isDome = true;
sb.Append($@" insert into import_niagara_tag(niagara_tags, device_area_tag, device_building_tag, device_system_tag, sb.Append($@" insert into import_niagara_tag(niagara_tags, device_area_tag, device_building_tag, device_system_tag,
device_name_tag, device_floor_tag, device_master_tag, device_last_name_tag, device_serial_tag, device_model_tag, device_full_name, atDateTime) values('" + device_name_tag, device_floor_tag, device_master_tag, device_last_name_tag, device_serial_tag, device_model_tag, device_full_name, atDateTime) values('" +
row.tag_name + "', '" + //niagara_tags row.tag_name + "', '" + //niagara_tags
@ -209,7 +209,7 @@ namespace Repository.BackendRepository.Implement
} }
} }
} }
/// <summary> /// <summary>
/// 獲取照明開關 是否在 device_node 層 /// 獲取照明開關 是否在 device_node 層
/// </summary> /// </summary>
@ -220,9 +220,9 @@ namespace Repository.BackendRepository.Implement
stopwatch.Start(); stopwatch.Start();
using (IDbConnection conn = GetDbConnection()) using (IDbConnection conn = GetDbConnection())
{ {
conn.Open(); conn.Open();
var sql = "select system_value from variable where system_type = 'module_light_switch' "; var sql = "select system_value from variable where system_type = 'module_light_switch' ";
var module_light_switch = conn.QueryAsync<string>(sql).Result.FirstOrDefault(); var module_light_switch = conn.QueryAsync<string>(sql).Result.FirstOrDefault();
conn.Close(); conn.Close();
stopwatch.Stop(); stopwatch.Stop();
await KeepTimeLog("getLightSwitchLevel", stopwatch.ElapsedMilliseconds); await KeepTimeLog("getLightSwitchLevel", stopwatch.ElapsedMilliseconds);
@ -250,7 +250,7 @@ namespace Repository.BackendRepository.Implement
{ {
stopwatchSection = new Stopwatch(); stopwatchSection = new Stopwatch();
stopwatchSection.Start(); stopwatchSection.Start();
foreach(var b in building) foreach (var b in building)
{ {
string sql = @"CREATE TABLE IF NOT EXISTS `import_niagara_item` ( string sql = @"CREATE TABLE IF NOT EXISTS `import_niagara_item` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
@ -263,7 +263,7 @@ namespace Repository.BackendRepository.Implement
`parent_path` varchar(50) DEFAULT NULL, `parent_path` varchar(50) DEFAULT NULL,
`full_name` varchar(50) DEFAULT NULL, `full_name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;"; ) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;";
using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{ {
await conn.ExecuteAsync(sql); await conn.ExecuteAsync(sql);
@ -281,7 +281,8 @@ namespace Repository.BackendRepository.Implement
stopwatchSection = new Stopwatch(); stopwatchSection = new Stopwatch();
stopwatchSection.Start(); stopwatchSection.Start();
ds = ds.GroupBy(x => new { ds = ds.GroupBy(x => new
{
device_area_tag = x.device_area_tag, device_area_tag = x.device_area_tag,
device_building_tag = x.device_building_tag, device_building_tag = x.device_building_tag,
device_system_tag = x.device_system_tag, device_system_tag = x.device_system_tag,
@ -304,7 +305,7 @@ namespace Repository.BackendRepository.Implement
foreach (var row in ds) foreach (var row in ds)
{ {
sb.Append($@" insert import_niagara_item(device_area_tag, device_building_tag, device_system_tag, device_name_tag, device_point_name, parent_path, full_name, is_history) sb.Append($@" insert import_niagara_item(device_area_tag, device_building_tag, device_system_tag, device_name_tag, device_point_name, parent_path, full_name, is_history)
values('"+ values('" +
row.device_area_tag + "', '" + row.device_area_tag + "', '" +
row.device_building_tag + "', '" + row.device_building_tag + "', '" +
row.device_system_tag + "', '" + row.device_system_tag + "', '" +
@ -446,7 +447,7 @@ namespace Repository.BackendRepository.Implement
{ {
conn.Close(); conn.Close();
} }
} }
} }
@ -456,377 +457,404 @@ namespace Repository.BackendRepository.Implement
/// <returns></returns> /// <returns></returns>
public async Task DeviceComparison(string LightSwitchLevel) public async Task DeviceComparison(string LightSwitchLevel)
{ {
stopwatch = new Stopwatch(); Stopwatch stopwatch = Stopwatch.StartNew();
stopwatch.Start();
using (IDbConnection conn = GetDbConnection()) using (IDbConnection conn = GetDbConnection())
{ {
conn.Open(); conn.Open();
try try
{ {
List<NiagaraTags> result; // 1. 取得需要新增的 NiagaraTags
StringBuilder sb = new StringBuilder(); Stopwatch sw1 = Stopwatch.StartNew();
StringBuilder sb2 = new StringBuilder(); string sqlGetNiagaraTags = @"
stopwatchSection = new Stopwatch(); SELECT m.*
stopwatchSection.Start(); FROM import_niagara_tag m
sb.Append($@"SELECT m.* WHERE NOT EXISTS (
FROM import_niagara_tag m SELECT 1
WHERE NOT EXISTS ( FROM device d
SELECT 1 WHERE m.niagara_tags = d.device_number
FROM device d );";
WHERE m.niagara_tags = d.device_number List<NiagaraTags> niagaraTagsToAdd = (await conn.QueryAsync<NiagaraTags>(sqlGetNiagaraTags)).ToList();
);"); sw1.Stop();
result = (await conn.QueryAsync<NiagaraTags>(sb.ToString())).ToList<NiagaraTags>(); await KeepTimeLog("filter device item from import_niagara_tag", sw1.ElapsedMilliseconds);
sb.Clear();
stopwatchSection.Stop();
await KeepTimeLog("filter device item from import_niagara_tag", stopwatchSection.ElapsedMilliseconds);
#region device, device_kind // 2. 新增 device 和 device_kind
stopwatchSection = new Stopwatch(); if (niagaraTagsToAdd.Count > 0)
stopwatchSection.Start();
int count = 0;
//新增至device, is_link = 1
if (result.Count > 0)
{ {
var result2 = result.GroupBy(x => new // 2.1. 新增 device
Stopwatch sw2 = Stopwatch.StartNew();
List<string> sqlInsertDeviceList = new List<string>();
foreach (var data in niagaraTagsToAdd)
{ {
device_building_tag2 = x.device_building_tag,
device_system_tag2 = x.device_system_tag,
device_name_tag2 = x.device_name_tag
}).Select(x => new Device_item8
{
device_building_tag = x.Key.device_building_tag2,
device_system_tag = x.Key.device_system_tag2,
device_name_tag = x.Key.device_name_tag2
});
foreach (var data in result)
{
//開關控制在個別燈具(device_node層) and 小類為二線式照明系統 and tag第八段開頭不是 G
if (LightSwitchLevel == "node" && data.device_name_tag == "L1" && data.device_serial_tag.Substring(0, 1) != "G") if (LightSwitchLevel == "node" && data.device_name_tag == "L1" && data.device_serial_tag.Substring(0, 1) != "G")
{ {
// 燈具新增 device_node // 燈具新增 device_node
continue; continue;
} }
else {
sb.Append($@" insert device(device_guid, deleted, status, priority, is_link, device_area_tag, string sqlInsertDevice = @"
device_building_tag, device_system_tag, device_name_tag, full_name, device_floor_tag, device_master, INSERT INTO device (device_guid, deleted, status, priority, is_link, device_area_tag,
device_last_name, device_serial_tag, device_model_tag, device_number, device_system_category_layer3, visible, created_at, updated_at) device_building_tag, device_system_tag, device_name_tag, full_name, device_floor_tag, device_master,
select uuid(), 0, 1, 0, 1, '" + device_last_name, device_serial_tag, device_model_tag, device_number, device_system_category_layer3, visible, created_at, updated_at)
data.device_area_tag + "', '" + select uuid(), 0, 1, 0, 1, @device_area_tag, @device_building_tag, @device_system_tag, @device_name_tag,
data.device_building_tag + "', '" + @full_name, @device_floor_tag, @device_master_tag, @device_last_name_tag, @device_serial_tag,
data.device_system_tag + "', '" + @device_model_tag, @niagara_tags, @device_system_tag, 1, now(), now() WHERE NOT EXISTS (SELECT 1 FROM device WHERE device_number = @niagara_tags); ";
data.device_name_tag + "', '" +
data.device_full_name + "', '" + sqlInsertDeviceList.Add(sqlInsertDevice); // 先暫存SQL語法等等一次執行
data.device_floor_tag + "', '" +
data.device_master_tag + "', '" +
data.device_last_name_tag + "', '" +
data.device_serial_tag + "', '" +
data.device_model_tag + "', '" +
data.niagara_tags + "', '" +
data.device_system_tag + "', 1, now(), now() " +
$@"WHERE NOT EXISTS ( SELECT 1 FROM device WHERE device_number = '{data.niagara_tags}'); ");
count += 1;
if (count >= 100)
{
using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
{
await conn.ExecuteAsync(sb.ToString());
}
sb.Clear();
count = 0;
}
}
} }
if (sb.Length > 0) // 2.1.1 分批執行新增 device
int batchSize = 100;
for (int i = 0; i < sqlInsertDeviceList.Count; i += batchSize)
{ {
//建立參數
var batch = niagaraTagsToAdd.Skip(i).Take(batchSize).ToList();
//建立參數化查詢要用的物件
List<object> parameters = new List<object>();
foreach (var data in batch)
{
parameters.Add(new
{
device_area_tag = data.device_area_tag,
device_building_tag = data.device_building_tag,
device_system_tag = data.device_system_tag,
device_name_tag = data.device_name_tag,
full_name = data.device_full_name,
device_floor_tag = data.device_floor_tag,
device_master_tag = data.device_master_tag,
device_last_name_tag = data.device_last_name_tag,
device_serial_tag = data.device_serial_tag,
device_model_tag = data.device_model_tag,
niagara_tags = data.niagara_tags
});
}
//建立多條Insert語法可以搭配Dapper的參數化查詢
string insertSql = string.Join(" ", sqlInsertDeviceList.Take(batchSize));
using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled)) using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
{ {
await conn.ExecuteAsync(sb.ToString()); await conn.ExecuteAsync(insertSql, parameters); // 使用參數化查詢
} scope.Complete();
sb.Clear();
}
stopwatchSection.Stop();
await KeepTimeLog("insert into device", stopwatchSection.ElapsedMilliseconds);
stopwatchSection = new Stopwatch();
stopwatchSection.Start();
foreach (var data in result2)
{
var sqlString = new StringBuilder();
sqlString.Append("select * from device_kind where device_building_tag = '" + data.device_building_tag + "' and device_system_tag = '" + data.device_system_tag + "' and device_name_tag = '" + data.device_name_tag + "'");
var dk = (await conn.QueryAsync<device_kind>(sqlString.ToString())).ToList<device_kind>();
if (dk.Count == 0)
{
sb2.Append($@"INSERT device_kind (device_kind_guid, device_building_tag, device_system_tag, device_name_tag,
device_normal_flashing, device_close_flashing, device_error_flashing, device_error_independent,
created_by, created_at, is_link)
VALUES (uuid(), '" + data.device_building_tag + "', '" + data.device_system_tag + "', '" + data.device_name_tag +
"', 0, 0, 1, 0, 'B43E3CA7-96DD-4FC7-B6E6-974ACC3B0878', now(), 1);");
count += 1;
if (count >= 100)
{
using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
{
await conn.ExecuteAsync(sb2.ToString());
}
sb2.Clear();
count = 0;
}
} }
} }
if (sb2.Length > 0) sw2.Stop();
await KeepTimeLog("insert into device", sw2.ElapsedMilliseconds);
// 2.2. 新增 device_kind
Stopwatch sw3 = Stopwatch.StartNew();
// 建立 device_kind 時需要用到的資料結構
var deviceKindData = niagaraTagsToAdd.GroupBy(x => new
{ {
using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled)) x.device_building_tag,
{ x.device_system_tag,
await conn.ExecuteAsync(sb2.ToString()); x.device_name_tag
} }).Select(g => new
sb2.Clear();
}
stopwatchSection.Stop();
await KeepTimeLog("insert into device_kind", stopwatchSection.ElapsedMilliseconds);
}
#endregion
#region device_kind process for cctv
stopwatchSection = new Stopwatch();
stopwatchSection.Start();
using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
{
await conn.ExecuteAsync(@"SET SQL_SAFE_UPDATES = 0;
update device d join import_niagara_tag n on d.device_number = n.niagara_tags set d.device_model_tag = n.device_model_tag
where d.device_system_tag = 'S' and d.device_name_tag = 'C' and (d.device_model_tag is null or d.device_model_tag = '');");
}
stopwatchSection.Stop();
await KeepTimeLog("update cctv model is null or empty", stopwatchSection.ElapsedMilliseconds);
stopwatchSection = new Stopwatch();
stopwatchSection.Start();
var resultDeviceItem = await conn.QueryAsync<NiagaraTags>($@"select * from import_niagara_tag where device_system_tag = 'S' and device_name_tag = 'C';");
var result3 = resultDeviceItem.GroupBy(x => new
{
device_building_tag2 = x.device_building_tag,
device_system_tag2 = x.device_system_tag,
device_name_tag2 = x.device_name_tag,
device_model_tag2 = x.device_model_tag
}).Select(x => new Device_item8
{
device_building_tag = x.Key.device_building_tag2,
device_system_tag = x.Key.device_system_tag2,
device_name_tag = x.Key.device_name_tag2,
device_model_tag = x.Key.device_model_tag2
});
foreach (var data in result3)
{
var sqlString = new StringBuilder();
sqlString.Append("select * from device_kind where device_building_tag = '" + data.device_building_tag + "' and device_system_tag = '" + data.device_system_tag + "' and device_name_tag = '" + data.device_name_tag + "' and device_model_tag = '" + data.device_model_tag + "'");
var dk = (await conn.QueryAsync<device_kind>(sqlString.ToString())).ToList<device_kind>();
if (dk.Count == 0)
{ {
sb2.Append($@"INSERT device_kind (device_kind_guid, device_building_tag, device_system_tag, device_name_tag, g.Key.device_building_tag,
device_normal_flashing, device_close_flashing, device_error_flashing, device_error_independent, g.Key.device_system_tag,
created_by, created_at, device_model_tag, is_link) g.Key.device_name_tag
VALUES (uuid(), '" + data.device_building_tag + "', '" + data.device_system_tag + "', '" + data.device_name_tag + }).ToList();
"', 0, 0, 1, 0, 'B43E3CA7-96DD-4FC7-B6E6-974ACC3B0878', now(), '" + data.device_model_tag + "', 1);");
count += 1; foreach (var data in deviceKindData)
if (count >= 100) {
// 檢查 device_kind 是否已存在 (改成使用 EXISTS 子查詢)
string sqlCheckDeviceKind = @"
SELECT EXISTS (
SELECT 1
FROM device_kind
WHERE device_building_tag = @device_building_tag
AND device_system_tag = @device_system_tag
AND device_name_tag = @device_name_tag
);";
var parameters = new { device_building_tag = data.device_building_tag, device_system_tag = data.device_system_tag, device_name_tag = data.device_name_tag };
bool deviceKindExists = await conn.ExecuteScalarAsync<bool>(sqlCheckDeviceKind, parameters); // 使用參數化查詢
if (!deviceKindExists)
{ {
// 新增 device_kind
string sqlInsertDeviceKind = @"
INSERT INTO device_kind (device_kind_guid, device_building_tag, device_system_tag, device_name_tag,
device_normal_flashing, device_close_flashing, device_error_flashing, device_error_independent,
created_by, created_at, is_link)
VALUES (uuid(), @device_building_tag, @device_system_tag, @device_name_tag,
0, 0, 1, 0, 'B43E3CA7-96DD-4FC7-B6E6-974ACC3B0878', now(), 1);";
var parameters2 = new { device_building_tag = data.device_building_tag, device_system_tag = data.device_system_tag, device_name_tag = data.device_name_tag };
using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled)) using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
{ {
await conn.ExecuteAsync(sb2.ToString()); await conn.ExecuteAsync(sqlInsertDeviceKind, parameters2); // 使用參數化查詢
scope.Complete();
} }
sb2.Clear(); }
count = 0; }
sw3.Stop();
await KeepTimeLog("insert into device_kind", sw3.ElapsedMilliseconds);
}
// 3. device_kind process for cctv
Stopwatch sw4 = Stopwatch.StartNew();
// 3.1 更新 device 的 model tag
string sqlUpdateCCTVModel = @"
UPDATE device d
JOIN import_niagara_tag n ON d.device_number = n.niagara_tags
SET d.device_model_tag = n.device_model_tag
WHERE d.device_system_tag = 'S'
AND d.device_name_tag = 'C'
AND (d.device_model_tag IS NULL OR d.device_model_tag = '');";
using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
{
await conn.ExecuteAsync(sqlUpdateCCTVModel); // 使用參數化查詢
scope.Complete();
}
sw4.Stop();
await KeepTimeLog("update cctv model is null or empty", sw4.ElapsedMilliseconds);
// 3.2 取得 CCTV 相關資料,並新增 device_kind (邏輯同 2.2,但需要額外考量 device_model_tag)
Stopwatch sw5 = Stopwatch.StartNew();
string sqlGetCCTVData = @"SELECT * FROM import_niagara_tag WHERE device_system_tag = 'S' AND device_name_tag = 'C';";
var cctvData = await conn.QueryAsync<NiagaraTags>(sqlGetCCTVData);
// 建立 device_kind 時需要用到的資料結構
var cctvKindData = cctvData.GroupBy(x => new
{
x.device_building_tag,
x.device_system_tag,
x.device_name_tag,
x.device_model_tag
}).Select(g => new
{
g.Key.device_building_tag,
g.Key.device_system_tag,
g.Key.device_name_tag,
g.Key.device_model_tag
}).ToList();
foreach (var data in cctvKindData)
{
// 檢查 device_kind 是否已存在 (改成使用 EXISTS 子查詢)
string sqlCheckDeviceKind = @"
SELECT EXISTS (
SELECT 1
FROM device_kind
WHERE device_building_tag = @device_building_tag
AND device_system_tag = @device_system_tag
AND device_name_tag = @device_name_tag
AND device_model_tag = @device_model_tag
);";
var parameters = new { device_building_tag = data.device_building_tag, device_system_tag = data.device_system_tag, device_name_tag = data.device_name_tag, device_model_tag = data.device_model_tag };
bool deviceKindExists = await conn.ExecuteScalarAsync<bool>(sqlCheckDeviceKind, parameters);
if (!deviceKindExists)
{
// 新增 device_kind
string sqlInsertDeviceKind = @"
INSERT INTO device_kind (device_kind_guid, device_building_tag, device_system_tag, device_name_tag,
device_normal_flashing, device_close_flashing, device_error_flashing, device_error_independent,
created_by, created_at, device_model_tag, is_link)
VALUES (uuid(), @device_building_tag, @device_system_tag, @device_name_tag,
0, 0, 1, 0, 'B43E3CA7-96DD-4FC7-B6E6-974ACC3B0878', now(), @device_model_tag, 1);";
var parameters2 = new { device_building_tag = data.device_building_tag, device_system_tag = data.device_system_tag, device_name_tag = data.device_name_tag, device_model_tag = data.device_model_tag };
using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
{
await conn.ExecuteAsync(sqlInsertDeviceKind, parameters2); // 使用參數化查詢
scope.Complete();
} }
} }
} }
if (sb2.Length > 0) sw5.Stop();
{ await KeepTimeLog("insert cctv device_kind", sw5.ElapsedMilliseconds);
using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
{
await conn.ExecuteAsync(sb2.ToString());
}
sb2.Clear(); // 3.3 更新 device_kind 的 is_link (邏輯複雜需要仔細確認SQL語法)
} Stopwatch sw6 = Stopwatch.StartNew();
// 先更新 device_kind 的 is_link 為 0
string sqlUpdateDeviceKindIsLinkToZero = @"
UPDATE device_kind
SET is_link = 0
WHERE device_system_tag = 'S'
AND device_name_tag = 'C'
AND NOT EXISTS (
SELECT 1
FROM import_niagara_tag
WHERE device_system_tag = device_kind.device_system_tag
AND device_name_tag = device_kind.device_name_tag
AND device_model_tag = device_kind.device_model_tag
);";
using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled)) using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
{ {
await conn.ExecuteAsync($@"SET SQL_SAFE_UPDATES = 0; await conn.ExecuteAsync(sqlUpdateDeviceKindIsLinkToZero); // 使用參數化查詢
UPDATE device_kind dk scope.Complete();
left JOIN (
SELECT device_system_tag, device_name_tag, device_model_tag
FROM import_niagara_tag
LIMIT 100000
) i ON dk.device_system_tag = i.device_system_tag
and dk.device_name_tag = i.device_name_tag
and dk.device_model_tag = i.device_model_tag
SET dk.is_link = 0
WHERE dk.is_link = 1 and dk.device_system_tag = 'S' and dk.device_name_tag = 'C' and i.device_system_tag is null;");
} }
// 再更新 device_kind 的 is_link 為 1
string sqlUpdateDeviceKindIsLinkToOne = @"
UPDATE device_kind
SET is_link = 1
WHERE device_system_tag = 'S'
AND device_name_tag = 'C'
AND EXISTS (
SELECT 1
FROM import_niagara_tag
WHERE device_system_tag = device_kind.device_system_tag
AND device_name_tag = device_kind.device_name_tag
AND device_model_tag = device_kind.device_model_tag
);";
using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled)) using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
{ {
await conn.ExecuteAsync($@"SET SQL_SAFE_UPDATES = 0; await conn.ExecuteAsync(sqlUpdateDeviceKindIsLinkToOne); // 使用參數化查詢
UPDATE device_kind dk scope.Complete();
left JOIN (
SELECT device_system_tag, device_name_tag, device_model_tag
FROM import_niagara_tag
LIMIT 100000
) i ON dk.device_system_tag = i.device_system_tag
and dk.device_name_tag = i.device_name_tag
and dk.device_model_tag = i.device_model_tag
SET dk.is_link = 1
WHERE dk.is_link = 0 and dk.device_system_tag = 'S' and dk.device_name_tag = 'C' and i.device_system_tag is not null;");
} }
stopwatchSection.Stop();
await KeepTimeLog("insert/update cctv model is_link", stopwatchSection.ElapsedMilliseconds);
#endregion
#region device_node sw6.Stop();
stopwatchSection = new Stopwatch(); await KeepTimeLog("insert/update cctv model is_link", sw6.ElapsedMilliseconds);
stopwatchSection.Start();
sb.Append($@" SELECT m.*
FROM import_niagara_tag m
WHERE NOT EXISTS (
SELECT 1
FROM device_node d
WHERE m.niagara_tags COLLATE utf8mb4_0900_ai_ci = d.device_number COLLATE utf8mb4_0900_ai_ci
);");
result = (await conn.QueryAsync<NiagaraTags>(sb.ToString())).ToList<NiagaraTags>();
sb.Clear();
if (result.Count > 0) // 4. device_node 新增 (邏輯同 2.1,需要調整 SQL 語法和參數)
Stopwatch sw7 = Stopwatch.StartNew();
string sqlGetNiagaraTagsForNode = @"
SELECT m.*
FROM import_niagara_tag m
WHERE NOT EXISTS (
SELECT 1
FROM device_node d
WHERE m.niagara_tags = d.device_number
);";
List<NiagaraTags> niagaraTagsToAddForNode = (await conn.QueryAsync<NiagaraTags>(sqlGetNiagaraTagsForNode)).ToList();
if (niagaraTagsToAddForNode.Count > 0)
{ {
var result2 = result.GroupBy(x => new // 4.1. 新增 device_node
List<string> sqlInsertDeviceNodeList = new List<string>();
foreach (var data in niagaraTagsToAddForNode)
{ {
device_building_tag2 = x.device_building_tag,
device_system_tag2 = x.device_system_tag,
device_name_tag2 = x.device_name_tag
}).Select(x => new Device_item8
{
device_building_tag = x.Key.device_building_tag2,
device_system_tag = x.Key.device_system_tag2,
device_name_tag = x.Key.device_name_tag2
});
count = 0;
foreach (var data in result)
{
//開關控制在個別燈具(device_node層) and 小類為二線式照明系統 and tag第八段開頭不是 G
if (LightSwitchLevel == "node" && data.device_name_tag == "L1" && data.device_serial_tag.Substring(0, 1) != "G") if (LightSwitchLevel == "node" && data.device_name_tag == "L1" && data.device_serial_tag.Substring(0, 1) != "G")
{ {
// 燈具新增 device_node // 燈具新增 device_node
sb.Append($@"INSERT INTO device_node(device_node_guid, deleted, device_guid, device_number, full_name, created_by, created_at, updated_at) string sqlInsertDeviceNode = @"
VALUES (uuid(), 0, '' " + //device_guid 父層需要 forge 那邊提供 INSERT INTO device_node (device_node_guid, deleted, device_guid, device_number, full_name, created_by, created_at, updated_at)
",'" + data.niagara_tags + // device_number VALUES (uuid(), 0, '', @device_number, @full_name, 'B43E3CA7-96DD-4FC7-B6E6-974ACC3B0878', now(), now());";
"', '" + data.device_full_name + //full_name sqlInsertDeviceNodeList.Add(sqlInsertDeviceNode);
"','B43E3CA7-96DD-4FC7-B6E6-974ACC3B0878', now(), now());");
count += 1;
if (count >= 100)
{
using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
{
await conn.ExecuteAsync(sb.ToString());
}
sb.Clear();
count = 0;
}
} }
} }
//4.1.1 分批執行新增device node
if (sb.Length > 0) int batchSizeNode = 100;
for (int i = 0; i < sqlInsertDeviceNodeList.Count; i += batchSizeNode)
{ {
//建立參數
var batch = niagaraTagsToAddForNode.Skip(i).Take(batchSizeNode).ToList();
//建立參數化查詢要用的物件
List<object> parameters = new List<object>();
foreach (var data in batch)
{
parameters.Add(new
{
device_number = data.niagara_tags,
full_name = data.device_full_name,
});
}
//建立多條Insert語法可以搭配Dapper的參數化查詢
string insertSql = string.Join(" ", sqlInsertDeviceNodeList.Take(batchSizeNode));
using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled)) using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
{ {
await conn.ExecuteAsync(sb.ToString()); await conn.ExecuteAsync(insertSql, parameters); // 使用參數化查詢
scope.Complete();
} }
sb.Clear();
} }
stopwatchSection.Stop();
await KeepTimeLog("insert into device_node", stopwatchSection.ElapsedMilliseconds);
} }
#endregion sw7.Stop();
await KeepTimeLog("insert into device_node", sw7.ElapsedMilliseconds);
stopwatchSection = new Stopwatch();
stopwatchSection.Start();
//device有niagara有is_link 更新成 1
sb.Append($@" SET SQL_SAFE_UPDATES = 0;
UPDATE device d
JOIN (
SELECT niagara_tags
FROM import_niagara_tag
LIMIT 100000
) i ON d.device_number = i.niagara_tags
SET d.is_link = 1, d.deleted = 0");
using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
{
await conn.ExecuteAsync(sb.ToString());
}
sb.Clear();
stopwatchSection.Stop();
await KeepTimeLog("update device is_link = 1, deleted = 0", stopwatchSection.ElapsedMilliseconds);
stopwatchSection = new Stopwatch(); // 5. 更新 device 的 is_link
stopwatchSection.Start(); Stopwatch sw8 = Stopwatch.StartNew();
//device有niagara沒有is_link 更新成 0
sb.Append($@" SET SQL_SAFE_UPDATES = 0;
UPDATE device d
left JOIN (
SELECT niagara_tags
FROM import_niagara_tag
LIMIT 100000
) i ON d.device_number = i.niagara_tags
SET d.is_link = 0
WHERE d.is_link = 1 and i.niagara_tags is null;");
using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
{
await conn.ExecuteAsync(sb.ToString());
}
sb.Clear();
stopwatchSection.Stop();
await KeepTimeLog("update device is_link = 0", stopwatchSection.ElapsedMilliseconds);
stopwatchSection = new Stopwatch(); // 先更新 device 的 is_link 為 1 (邏輯簡單,直接使用 UPDATE JOIN)
stopwatchSection.Start(); string sqlUpdateDeviceIsLinkToOne = @"
// device_node 有, niagara沒有, is_link 更新成 0 UPDATE device d
sb.Append($@" SET SQL_SAFE_UPDATES = 0; JOIN import_niagara_tag i ON d.device_number = i.niagara_tags
UPDATE device_node dn SET d.is_link = 1, d.deleted = 0;";
left JOIN (
SELECT niagara_tags
FROM import_niagara_tag
LIMIT 100000
) i ON dn.device_number COLLATE utf8mb4_0900_ai_ci = i.niagara_tags COLLATE utf8mb4_0900_ai_ci
SET dn.is_link = 0
WHERE dn.is_link = 1 and i.niagara_tags is null;");
using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled)) using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
{ {
await conn.ExecuteAsync(sb.ToString()); await conn.ExecuteAsync(sqlUpdateDeviceIsLinkToOne); // 使用參數化查詢
scope.Complete();
} }
stopwatchSection.Stop();
await KeepTimeLog("update device_node is_link = 0", stopwatchSection.ElapsedMilliseconds);
sw8.Stop();
await KeepTimeLog("update device is_link = 1, deleted = 0", sw8.ElapsedMilliseconds);
// 再更新 device 的 is_link 為 0 (邏輯需要調整,使用 NOT EXISTS 子查詢)
Stopwatch sw9 = Stopwatch.StartNew();
string sqlUpdateDeviceIsLinkToZero = @"
UPDATE device
SET is_link = 0
WHERE is_link = 1
AND NOT EXISTS (
SELECT 1
FROM import_niagara_tag
WHERE device.device_number = import_niagara_tag.niagara_tags
);";
using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
{
await conn.ExecuteAsync(sqlUpdateDeviceIsLinkToZero); // 使用參數化查詢
scope.Complete();
}
sw9.Stop();
await KeepTimeLog("update device is_link = 0", sw9.ElapsedMilliseconds);
// 6. 更新 device_node 的 is_link (邏輯同 5需要調整 SQL 語法和參數)
Stopwatch sw10 = Stopwatch.StartNew();
string sqlUpdateDeviceNodeIsLinkToZero = @"
UPDATE device_node
SET is_link = 0
WHERE is_link = 1
AND NOT EXISTS (
SELECT 1
FROM import_niagara_tag
WHERE device_node.device_number = import_niagara_tag.niagara_tags
);";
using (TransactionScope scope = new TransactionScope((TransactionScopeOption)TransactionScopeAsyncFlowOption.Enabled))
{
await conn.ExecuteAsync(sqlUpdateDeviceNodeIsLinkToZero); // 使用參數化查詢
scope.Complete();
}
sw10.Stop();
await KeepTimeLog("update device_node is_link = 0", sw10.ElapsedMilliseconds);
//Main總時間
stopwatch.Stop(); stopwatch.Stop();
await KeepTimeLog("DeviceComparison", stopwatch.ElapsedMilliseconds); await KeepTimeLog("DeviceComparison", stopwatch.ElapsedMilliseconds);
} }
catch (Exception exception) catch (Exception exception)
{ {
//throw exception; throw; // 保留原始堆疊追蹤
throw;
} }
finally finally
{ {
conn.Close(); if (conn.State == ConnectionState.Open)
{
conn.Close();
}
} }
} }
} }
@ -952,8 +980,7 @@ namespace Repository.BackendRepository.Implement
AND d.device_name_tag = subquery.device_name_tag AND d.device_name_tag = subquery.device_name_tag
AND d.points = subquery.device_point_name AND d.points = subquery.device_point_name
AND d.device_building_tag = subquery.device_building_tag AND d.device_building_tag = subquery.device_building_tag
SET d.is_show_history = CASE WHEN subquery.device_point_name IS NULL THEN 0 ELSE 1 END SET d.is_show_history = CASE WHEN subquery.device_point_name IS NULL THEN 0 ELSE 1 END");
where date(d.created_at) = subquery.created_at;");
using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{ {
@ -1337,7 +1364,7 @@ namespace Repository.BackendRepository.Implement
throw exception; throw exception;
} }
finally finally
{ {
conn.Close(); conn.Close();
} }
} }
@ -1358,7 +1385,8 @@ namespace Repository.BackendRepository.Implement
try try
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.Append("update device set full_name=device_number where full_Name='';"); sb.Append(@"SET SQL_SAFE_UPDATES = 0;
update device set full_name=device_number where full_Name='';");
await conn.ExecuteAsync(sb.ToString()); await conn.ExecuteAsync(sb.ToString());
} }
catch (Exception exception) catch (Exception exception)
@ -1426,13 +1454,13 @@ namespace Repository.BackendRepository.Implement
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.Append(@"SET SQL_SAFE_UPDATES = 0; sb.Append(@"SET SQL_SAFE_UPDATES = 0;
UPDATE device d
JOIN ( UPDATE device AS d
SELECT niagara_tags, device_full_name JOIN import_niagara_tag AS i ON d.device_number = i.niagara_tags
FROM import_niagara_tag SET d.full_name = i.device_full_name
) m ON m.niagara_tags = d.device_number WHERE d.full_name <> i.device_full_name;
SET d.full_name = m.device_full_name
WHERE d.full_name != m.device_full_name;"); SET SQL_SAFE_UPDATES = 1;");
await conn.ExecuteAsync(sb.ToString()); await conn.ExecuteAsync(sb.ToString());
} }
catch (Exception exception) catch (Exception exception)
@ -1589,7 +1617,7 @@ namespace Repository.BackendRepository.Implement
conn.Close(); conn.Close();
} }
} }
stopwatchSection = new Stopwatch(); stopwatchSection = new Stopwatch();
stopwatchSection.Start(); stopwatchSection.Start();
// 過濾 dv 集合,只保留設備編號有效的項目 // 過濾 dv 集合,只保留設備編號有效的項目
@ -1800,24 +1828,24 @@ SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `import_niagara_item_history`; DROP TABLE IF EXISTS `import_niagara_item_history`;
CREATE TABLE `import_niagara_item_history` ( CREATE TABLE `import_niagara_item_history` (
`id` int(11) NOT NULL AUTO_INCREMENT, `id` int(11) NOT NULL AUTO_INCREMENT,
`device_number` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT ' TagName ', `device_number` varchar(50) NULL DEFAULT NULL COMMENT ' TagName ',
`device_area_tag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, `device_area_tag` varchar(50) NULL DEFAULT NULL,
`device_building_tag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, `device_building_tag` varchar(50) NULL DEFAULT NULL,
`device_system_tag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, `device_system_tag` varchar(50) NULL DEFAULT NULL,
`device_name_tag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, `device_name_tag` varchar(50) NULL DEFAULT NULL,
`device_floor_tag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, `device_floor_tag` varchar(50) NULL DEFAULT NULL,
`device_master_tag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, `device_master_tag` varchar(50) NULL DEFAULT NULL,
`device_last_name_tag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, `device_last_name_tag` varchar(50) NULL DEFAULT NULL,
`device_serial_tag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, `device_serial_tag` varchar(50) NULL DEFAULT NULL,
`device_point_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, `device_point_name` varchar(50) NULL DEFAULT NULL,
`parent_path` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, `parent_path` varchar(50) NULL DEFAULT NULL,
`is_history` bit(1) NULL DEFAULT b'0', `is_history` bit(1) NULL DEFAULT b'0',
`full_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, `full_name` varchar(50) NULL DEFAULT NULL,
`check_status` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, `check_status` varchar(50) NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE, PRIMARY KEY (`id`) USING BTREE,
KEY `idx_device_number` (`device_number`) KEY `idx_device_number` (`device_number`)
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;"; SET FOREIGN_KEY_CHECKS = 1;";
using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
@ -1957,7 +1985,7 @@ SET FOREIGN_KEY_CHECKS = 1;";
/// </summary> /// </summary>
/// <param name="dcp"></param> /// <param name="dcp"></param>
/// <returns></returns> /// <returns></returns>
public async Task DeviceControlPoint (List<DeviceControlPoint> dcp) public async Task DeviceControlPoint(List<DeviceControlPoint> dcp)
{ {
stopwatch = new Stopwatch(); stopwatch = new Stopwatch();
stopwatch.Start(); stopwatch.Start();
@ -1979,7 +2007,7 @@ SET FOREIGN_KEY_CHECKS = 1;";
`device_number` varchar(50) DEFAULT NULL, `device_number` varchar(50) DEFAULT NULL,
`device_point_name` varchar(50) DEFAULT NULL, `device_point_name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;"; ) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;";
using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{ {
await conn.ExecuteAsync(sql); await conn.ExecuteAsync(sql);