186 lines
7.7 KiB
C#
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);
|
|
}
|
|
}
|