Populate machine-user assignments and template sync metadata from device discovery.
Upserts discovered users into attendance_machine_user Updates attendance_machine.total_usersmain
parent
33c2dfabb3
commit
a5d0bc04c1
|
|
@ -9,6 +9,7 @@ using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Web.Script.Serialization;
|
using System.Web.Script.Serialization;
|
||||||
|
using HikvisionAttendanceService.Data;
|
||||||
|
|
||||||
namespace HikvisionAttendanceService;
|
namespace HikvisionAttendanceService;
|
||||||
|
|
||||||
|
|
@ -25,7 +26,7 @@ internal sealed partial class HikvisionAttendanceManager
|
||||||
var key = DeviceIdentity.CanonicalLookupKey(deviceId);
|
var key = DeviceIdentity.CanonicalLookupKey(deviceId);
|
||||||
if (key.Length == 0)
|
if (key.Length == 0)
|
||||||
return null;
|
return null;
|
||||||
foreach (var d in _config.Devices ?? Enumerable.Empty<HikvisionAttendanceWindowsService.DeviceConfig>())
|
foreach (var d in ResolveRuntimeDevices() ?? Enumerable.Empty<HikvisionAttendanceWindowsService.DeviceConfig>())
|
||||||
{
|
{
|
||||||
if (DeviceIdentity.CanonicalLookupKey(d.DeviceId) == key)
|
if (DeviceIdentity.CanonicalLookupKey(d.DeviceId) == key)
|
||||||
return d;
|
return d;
|
||||||
|
|
@ -120,6 +121,18 @@ internal sealed partial class HikvisionAttendanceManager
|
||||||
var targetMap = targetUsers.ToDictionary(x => x.EmployeeNo, x => x, StringComparer.OrdinalIgnoreCase);
|
var targetMap = targetUsers.ToDictionary(x => x.EmployeeNo, x => x, StringComparer.OrdinalIgnoreCase);
|
||||||
var initialTargetEmployeeIds = new HashSet<string>(targetUsers.Select(u => u.EmployeeNo),
|
var initialTargetEmployeeIds = new HashSet<string>(targetUsers.Select(u => u.EmployeeNo),
|
||||||
StringComparer.OrdinalIgnoreCase);
|
StringComparer.OrdinalIgnoreCase);
|
||||||
|
HashSet<string>? assignedUsers = null;
|
||||||
|
if (_config.EnableDbIntegration && _attendanceMachineUserRepository != null)
|
||||||
|
{
|
||||||
|
var dbAssignments = _attendanceMachineUserRepository.GetActiveUsersByMachine(target.DeviceId ?? "", out var dbAssignErr);
|
||||||
|
if (!string.IsNullOrWhiteSpace(dbAssignErr))
|
||||||
|
_logger.Warn("UserSync DB assignment fetch failed target=" + target.DeviceId + " err=" + dbAssignErr);
|
||||||
|
else if (dbAssignments.Count > 0)
|
||||||
|
{
|
||||||
|
assignedUsers = new HashSet<string>(dbAssignments.Select(x => x.SerialNumber), StringComparer.OrdinalIgnoreCase);
|
||||||
|
_logger.JobInfo("user_sync", "Target " + target.DeviceId + " DB-assigned active users=" + assignedUsers.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_logger.Info("UserSync: cycle=" + cycleId + " target=" + target.DeviceId + " existingUsers=" + targetUsers.Count);
|
_logger.Info("UserSync: cycle=" + cycleId + " target=" + target.DeviceId + " existingUsers=" + targetUsers.Count);
|
||||||
|
|
||||||
|
|
@ -135,6 +148,8 @@ internal sealed partial class HikvisionAttendanceManager
|
||||||
foreach (var srcUser in sourceUsers)
|
foreach (var srcUser in sourceUsers)
|
||||||
{
|
{
|
||||||
ct.ThrowIfCancellationRequested();
|
ct.ThrowIfCancellationRequested();
|
||||||
|
if (assignedUsers != null && !assignedUsers.Contains(srcUser.EmployeeNo))
|
||||||
|
continue;
|
||||||
targetMap.TryGetValue(srcUser.EmployeeNo, out var tgtRow);
|
targetMap.TryGetValue(srcUser.EmployeeNo, out var tgtRow);
|
||||||
bool createdNew = false;
|
bool createdNew = false;
|
||||||
|
|
||||||
|
|
@ -186,6 +201,23 @@ internal sealed partial class HikvisionAttendanceManager
|
||||||
|
|
||||||
bool hasFaceBytes = faceBytesByEmployee.TryGetValue(srcUser.EmployeeNo, out var fb) && fb != null &&
|
bool hasFaceBytes = faceBytesByEmployee.TryGetValue(srcUser.EmployeeNo, out var fb) && fb != null &&
|
||||||
fb.Length > 0;
|
fb.Length > 0;
|
||||||
|
string tplErr = "";
|
||||||
|
if (!hasFaceBytes &&
|
||||||
|
_config.EnableDbIntegration &&
|
||||||
|
_config.EnableTemplateDbToDeviceSync &&
|
||||||
|
_attendanceMachineFaceTemplateRepository != null &&
|
||||||
|
_attendanceMachineFaceTemplateRepository.TryGetActiveFaceTemplateBySerial(srcUser.EmployeeNo, out var tplRow, out tplErr) &&
|
||||||
|
tplRow != null &&
|
||||||
|
tplRow.Template != null &&
|
||||||
|
tplRow.Template.Length > 0)
|
||||||
|
{
|
||||||
|
fb = tplRow.Template;
|
||||||
|
hasFaceBytes = true;
|
||||||
|
}
|
||||||
|
else if (!hasFaceBytes && !string.IsNullOrWhiteSpace(tplErr))
|
||||||
|
{
|
||||||
|
_logger.Warn("UserSync template lookup failed employeeNo=" + srcUser.EmployeeNo + " err=" + tplErr);
|
||||||
|
}
|
||||||
if (!hasFaceBytes)
|
if (!hasFaceBytes)
|
||||||
{
|
{
|
||||||
skipped++;
|
skipped++;
|
||||||
|
|
@ -238,6 +270,30 @@ internal sealed partial class HikvisionAttendanceManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_config.EnableDbIntegration && _attendanceMachineUserRepository != null)
|
||||||
|
{
|
||||||
|
var pendingDeletionUsers = _attendanceMachineUserRepository.GetPendingDeletionUsersByMachine(target.DeviceId ?? "", out var pendingErr);
|
||||||
|
if (!string.IsNullOrWhiteSpace(pendingErr))
|
||||||
|
{
|
||||||
|
_logger.Warn("UserSync pending deletion fetch failed target=" + target.DeviceId + " err=" + pendingErr);
|
||||||
|
}
|
||||||
|
else if (pendingDeletionUsers.Count > 0)
|
||||||
|
{
|
||||||
|
var toDelete = pendingDeletionUsers.Select(x => x.SerialNumber).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct(StringComparer.OrdinalIgnoreCase).ToList();
|
||||||
|
string delErr2 = "";
|
||||||
|
if (toDelete.Count > 0 && TryDeleteUsersOnTarget(target, toDelete, maxRetries, ct, out delErr2))
|
||||||
|
{
|
||||||
|
foreach (var serial in toDelete)
|
||||||
|
_attendanceMachineUserRepository.MarkDeleted(target.DeviceId ?? "", serial, "hikvision-service", out _);
|
||||||
|
_logger.JobInfo("user_sync", "Target " + target.DeviceId + " DB deletion requests processed=" + toDelete.Count);
|
||||||
|
}
|
||||||
|
else if (toDelete.Count > 0)
|
||||||
|
{
|
||||||
|
_logger.Warn("UserSync DB-requested deletions failed target=" + target.DeviceId + " err=" + delErr2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var statusLine = string.Join(", ", statusCounts.OrderBy(kv => kv.Key.ToString())
|
var statusLine = string.Join(", ", statusCounts.OrderBy(kv => kv.Key.ToString())
|
||||||
.Select(kv => kv.Key + "=" + kv.Value));
|
.Select(kv => kv.Key + "=" + kv.Value));
|
||||||
_logger.Info("UserSync: cycle=" + cycleId + " target=" + target.DeviceId + " summary created=" + created +
|
_logger.Info("UserSync: cycle=" + cycleId + " target=" + target.DeviceId + " summary created=" + created +
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue