diff --git a/HikvisionAttendanceManager.UserSync.cs b/HikvisionAttendanceManager.UserSync.cs index 1594b52..2481fbc 100644 --- a/HikvisionAttendanceManager.UserSync.cs +++ b/HikvisionAttendanceManager.UserSync.cs @@ -9,6 +9,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web.Script.Serialization; +using HikvisionAttendanceService.Data; namespace HikvisionAttendanceService; @@ -25,7 +26,7 @@ internal sealed partial class HikvisionAttendanceManager var key = DeviceIdentity.CanonicalLookupKey(deviceId); if (key.Length == 0) return null; - foreach (var d in _config.Devices ?? Enumerable.Empty()) + foreach (var d in ResolveRuntimeDevices() ?? Enumerable.Empty()) { if (DeviceIdentity.CanonicalLookupKey(d.DeviceId) == key) return d; @@ -120,6 +121,18 @@ internal sealed partial class HikvisionAttendanceManager var targetMap = targetUsers.ToDictionary(x => x.EmployeeNo, x => x, StringComparer.OrdinalIgnoreCase); var initialTargetEmployeeIds = new HashSet(targetUsers.Select(u => u.EmployeeNo), StringComparer.OrdinalIgnoreCase); + HashSet? 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(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); @@ -135,6 +148,8 @@ internal sealed partial class HikvisionAttendanceManager foreach (var srcUser in sourceUsers) { ct.ThrowIfCancellationRequested(); + if (assignedUsers != null && !assignedUsers.Contains(srcUser.EmployeeNo)) + continue; targetMap.TryGetValue(srcUser.EmployeeNo, out var tgtRow); bool createdNew = false; @@ -186,6 +201,23 @@ internal sealed partial class HikvisionAttendanceManager bool hasFaceBytes = faceBytesByEmployee.TryGetValue(srcUser.EmployeeNo, out var fb) && fb != null && 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) { 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()) .Select(kv => kv.Key + "=" + kv.Value)); _logger.Info("UserSync: cycle=" + cycleId + " target=" + target.DeviceId + " summary created=" + created +