uind-hikvision-attendance-m.../src/HikvisionAttendanceManager.App/Services/UserManagementService.cs

186 lines
7.7 KiB
C#

using HikvisionAttendanceManager.App.Models;
namespace HikvisionAttendanceManager.App.Services;
public sealed class UserManagementService(
HikvisionIsapiClient hikvision,
EmployeePhotoService photos,
AttendanceMachineUserRepository machineUsers,
OperationHistoryService history)
{
public static EmployeeSyncResult ValidateEmployee(HrmsEmployee employee, long siteId)
{
var result = new EmployeeSyncResult
{
Employee = employee,
EmployeeNumber = employee.SerialNumber,
EmployeeName = employee.Name,
Department = employee.DepartmentName,
OverallResult = "READY"
};
if (!employee.Active) { result.OverallResult = "SKIPPED"; result.Reason = "Employee inactive."; }
else if (employee.DepartmentId is null) { result.OverallResult = "SKIPPED"; result.Reason = "Department not found."; }
else if (!employee.DepartmentActive) { result.OverallResult = "SKIPPED"; result.Reason = "Department inactive."; }
else if (employee.LocationSiteId != siteId) { result.OverallResult = "SKIPPED"; result.Reason = "Wrong location site."; }
return result;
}
public async Task<EmployeeSyncResult> CreateUserWithFaceAsync(Device device, HrmsEmployee employee, long siteId,
IProgress<string>? progress, CancellationToken cancellationToken)
{
var validation = ValidateEmployee(employee, siteId);
if (validation.OverallResult == "SKIPPED") return validation;
var entry = new SyncHistoryEntry
{
Operation = SyncOperationKind.UserCreation,
TargetDevice = device.DisplayName,
Context = employee.SerialNumber + " — " + employee.Name,
Total = 1,
Status = "Running"
};
await history.AddAsync(entry, cancellationToken);
var result = new EmployeeSyncResult
{
Employee = employee,
EmployeeNumber = employee.SerialNumber,
EmployeeName = employee.Name,
Department = employee.DepartmentName
};
try
{
progress?.Report("Checking if user already exists…");
if (await hikvision.UserExistsAsync(device, employee.SerialNumber, cancellationToken))
{
result.UserStatus = "Already exists";
result.OverallResult = "ALREADY_EXISTS";
}
else
{
progress?.Report("Creating Hikvision UserInfo…");
var create = await hikvision.CreateUserAsync(device, employee, cancellationToken);
if (!create.Success)
{
result.UserStatus = "Failed";
result.OverallResult = "FAILED";
result.Reason = create.Reason;
await CompleteHistory(entry, result, cancellationToken);
return result;
}
result.UserStatus = "Created";
}
progress?.Report("Downloading employee photo…");
var photo = await photos.DownloadAndNormalizeAsync(employee, cancellationToken);
if (!photo.Success)
{
result.FaceStatus = "Failed";
result.OverallResult = "FAILED";
result.Reason = photo.Reason;
await CompleteHistory(entry, result, cancellationToken);
return result;
}
progress?.Report("Uploading face…");
var face = await hikvision.UploadFaceAsync(device, employee.SerialNumber, photo.Bytes, cancellationToken);
if (!face.Success)
{
result.FaceStatus = "Failed";
result.OverallResult = "FAILED";
result.Reason = face.Reason;
await CompleteHistory(entry, result, cancellationToken);
return result;
}
result.FaceStatus = "Uploaded";
progress?.Report("Verifying face enrollment…");
var verify = await hikvision.VerifyFaceAsync(device, employee.SerialNumber, cancellationToken);
result.VerificationStatus = verify.Success ? "Verified" : "Failed";
result.OverallResult = verify.Success ? "SUCCESS" : "FAILED";
result.Reason = verify.Reason;
if (verify.Success)
await machineUsers.UpsertAsync(device.MachineId, employee.SerialNumber, employee.Name, cancellationToken);
await CompleteHistory(entry, result, cancellationToken);
return result;
}
catch (Exception ex)
{
result.OverallResult = "FAILED";
result.Reason = ex is InvalidOperationException ? ex.Message : "Device unavailable or operation failed.";
await CompleteHistory(entry, result, cancellationToken);
return result;
}
}
public async Task<IReadOnlyList<EmployeeSyncResult>> DeleteUsersAsync(Device device, IReadOnlyList<string> employeeNumbers,
IProgress<string>? progress, CancellationToken cancellationToken)
{
var normalized = employeeNumbers.Where(e => !string.IsNullOrWhiteSpace(e)).Select(e => e.Trim()).Distinct(StringComparer.OrdinalIgnoreCase).ToList();
var entry = new SyncHistoryEntry
{
Operation = SyncOperationKind.UserDeletion,
TargetDevice = device.DisplayName,
Context = normalized.Count == 1 ? normalized[0] : $"{normalized.Count} employees",
Total = normalized.Count,
Status = "Running"
};
await history.AddAsync(entry, cancellationToken);
var results = new List<EmployeeSyncResult>();
if (normalized.Count == 0)
{
entry.Status = "Completed";
entry.CompletedAt = DateTime.Now;
await history.UpdateAsync(entry, cancellationToken);
return results;
}
progress?.Report("Deleting users on device…");
var batchResult = await hikvision.DeleteUsersAsync(device, normalized, cancellationToken);
foreach (var employeeNo in normalized)
{
if (batchResult.Success)
{
await machineUsers.MarkDeletedAsync(device.MachineId, employeeNo, cancellationToken);
results.Add(new EmployeeSyncResult
{
EmployeeNumber = employeeNo,
UserStatus = "Deleted",
OverallResult = "SUCCESS"
});
}
else
{
results.Add(new EmployeeSyncResult
{
EmployeeNumber = employeeNo,
UserStatus = "Failed",
OverallResult = "FAILED",
Reason = batchResult.Reason
});
}
}
entry.CompletedAt = DateTime.Now;
entry.Success = results.Count(r => r.OverallResult == "SUCCESS");
entry.Failed = results.Count(r => r.OverallResult == "FAILED");
entry.Status = entry.Failed > 0 ? "Completed with errors" : "Completed";
await history.UpdateAsync(entry, cancellationToken);
return results;
}
private async Task CompleteHistory(SyncHistoryEntry entry, EmployeeSyncResult result, CancellationToken cancellationToken)
{
entry.CompletedAt = DateTime.Now;
entry.Success = result.OverallResult is "SUCCESS" or "ALREADY_EXISTS" ? 1 : 0;
entry.Failed = result.OverallResult == "FAILED" ? 1 : 0;
entry.Skipped = result.OverallResult == "SKIPPED" ? 1 : 0;
entry.Status = result.OverallResult == "FAILED" ? "Failed" : "Completed";
await history.UpdateAsync(entry, cancellationToken);
}
}