358 lines
15 KiB
C#
358 lines
15 KiB
C#
using MySql.Data.MySqlClient;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace HanvonF710XAttendanceService
|
|
{
|
|
internal sealed class AttendanceMachineFaceTemplateDAO
|
|
{
|
|
public bool ExistsBySerialNo(MySqlConnection connection, string serialNo)
|
|
{
|
|
const string checkQuery = "SELECT COUNT(*) FROM attendance_machine_face_templates WHERE serial_no = @SerialNumber";
|
|
using (var cmd = new MySqlCommand(checkQuery, connection))
|
|
{
|
|
cmd.Parameters.AddWithValue("@SerialNumber", serialNo);
|
|
object result = cmd.ExecuteScalar();
|
|
if (result == null || result == DBNull.Value)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (int.TryParse(result.ToString(), out int count))
|
|
{
|
|
return count > 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public int InsertTemplate(MySqlConnection connection, string serialNo, byte[] templateBytes, DateTime createdDate)
|
|
{
|
|
const string insertQuery =
|
|
"INSERT INTO attendance_machine_face_templates (serial_no, template, created_date) " +
|
|
"VALUES (@SerialNumber, @FaceTemplate, @created_date)";
|
|
|
|
using (var cmd = new MySqlCommand(insertQuery, connection))
|
|
{
|
|
cmd.Parameters.AddWithValue("@SerialNumber", serialNo);
|
|
cmd.Parameters.Add("@FaceTemplate", MySqlDbType.Blob).Value = templateBytes ?? Array.Empty<byte>();
|
|
cmd.Parameters.AddWithValue("@created_date", createdDate);
|
|
return cmd.ExecuteNonQuery();
|
|
}
|
|
}
|
|
|
|
public Dictionary<string, byte[]> GetActiveTemplatesBySerialNos(MySqlConnection connection, IReadOnlyList<string> serialNos)
|
|
{
|
|
var result = new Dictionary<string, byte[]>(StringComparer.OrdinalIgnoreCase);
|
|
if (serialNos == null || serialNos.Count == 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
// Chunk to keep query size reasonable.
|
|
const int chunkSize = 200;
|
|
for (int offset = 0; offset < serialNos.Count; offset += chunkSize)
|
|
{
|
|
var chunk = serialNos.Skip(offset).Take(chunkSize).ToArray();
|
|
var parameterNames = new List<string>(chunk.Length);
|
|
for (int i = 0; i < chunk.Length; i++)
|
|
{
|
|
parameterNames.Add("@SerialNumber" + i);
|
|
}
|
|
|
|
string inClause = string.Join(",", parameterNames);
|
|
string query =
|
|
$"SELECT serial_no, template FROM attendance_machine_face_templates " +
|
|
$"WHERE serial_no IN ({inClause}) AND is_active = 1";
|
|
|
|
using (var cmd = new MySqlCommand(query, connection))
|
|
{
|
|
for (int i = 0; i < chunk.Length; i++)
|
|
{
|
|
cmd.Parameters.AddWithValue(parameterNames[i], chunk[i]);
|
|
}
|
|
|
|
using (var reader = cmd.ExecuteReader())
|
|
{
|
|
while (reader.Read())
|
|
{
|
|
var serial = reader.IsDBNull(0) ? null : reader.GetString(0);
|
|
if (string.IsNullOrWhiteSpace(serial))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
byte[] blob = null;
|
|
if (!reader.IsDBNull(1))
|
|
{
|
|
blob = (byte[])reader["template"];
|
|
}
|
|
|
|
result[serial] = blob ?? Array.Empty<byte>();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>All active template serial numbers (CENTRAL hub scope).</summary>
|
|
public List<string> GetActiveSerialNumbersAll(MySqlConnection connection)
|
|
{
|
|
var list = new List<string>();
|
|
const string query = "SELECT serial_no FROM hrms.attendance_machine_face_templates WHERE is_active = 1";
|
|
using (var cmd = new MySqlCommand(query, connection))
|
|
{
|
|
cmd.CommandTimeout = 120;
|
|
using (var reader = cmd.ExecuteReader())
|
|
{
|
|
while (reader.Read())
|
|
{
|
|
if (reader.IsDBNull(0)) continue;
|
|
var s = reader.GetString(0);
|
|
if (!string.IsNullOrWhiteSpace(s))
|
|
{
|
|
list.Add(s.Trim());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serial numbers that have an active template and appear on scoped machines (SITE deployment).
|
|
/// </summary>
|
|
public List<string> GetActiveSerialNumbersForScopedMachines(MySqlConnection connection, IReadOnlyList<string> machineIds)
|
|
{
|
|
var result = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
if (machineIds == null || machineIds.Count == 0)
|
|
{
|
|
return new List<string>();
|
|
}
|
|
|
|
const int chunkSize = 80;
|
|
for (int offset = 0; offset < machineIds.Count; offset += chunkSize)
|
|
{
|
|
var chunk = machineIds.Skip(offset).Take(chunkSize).ToArray();
|
|
var parameterNames = new List<string>(chunk.Length);
|
|
for (int i = 0; i < chunk.Length; i++)
|
|
{
|
|
parameterNames.Add("@Mid" + i);
|
|
}
|
|
|
|
string inClause = string.Join(",", parameterNames);
|
|
string query =
|
|
"SELECT DISTINCT t.serial_no " +
|
|
"FROM hrms.attendance_machine_face_templates t " +
|
|
"INNER JOIN hrms.attendance_machine_user u ON u.serial_number = t.serial_no AND u.is_deleted = 0 " +
|
|
"WHERE t.is_active = 1 AND u.machine_id IN (" + inClause + ")";
|
|
|
|
using (var cmd = new MySqlCommand(query, connection))
|
|
{
|
|
cmd.CommandTimeout = 120;
|
|
for (int i = 0; i < chunk.Length; i++)
|
|
{
|
|
cmd.Parameters.AddWithValue(parameterNames[i], chunk[i]);
|
|
}
|
|
|
|
using (var reader = cmd.ExecuteReader())
|
|
{
|
|
while (reader.Read())
|
|
{
|
|
if (reader.IsDBNull(0)) continue;
|
|
var s = reader.GetString(0);
|
|
if (!string.IsNullOrWhiteSpace(s))
|
|
{
|
|
result.Add(s.Trim());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result.ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Active templates for employees registered on a specific Nedo machine.
|
|
/// Registration-based target selection: attendance_machine_user (is_deleted=0) drives which employees are allowed.
|
|
/// </summary>
|
|
public Dictionary<string, byte[]> GetActiveTemplatesForRegisteredMachine(MySqlConnection connection, string machineId)
|
|
{
|
|
var result = new Dictionary<string, byte[]>(StringComparer.OrdinalIgnoreCase);
|
|
if (string.IsNullOrWhiteSpace(machineId))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
const string query =
|
|
"SELECT t.serial_no, t.template " +
|
|
"FROM hrms.attendance_machine_face_templates t " +
|
|
"INNER JOIN hrms.attendance_machine_user u ON u.serial_number = t.serial_no " +
|
|
"WHERE t.is_active = 1 AND u.is_deleted = 0 AND u.machine_id = @MachineId";
|
|
|
|
using (var cmd = new MySqlCommand(query, connection))
|
|
{
|
|
cmd.CommandTimeout = 120;
|
|
cmd.Parameters.AddWithValue("@MachineId", machineId);
|
|
|
|
using (var reader = cmd.ExecuteReader())
|
|
{
|
|
while (reader.Read())
|
|
{
|
|
if (reader.IsDBNull(0)) continue;
|
|
var serial = reader.GetString(0);
|
|
if (string.IsNullOrWhiteSpace(serial)) continue;
|
|
|
|
byte[] blob = Array.Empty<byte>();
|
|
if (!reader.IsDBNull(1))
|
|
{
|
|
blob = (byte[])reader["template"];
|
|
}
|
|
|
|
result[serial.Trim()] = blob;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// For each serial_no, return machine_ids where the employee is registered (attendance_machine_user),
|
|
/// restricted to the allowedMachineIds list (scope) and to Nedo machines (exclude ZKTECO by machine_type).
|
|
/// </summary>
|
|
public Dictionary<string, List<string>> GetRegisteredTargetMachineIdsForSerialNos(
|
|
MySqlConnection connection,
|
|
IReadOnlyList<string> serialNos,
|
|
IReadOnlyList<string> allowedMachineIds)
|
|
{
|
|
var result = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
|
|
if (serialNos == null || serialNos.Count == 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
var allowedSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
if (allowedMachineIds != null)
|
|
{
|
|
foreach (var id in allowedMachineIds)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(id))
|
|
{
|
|
allowedSet.Add(id.Trim());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (allowedSet.Count == 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
const int serialChunkSize = 200;
|
|
var allowedList = allowedSet.ToList();
|
|
|
|
// Keep allowed-machine IN clause reasonably small.
|
|
const int allowedChunkSize = 100;
|
|
for (int offset = 0; offset < serialNos.Count; offset += serialChunkSize)
|
|
{
|
|
var serialChunk = serialNos.Skip(offset).Take(serialChunkSize).ToArray();
|
|
var serialParamNames = new List<string>(serialChunk.Length);
|
|
for (int i = 0; i < serialChunk.Length; i++)
|
|
{
|
|
serialParamNames.Add("@S" + i);
|
|
}
|
|
|
|
string serialInClause = string.Join(",", serialParamNames);
|
|
|
|
for (int ao = 0; ao < allowedList.Count; ao += allowedChunkSize)
|
|
{
|
|
var allowedChunk = allowedList.Skip(ao).Take(allowedChunkSize).ToArray();
|
|
var allowedParamNames = new List<string>(allowedChunk.Length);
|
|
for (int i = 0; i < allowedChunk.Length; i++)
|
|
{
|
|
allowedParamNames.Add("@A" + i);
|
|
}
|
|
string allowedInClause = string.Join(",", allowedParamNames);
|
|
|
|
const string query =
|
|
"SELECT u.serial_number, u.machine_id " +
|
|
"FROM hrms.attendance_machine_user u " +
|
|
"INNER JOIN hrms.attendance_machine m ON m.machine_id = u.machine_id " +
|
|
"WHERE u.is_deleted = 0 " +
|
|
"AND (m.machine_type IS NULL OR m.machine_type <> 'ZKTECO') " +
|
|
"AND u.serial_number IN ({SERIAL_IN}) " +
|
|
"AND u.machine_id IN ({ALLOWED_IN})";
|
|
|
|
string q = query
|
|
.Replace("{SERIAL_IN}", serialInClause)
|
|
.Replace("{ALLOWED_IN}", allowedInClause);
|
|
|
|
using (var cmd = new MySqlCommand())
|
|
{
|
|
cmd.Connection = connection;
|
|
cmd.CommandTimeout = 120;
|
|
string typeClause = MachineScope.BuildMachineTypeWhereClause(cmd, "m.machine_type", "mtreg" + offset + ao);
|
|
cmd.CommandText = q.Replace("AND (m.machine_type IS NULL OR m.machine_type <> 'ZKTECO')", "AND " + typeClause);
|
|
for (int i = 0; i < serialChunk.Length; i++)
|
|
{
|
|
cmd.Parameters.AddWithValue(serialParamNames[i], serialChunk[i]);
|
|
}
|
|
for (int i = 0; i < allowedChunk.Length; i++)
|
|
{
|
|
cmd.Parameters.AddWithValue(allowedParamNames[i], allowedChunk[i]);
|
|
}
|
|
|
|
using (var reader = cmd.ExecuteReader())
|
|
{
|
|
// temp set per serial to avoid duplicates from allowed-chunk partitioning
|
|
var serialToSet = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
while (reader.Read())
|
|
{
|
|
if (reader.IsDBNull(0) || reader.IsDBNull(1)) continue;
|
|
var serial = reader.GetString(0);
|
|
var mid = reader.GetString(1);
|
|
if (string.IsNullOrWhiteSpace(serial) || string.IsNullOrWhiteSpace(mid)) continue;
|
|
|
|
if (!serialToSet.TryGetValue(serial, out var set))
|
|
{
|
|
set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
serialToSet[serial] = set;
|
|
}
|
|
set.Add(mid.Trim());
|
|
}
|
|
|
|
foreach (var kvp in serialToSet)
|
|
{
|
|
if (!result.TryGetValue(kvp.Key, out var list))
|
|
{
|
|
list = new List<string>();
|
|
result[kvp.Key] = list;
|
|
}
|
|
|
|
foreach (var mid in kvp.Value)
|
|
{
|
|
if (!list.Contains(mid))
|
|
{
|
|
list.Add(mid);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|