Add MySQL-backed HRMS repositories and DB integration configuration.
1) Introduces DB connectivity, repository abstractions, and new config flags/env overrides. 2) Keeps architecture clean and isolated.main
parent
fbd4d1d313
commit
93b2bf9bd7
|
|
@ -0,0 +1,34 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace HikvisionAttendanceService.Data;
|
||||||
|
|
||||||
|
internal sealed class AttendanceMachineRow
|
||||||
|
{
|
||||||
|
public string MachineId { get; set; } = "";
|
||||||
|
public string MachineIp { get; set; } = "";
|
||||||
|
public int PortNumber { get; set; } = 8000;
|
||||||
|
public string MachineName { get; set; } = "";
|
||||||
|
public int? SiteId { get; set; }
|
||||||
|
public string MachineStatus { get; set; } = "";
|
||||||
|
public string MachineType { get; set; } = "";
|
||||||
|
public string Status { get; set; } = "";
|
||||||
|
public DateTime? LastSyncDate { get; set; }
|
||||||
|
public int TotalUsers { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class AttendanceMachineUserRow
|
||||||
|
{
|
||||||
|
public string MachineId { get; set; } = "";
|
||||||
|
public string SerialNumber { get; set; } = "";
|
||||||
|
public string EmployeeName { get; set; } = "";
|
||||||
|
public bool IsDeleted { get; set; }
|
||||||
|
public bool IsDeletionRequested { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class AttendanceMachineFaceTemplateRow
|
||||||
|
{
|
||||||
|
public string SerialNo { get; set; } = "";
|
||||||
|
public byte[] Template { get; set; } = Array.Empty<byte>();
|
||||||
|
public DateTime CreatedDate { get; set; }
|
||||||
|
public bool IsActive { get; set; } = true;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace HikvisionAttendanceService.Data;
|
||||||
|
|
||||||
|
internal interface IHrmsDbConnectionFactory
|
||||||
|
{
|
||||||
|
string BuildConnectionStringMasked();
|
||||||
|
bool TryBuildConnectionString(out string connectionString, out string error);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface IAttendanceMachineRepository
|
||||||
|
{
|
||||||
|
List<AttendanceMachineRow> GetActiveMachines(string? machineTypeFilter, out string error);
|
||||||
|
void UpdateMachineSyncState(string machineIp, string status, DateTime? lastSyncDate, int? totalUsers, out string error);
|
||||||
|
bool TryGetMachineLastSyncDate(string machineIp, out DateTime? lastSyncDate, out string error);
|
||||||
|
bool UpdateMachineLastSyncDate(string machineIp, DateTime lastSyncDate, out string error);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface IAttendanceLogRepository
|
||||||
|
{
|
||||||
|
bool UpsertAttendance(
|
||||||
|
string acNo,
|
||||||
|
DateTime checkTime,
|
||||||
|
int processed,
|
||||||
|
string machineId,
|
||||||
|
int inOutTypeId,
|
||||||
|
string machineIp,
|
||||||
|
DateTime dateOnly,
|
||||||
|
out string error);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface IAttendanceMachineUserRepository
|
||||||
|
{
|
||||||
|
List<AttendanceMachineUserRow> GetActiveUsersByMachine(string machineId, out string error);
|
||||||
|
List<AttendanceMachineUserRow> GetPendingDeletionUsersByMachine(string machineId, out string error);
|
||||||
|
bool UpsertMachineUser(string machineId, string serialNumber, string employeeName, out string error);
|
||||||
|
bool MarkDeleted(string machineId, string serialNumber, string updatedBy, out string error);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface IAttendanceMachineFaceTemplateRepository
|
||||||
|
{
|
||||||
|
bool UpsertFaceTemplate(string serialNo, byte[] template, DateTime createdDate, bool isActive, out string error);
|
||||||
|
bool TryGetActiveFaceTemplateBySerial(string serialNo, out AttendanceMachineFaceTemplateRow? row, out string error);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
using MySql.Data.MySqlClient;
|
||||||
|
|
||||||
|
namespace HikvisionAttendanceService.Data;
|
||||||
|
|
||||||
|
internal sealed class MySqlHrmsDbConnectionFactory : IHrmsDbConnectionFactory
|
||||||
|
{
|
||||||
|
private readonly HikvisionAttendanceWindowsService.HikvisionServiceConfig _config;
|
||||||
|
|
||||||
|
public MySqlHrmsDbConnectionFactory(HikvisionAttendanceWindowsService.HikvisionServiceConfig config)
|
||||||
|
{
|
||||||
|
_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string BuildConnectionStringMasked()
|
||||||
|
{
|
||||||
|
if (TryBuildConnectionString(out var cs, out _))
|
||||||
|
{
|
||||||
|
var b = new MySqlConnectionStringBuilder(cs);
|
||||||
|
b.Password = "***";
|
||||||
|
return b.ConnectionString;
|
||||||
|
}
|
||||||
|
return "(invalid)";
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryBuildConnectionString(out string connectionString, out string error)
|
||||||
|
{
|
||||||
|
connectionString = "";
|
||||||
|
error = "";
|
||||||
|
if (!string.IsNullOrWhiteSpace(_config.DbConnectionString))
|
||||||
|
{
|
||||||
|
connectionString = _config.DbConnectionString.Trim();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(_config.DbHost) ||
|
||||||
|
string.IsNullOrWhiteSpace(_config.DbUsername) ||
|
||||||
|
string.IsNullOrWhiteSpace(_config.DbName))
|
||||||
|
{
|
||||||
|
error = "DbHost/DbUsername/DbName are required when DbConnectionString is empty.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var b = new MySqlConnectionStringBuilder
|
||||||
|
{
|
||||||
|
Server = _config.DbHost.Trim(),
|
||||||
|
Port = (uint)(_config.DbPort > 0 ? _config.DbPort : 3306),
|
||||||
|
Database = _config.DbName.Trim(),
|
||||||
|
UserID = _config.DbUsername.Trim(),
|
||||||
|
Password = _config.DbPassword ?? "",
|
||||||
|
DefaultCommandTimeout = 30
|
||||||
|
};
|
||||||
|
connectionString = b.ConnectionString;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,298 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using MySql.Data.MySqlClient;
|
||||||
|
|
||||||
|
namespace HikvisionAttendanceService.Data;
|
||||||
|
|
||||||
|
internal sealed class MySqlAttendanceMachineRepository : IAttendanceMachineRepository
|
||||||
|
{
|
||||||
|
private readonly IHrmsDbConnectionFactory _factory;
|
||||||
|
public MySqlAttendanceMachineRepository(IHrmsDbConnectionFactory factory) { _factory = factory; }
|
||||||
|
|
||||||
|
public List<AttendanceMachineRow> GetActiveMachines(string? machineTypeFilter, out string error)
|
||||||
|
{
|
||||||
|
error = "";
|
||||||
|
var rows = new List<AttendanceMachineRow>();
|
||||||
|
if (!_factory.TryBuildConnectionString(out var cs, out error))
|
||||||
|
return rows;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var conn = new MySqlConnection(cs);
|
||||||
|
conn.Open();
|
||||||
|
string sql = "SELECT machine_id,machine_ip,port_number,machine_name,site_id,machine_status,machine_type,status,last_sync_date,total_users " +
|
||||||
|
"FROM attendance_machine WHERE (machine_status='active' OR machine_status='1' OR machine_status=1)";
|
||||||
|
if (!string.IsNullOrWhiteSpace(machineTypeFilter))
|
||||||
|
sql += " AND machine_type=@machineType";
|
||||||
|
using var cmd = new MySqlCommand(sql, conn);
|
||||||
|
if (!string.IsNullOrWhiteSpace(machineTypeFilter))
|
||||||
|
cmd.Parameters.AddWithValue("@machineType", machineTypeFilter);
|
||||||
|
using var rd = cmd.ExecuteReader();
|
||||||
|
while (rd.Read())
|
||||||
|
{
|
||||||
|
rows.Add(new AttendanceMachineRow
|
||||||
|
{
|
||||||
|
MachineId = rd["machine_id"]?.ToString() ?? "",
|
||||||
|
MachineIp = rd["machine_ip"]?.ToString() ?? "",
|
||||||
|
PortNumber = rd["port_number"] == DBNull.Value ? 8000 : Convert.ToInt32(rd["port_number"]),
|
||||||
|
MachineName = rd["machine_name"]?.ToString() ?? "",
|
||||||
|
SiteId = rd["site_id"] == DBNull.Value ? (int?)null : Convert.ToInt32(rd["site_id"]),
|
||||||
|
MachineStatus = rd["machine_status"]?.ToString() ?? "",
|
||||||
|
MachineType = rd["machine_type"]?.ToString() ?? "",
|
||||||
|
Status = rd["status"]?.ToString() ?? "",
|
||||||
|
LastSyncDate = rd["last_sync_date"] == DBNull.Value ? (DateTime?)null : Convert.ToDateTime(rd["last_sync_date"]),
|
||||||
|
TotalUsers = rd["total_users"] == DBNull.Value ? 0 : Convert.ToInt32(rd["total_users"])
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) { error = ex.Message; }
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateMachineSyncState(string machineIp, string status, DateTime? lastSyncDate, int? totalUsers, out string error)
|
||||||
|
{
|
||||||
|
error = "";
|
||||||
|
if (!_factory.TryBuildConnectionString(out var cs, out error))
|
||||||
|
return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var conn = new MySqlConnection(cs);
|
||||||
|
conn.Open();
|
||||||
|
using var cmd = new MySqlCommand(
|
||||||
|
"UPDATE attendance_machine SET status=@status,last_sync_date=COALESCE(@last_sync_date,last_sync_date),total_users=COALESCE(@total_users,total_users) WHERE machine_ip=@machine_ip",
|
||||||
|
conn);
|
||||||
|
cmd.Parameters.AddWithValue("@status", status ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@last_sync_date", (object?)lastSyncDate ?? DBNull.Value);
|
||||||
|
cmd.Parameters.AddWithValue("@total_users", (object?)totalUsers ?? DBNull.Value);
|
||||||
|
cmd.Parameters.AddWithValue("@machine_ip", machineIp ?? "");
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
catch (Exception ex) { error = ex.Message; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetMachineLastSyncDate(string machineIp, out DateTime? lastSyncDate, out string error)
|
||||||
|
{
|
||||||
|
lastSyncDate = null;
|
||||||
|
error = "";
|
||||||
|
if (!_factory.TryBuildConnectionString(out var cs, out error))
|
||||||
|
return false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var conn = new MySqlConnection(cs);
|
||||||
|
conn.Open();
|
||||||
|
using var cmd = new MySqlCommand(
|
||||||
|
"SELECT last_sync_date FROM attendance_machine WHERE machine_ip=@machine_ip LIMIT 1",
|
||||||
|
conn);
|
||||||
|
cmd.Parameters.AddWithValue("@machine_ip", machineIp ?? "");
|
||||||
|
var obj = cmd.ExecuteScalar();
|
||||||
|
if (obj == null || obj == DBNull.Value)
|
||||||
|
return true;
|
||||||
|
lastSyncDate = Convert.ToDateTime(obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
error = ex.Message;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UpdateMachineLastSyncDate(string machineIp, DateTime lastSyncDate, out string error)
|
||||||
|
{
|
||||||
|
error = "";
|
||||||
|
if (!_factory.TryBuildConnectionString(out var cs, out error))
|
||||||
|
return false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var conn = new MySqlConnection(cs);
|
||||||
|
conn.Open();
|
||||||
|
using var cmd = new MySqlCommand(
|
||||||
|
"UPDATE attendance_machine SET last_sync_date=@last_sync_date WHERE machine_ip=@machine_ip",
|
||||||
|
conn);
|
||||||
|
cmd.Parameters.AddWithValue("@last_sync_date", lastSyncDate);
|
||||||
|
cmd.Parameters.AddWithValue("@machine_ip", machineIp ?? "");
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
error = ex.Message;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class MySqlAttendanceLogRepository : IAttendanceLogRepository
|
||||||
|
{
|
||||||
|
private readonly IHrmsDbConnectionFactory _factory;
|
||||||
|
public MySqlAttendanceLogRepository(IHrmsDbConnectionFactory factory) { _factory = factory; }
|
||||||
|
|
||||||
|
public bool UpsertAttendance(string acNo, DateTime checkTime, int processed, string machineId, int inOutTypeId, string machineIp, DateTime dateOnly, out string error)
|
||||||
|
{
|
||||||
|
error = "";
|
||||||
|
if (!_factory.TryBuildConnectionString(out var cs, out error))
|
||||||
|
return false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var conn = new MySqlConnection(cs);
|
||||||
|
conn.Open();
|
||||||
|
using var cmd = new MySqlCommand(
|
||||||
|
"REPLACE INTO attendance_log (ac_no,checktime,processed,machine_id,in_out_type_id,machine_ip,date) " +
|
||||||
|
"VALUES (@ac_no,@checktime,@processed,@machine_id,@in_out_type_id,@machine_ip,@date)",
|
||||||
|
conn);
|
||||||
|
cmd.Parameters.AddWithValue("@ac_no", acNo ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@checktime", checkTime);
|
||||||
|
cmd.Parameters.AddWithValue("@processed", processed);
|
||||||
|
cmd.Parameters.AddWithValue("@machine_id", machineId ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@in_out_type_id", inOutTypeId);
|
||||||
|
cmd.Parameters.AddWithValue("@machine_ip", machineIp ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@date", dateOnly.Date);
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex) { error = ex.Message; return false; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class MySqlAttendanceMachineUserRepository : IAttendanceMachineUserRepository
|
||||||
|
{
|
||||||
|
private readonly IHrmsDbConnectionFactory _factory;
|
||||||
|
public MySqlAttendanceMachineUserRepository(IHrmsDbConnectionFactory factory) { _factory = factory; }
|
||||||
|
|
||||||
|
public List<AttendanceMachineUserRow> GetActiveUsersByMachine(string machineId, out string error) =>
|
||||||
|
QueryUsers(machineId, false, out error);
|
||||||
|
|
||||||
|
public List<AttendanceMachineUserRow> GetPendingDeletionUsersByMachine(string machineId, out string error) =>
|
||||||
|
QueryUsers(machineId, true, out error);
|
||||||
|
|
||||||
|
private List<AttendanceMachineUserRow> QueryUsers(string machineId, bool deletionRequested, out string error)
|
||||||
|
{
|
||||||
|
error = "";
|
||||||
|
var rows = new List<AttendanceMachineUserRow>();
|
||||||
|
if (!_factory.TryBuildConnectionString(out var cs, out error))
|
||||||
|
return rows;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var conn = new MySqlConnection(cs);
|
||||||
|
conn.Open();
|
||||||
|
using var cmd = new MySqlCommand(
|
||||||
|
"SELECT machine_id,serial_number,employee_name,is_deleted,is_deletion_requested FROM attendance_machine_user " +
|
||||||
|
"WHERE machine_id=@machine_id AND is_deleted=0 AND is_deletion_requested=@is_deletion_requested",
|
||||||
|
conn);
|
||||||
|
cmd.Parameters.AddWithValue("@machine_id", machineId ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@is_deletion_requested", deletionRequested ? 1 : 0);
|
||||||
|
using var rd = cmd.ExecuteReader();
|
||||||
|
while (rd.Read())
|
||||||
|
{
|
||||||
|
rows.Add(new AttendanceMachineUserRow
|
||||||
|
{
|
||||||
|
MachineId = rd["machine_id"]?.ToString() ?? "",
|
||||||
|
SerialNumber = rd["serial_number"]?.ToString() ?? "",
|
||||||
|
EmployeeName = rd["employee_name"]?.ToString() ?? "",
|
||||||
|
IsDeleted = Convert.ToInt32(rd["is_deleted"]) != 0,
|
||||||
|
IsDeletionRequested = Convert.ToInt32(rd["is_deletion_requested"]) != 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) { error = ex.Message; }
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UpsertMachineUser(string machineId, string serialNumber, string employeeName, out string error)
|
||||||
|
{
|
||||||
|
error = "";
|
||||||
|
if (!_factory.TryBuildConnectionString(out var cs, out error))
|
||||||
|
return false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var conn = new MySqlConnection(cs);
|
||||||
|
conn.Open();
|
||||||
|
using var cmd = new MySqlCommand(
|
||||||
|
"REPLACE INTO attendance_machine_user (machine_id,serial_number,employee_name,is_deleted,is_deletion_requested,updated_at) " +
|
||||||
|
"VALUES (@machine_id,@serial_number,@employee_name,0,0,NOW())", conn);
|
||||||
|
cmd.Parameters.AddWithValue("@machine_id", machineId ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@serial_number", serialNumber ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@employee_name", employeeName ?? "");
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex) { error = ex.Message; return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MarkDeleted(string machineId, string serialNumber, string updatedBy, out string error)
|
||||||
|
{
|
||||||
|
error = "";
|
||||||
|
if (!_factory.TryBuildConnectionString(out var cs, out error))
|
||||||
|
return false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var conn = new MySqlConnection(cs);
|
||||||
|
conn.Open();
|
||||||
|
using var cmd = new MySqlCommand(
|
||||||
|
"UPDATE attendance_machine_user SET is_deleted=1,is_deletion_requested=0,updated_by=@updated_by,updated_at=NOW() " +
|
||||||
|
"WHERE machine_id=@machine_id AND serial_number=@serial_number", conn);
|
||||||
|
cmd.Parameters.AddWithValue("@updated_by", updatedBy ?? "hikvision-service");
|
||||||
|
cmd.Parameters.AddWithValue("@machine_id", machineId ?? "");
|
||||||
|
cmd.Parameters.AddWithValue("@serial_number", serialNumber ?? "");
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex) { error = ex.Message; return false; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class MySqlAttendanceMachineFaceTemplateRepository : IAttendanceMachineFaceTemplateRepository
|
||||||
|
{
|
||||||
|
private readonly IHrmsDbConnectionFactory _factory;
|
||||||
|
public MySqlAttendanceMachineFaceTemplateRepository(IHrmsDbConnectionFactory factory) { _factory = factory; }
|
||||||
|
|
||||||
|
public bool UpsertFaceTemplate(string serialNo, byte[] template, DateTime createdDate, bool isActive, out string error)
|
||||||
|
{
|
||||||
|
error = "";
|
||||||
|
if (!_factory.TryBuildConnectionString(out var cs, out error))
|
||||||
|
return false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var conn = new MySqlConnection(cs);
|
||||||
|
conn.Open();
|
||||||
|
using var cmd = new MySqlCommand(
|
||||||
|
"REPLACE INTO attendance_machine_face_templates (serial_no,template,created_date,is_active) " +
|
||||||
|
"VALUES (@serial_no,@template,@created_date,@is_active)", conn);
|
||||||
|
cmd.Parameters.AddWithValue("@serial_no", serialNo ?? "");
|
||||||
|
cmd.Parameters.Add("@template", MySqlDbType.Blob).Value = (object?)template ?? Array.Empty<byte>();
|
||||||
|
cmd.Parameters.AddWithValue("@created_date", createdDate);
|
||||||
|
cmd.Parameters.AddWithValue("@is_active", isActive ? 1 : 0);
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex) { error = ex.Message; return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetActiveFaceTemplateBySerial(string serialNo, out AttendanceMachineFaceTemplateRow? row, out string error)
|
||||||
|
{
|
||||||
|
row = null;
|
||||||
|
error = "";
|
||||||
|
if (!_factory.TryBuildConnectionString(out var cs, out error))
|
||||||
|
return false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var conn = new MySqlConnection(cs);
|
||||||
|
conn.Open();
|
||||||
|
using var cmd = new MySqlCommand(
|
||||||
|
"SELECT serial_no,template,created_date,is_active FROM attendance_machine_face_templates " +
|
||||||
|
"WHERE serial_no=@serial_no AND is_active=1 ORDER BY created_date DESC LIMIT 1", conn);
|
||||||
|
cmd.Parameters.AddWithValue("@serial_no", serialNo ?? "");
|
||||||
|
using var rd = cmd.ExecuteReader();
|
||||||
|
if (!rd.Read())
|
||||||
|
return false;
|
||||||
|
row = new AttendanceMachineFaceTemplateRow
|
||||||
|
{
|
||||||
|
SerialNo = rd["serial_no"]?.ToString() ?? "",
|
||||||
|
Template = rd["template"] == DBNull.Value ? Array.Empty<byte>() : (byte[])rd["template"],
|
||||||
|
CreatedDate = rd["created_date"] == DBNull.Value ? DateTime.MinValue : Convert.ToDateTime(rd["created_date"]),
|
||||||
|
IsActive = rd["is_active"] != DBNull.Value && Convert.ToInt32(rd["is_active"]) != 0
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex) { error = ex.Message; return false; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -22,13 +22,10 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- Hikvision interop (imported directly from the SDK demo source files) -->
|
<!-- Hikvision interop (imported directly from the SDK demo source files) -->
|
||||||
<Compile Include="D:\EN-HCNetSDKV6.1.9.48_build20230410_win32\EN-HCNetSDKV6.1.9.48_build20230410_win32\C# demo\8-ACS_Optimization_ALL\EventByDeploy\EventByDeploy\HCNetSDK.cs"
|
<Compile Include="D:\EN-HCNetSDKV6.1.9.48_build20230410_win32\EN-HCNetSDKV6.1.9.48_build20230410_win32\C# demo\8-ACS_Optimization_ALL\EventByDeploy\EventByDeploy\HCNetSDK.cs" Link="Interop\EventByDeploy\HCNetSDK.cs" />
|
||||||
Link="Interop\EventByDeploy\HCNetSDK.cs" />
|
<Compile Include="D:\EN-HCNetSDKV6.1.9.48_build20230410_win32\EN-HCNetSDKV6.1.9.48_build20230410_win32\C# demo\8-ACS_Optimization_ALL\EventByDeploy\EventByDeploy\TypeMap.cs" Link="Interop\EventByDeploy\TypeMap.cs" />
|
||||||
<Compile Include="D:\EN-HCNetSDKV6.1.9.48_build20230410_win32\EN-HCNetSDKV6.1.9.48_build20230410_win32\C# demo\8-ACS_Optimization_ALL\EventByDeploy\EventByDeploy\TypeMap.cs"
|
|
||||||
Link="Interop\EventByDeploy\TypeMap.cs" />
|
|
||||||
<!-- Login_V30 + core callback helpers -->
|
<!-- Login_V30 + core callback helpers -->
|
||||||
<Compile Include="D:\EN-HCNetSDKV6.1.9.48_build20230410_win32\EN-HCNetSDKV6.1.9.48_build20230410_win32\C# demo\9-AppsDemo_build20201230191326\CommonBase\Head\HCNetSDK.cs"
|
<Compile Include="D:\EN-HCNetSDKV6.1.9.48_build20230410_win32\EN-HCNetSDKV6.1.9.48_build20230410_win32\C# demo\9-AppsDemo_build20201230191326\CommonBase\Head\HCNetSDK.cs" Link="Interop\AppsDemo\CommonBase\HCNetSDK_LoginV30.cs" />
|
||||||
Link="Interop\AppsDemo\CommonBase\HCNetSDK_LoginV30.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
@ -51,4 +48,10 @@
|
||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="MySql.Data">
|
||||||
|
<Version>9.6.0</Version>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,48 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
|
||||||
[DataMember]
|
[DataMember]
|
||||||
public string SqlConnectionString { get; set; } = "";
|
public string SqlConnectionString { get; set; } = "";
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public bool EnableDbIntegration { get; set; }
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public bool EnableDbMachineLoading { get; set; }
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public bool PreferDbMachinesOverConfig { get; set; }
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public bool EnableAttendanceDbPersistence { get; set; }
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public bool EnableTemplateDbPersistence { get; set; }
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public bool EnableTemplateDbToDeviceSync { get; set; }
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public bool KeepAttendanceFileExport { get; set; } = true;
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public bool KeepTemplateFiles { get; set; } = true;
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public string DbHost { get; set; } = "";
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public int DbPort { get; set; } = 3306;
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public string DbName { get; set; } = "hrms";
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public string DbUsername { get; set; } = "";
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public string DbPassword { get; set; } = "";
|
||||||
|
|
||||||
|
[DataMember]
|
||||||
|
public string DbConnectionString { get; set; } = "";
|
||||||
|
|
||||||
/// <summary>When true and SqlConnectionString is set, attendance rows are INSERTed. Default false until schema is finalized.</summary>
|
/// <summary>When true and SqlConnectionString is set, attendance rows are INSERTed. Default false until schema is finalized.</summary>
|
||||||
[DataMember]
|
[DataMember]
|
||||||
public bool EnableDatabasePersistence { get; set; }
|
public bool EnableDatabasePersistence { get; set; }
|
||||||
|
|
@ -177,6 +219,20 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
|
||||||
{
|
{
|
||||||
LogDirectory = @"C:\Users\Public\HikvisionAttendanceService",
|
LogDirectory = @"C:\Users\Public\HikvisionAttendanceService",
|
||||||
SqlConnectionString = "",
|
SqlConnectionString = "",
|
||||||
|
EnableDbIntegration = false,
|
||||||
|
EnableDbMachineLoading = false,
|
||||||
|
PreferDbMachinesOverConfig = false,
|
||||||
|
EnableAttendanceDbPersistence = false,
|
||||||
|
EnableTemplateDbPersistence = false,
|
||||||
|
EnableTemplateDbToDeviceSync = false,
|
||||||
|
KeepAttendanceFileExport = true,
|
||||||
|
KeepTemplateFiles = true,
|
||||||
|
DbHost = "",
|
||||||
|
DbPort = 3306,
|
||||||
|
DbName = "hrms",
|
||||||
|
DbUsername = "",
|
||||||
|
DbPassword = "",
|
||||||
|
DbConnectionString = "",
|
||||||
EnableDatabasePersistence = false,
|
EnableDatabasePersistence = false,
|
||||||
AttendanceTextFilePath = "",
|
AttendanceTextFilePath = "",
|
||||||
AttendanceTableName = "dbo.HikvisionAttendanceEvents",
|
AttendanceTableName = "dbo.HikvisionAttendanceEvents",
|
||||||
|
|
@ -211,6 +267,7 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
|
||||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(raw)))
|
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(raw)))
|
||||||
{
|
{
|
||||||
var cfg = (HikvisionServiceConfig)serializer.ReadObject(ms);
|
var cfg = (HikvisionServiceConfig)serializer.ReadObject(ms);
|
||||||
|
ApplyEnvironmentOverrides(cfg);
|
||||||
NormalizeLoadedDeviceConfig(cfg);
|
NormalizeLoadedDeviceConfig(cfg);
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
|
|
@ -222,6 +279,7 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
|
||||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(sanitized)))
|
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(sanitized)))
|
||||||
{
|
{
|
||||||
var cfg = (HikvisionServiceConfig)serializer.ReadObject(ms);
|
var cfg = (HikvisionServiceConfig)serializer.ReadObject(ms);
|
||||||
|
ApplyEnvironmentOverrides(cfg);
|
||||||
NormalizeLoadedDeviceConfig(cfg);
|
NormalizeLoadedDeviceConfig(cfg);
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
|
|
@ -233,6 +291,20 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
|
||||||
{
|
{
|
||||||
LogDirectory = @"C:\Users\Public\HikvisionAttendanceService",
|
LogDirectory = @"C:\Users\Public\HikvisionAttendanceService",
|
||||||
SqlConnectionString = "",
|
SqlConnectionString = "",
|
||||||
|
EnableDbIntegration = false,
|
||||||
|
EnableDbMachineLoading = false,
|
||||||
|
PreferDbMachinesOverConfig = false,
|
||||||
|
EnableAttendanceDbPersistence = false,
|
||||||
|
EnableTemplateDbPersistence = false,
|
||||||
|
EnableTemplateDbToDeviceSync = false,
|
||||||
|
KeepAttendanceFileExport = true,
|
||||||
|
KeepTemplateFiles = true,
|
||||||
|
DbHost = "",
|
||||||
|
DbPort = 3306,
|
||||||
|
DbName = "hrms",
|
||||||
|
DbUsername = "",
|
||||||
|
DbPassword = "",
|
||||||
|
DbConnectionString = "",
|
||||||
EnableDatabasePersistence = false,
|
EnableDatabasePersistence = false,
|
||||||
AttendanceTextFilePath = "",
|
AttendanceTextFilePath = "",
|
||||||
AttendanceTableName = "dbo.HikvisionAttendanceEvents",
|
AttendanceTableName = "dbo.HikvisionAttendanceEvents",
|
||||||
|
|
@ -347,6 +419,34 @@ public sealed class HikvisionAttendanceWindowsService : ServiceBase
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ApplyEnvironmentOverrides(HikvisionServiceConfig cfg)
|
||||||
|
{
|
||||||
|
if (cfg == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string ReadEnv(string key) => Environment.GetEnvironmentVariable(key) ?? "";
|
||||||
|
|
||||||
|
var cs = ReadEnv("HIKVISION_DB_CONNECTION_STRING");
|
||||||
|
var host = ReadEnv("HIKVISION_DB_HOST");
|
||||||
|
var user = ReadEnv("HIKVISION_DB_USERNAME");
|
||||||
|
var pass = ReadEnv("HIKVISION_DB_PASSWORD");
|
||||||
|
var dbName = ReadEnv("HIKVISION_DB_NAME");
|
||||||
|
var dbPort = ReadEnv("HIKVISION_DB_PORT");
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(cs))
|
||||||
|
cfg.DbConnectionString = cs.Trim();
|
||||||
|
if (!string.IsNullOrWhiteSpace(host))
|
||||||
|
cfg.DbHost = host.Trim();
|
||||||
|
if (!string.IsNullOrWhiteSpace(user))
|
||||||
|
cfg.DbUsername = user.Trim();
|
||||||
|
if (!string.IsNullOrWhiteSpace(pass))
|
||||||
|
cfg.DbPassword = pass;
|
||||||
|
if (!string.IsNullOrWhiteSpace(dbName))
|
||||||
|
cfg.DbName = dbName.Trim();
|
||||||
|
if (int.TryParse(dbPort, out var parsedPort) && parsedPort > 0)
|
||||||
|
cfg.DbPort = parsedPort;
|
||||||
|
}
|
||||||
|
|
||||||
private static void NormalizeLoadedDeviceConfig(HikvisionServiceConfig? cfg)
|
private static void NormalizeLoadedDeviceConfig(HikvisionServiceConfig? cfg)
|
||||||
{
|
{
|
||||||
if (cfg == null)
|
if (cfg == null)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue