feat: add DB_TO_DEVICE planning and employee selection
Resolve EmpIds / DepartmentIds / registered employees; keep SourceIp optional.main
parent
5424d0abb8
commit
7741523d25
|
|
@ -0,0 +1,88 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace HanvonF710XAttendanceService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Pure planning logic for DB_TO_DEVICE jobs (testable without device/DB I/O).
|
||||||
|
/// </summary>
|
||||||
|
internal static class DbToDevicePlanning
|
||||||
|
{
|
||||||
|
public const int TemplateLoadBatchSize = 50;
|
||||||
|
|
||||||
|
public static bool ShouldIgnoreSourceDevice(TemplateTransferMode mode)
|
||||||
|
{
|
||||||
|
return mode == TemplateTransferMode.DB_TO_DEVICE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<string> NormalizeEmployeeIds(IEnumerable<string> employeeIds)
|
||||||
|
{
|
||||||
|
if (employeeIds == null)
|
||||||
|
{
|
||||||
|
return new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return employeeIds
|
||||||
|
.Where(id => !string.IsNullOrWhiteSpace(id))
|
||||||
|
.Select(id => id.Trim())
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves which employee serial numbers should be synced from HRMS DB to target devices.
|
||||||
|
/// </summary>
|
||||||
|
public static List<string> ResolveEmployeeIds(
|
||||||
|
TemplateTransferJob job,
|
||||||
|
Func<IReadOnlyList<string>, List<string>> loadByDepartmentIds,
|
||||||
|
Func<List<string>> loadRegisteredOnTargets)
|
||||||
|
{
|
||||||
|
if (job == null)
|
||||||
|
{
|
||||||
|
return new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (job.EmpIds != null && job.EmpIds.Count > 0)
|
||||||
|
{
|
||||||
|
return NormalizeEmployeeIds(job.EmpIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (job.DepartmentIds != null && job.DepartmentIds.Count > 0)
|
||||||
|
{
|
||||||
|
if (loadByDepartmentIds != null)
|
||||||
|
{
|
||||||
|
var fromDepartments = loadByDepartmentIds(job.DepartmentIds);
|
||||||
|
if (fromDepartments != null && fromDepartments.Count > 0)
|
||||||
|
{
|
||||||
|
return NormalizeEmployeeIds(fromDepartments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NormalizeEmployeeIds(loadRegisteredOnTargets != null ? loadRegisteredOnTargets() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int ComputeTemplateLoadBatchCount(int employeeCount)
|
||||||
|
{
|
||||||
|
if (employeeCount <= 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (employeeCount + TemplateLoadBatchSize - 1) / TemplateLoadBatchSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int ComputeLastTemplateLoadBatchSize(int employeeCount)
|
||||||
|
{
|
||||||
|
if (employeeCount <= 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int remainder = employeeCount % TemplateLoadBatchSize;
|
||||||
|
return remainder == 0 ? TemplateLoadBatchSize : remainder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace HanvonF710XAttendanceService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Lightweight unit tests for DB_TO_DEVICE planning logic (no MSTest dependency).
|
||||||
|
/// </summary>
|
||||||
|
internal static class DbToDevicePlanningTests
|
||||||
|
{
|
||||||
|
public static int RunAll()
|
||||||
|
{
|
||||||
|
int failed = 0;
|
||||||
|
failed += Run("1 DB_TO_DEVICE ignores source device", TestDbToDeviceIgnoresSource);
|
||||||
|
failed += Run("2 DEVICE_TO_DB requires source device", TestDeviceToDbRequiresSource);
|
||||||
|
failed += Run("3 DB_TO_DEVICE allows empty SourceIp planning", TestDbToDeviceAllowsEmptySourceIp);
|
||||||
|
failed += Run("4 EmpIds only selection", TestEmpIdsFilter);
|
||||||
|
failed += Run("5 DepartmentIds selection", TestDepartmentIdsFallback);
|
||||||
|
failed += Run("6 empty EmpIds uses registered employees", TestEmptyEmpIdsUsesRegistered);
|
||||||
|
failed += Run("7 normalize employee ids", TestNormalizeEmployeeIds);
|
||||||
|
failed += Run("8 DEVICE_TO_DEVICE requires source device", TestDeviceToDeviceRequiresSource);
|
||||||
|
failed += Run("9 template load batches for 673 employees", TestTemplateLoadBatchCount673);
|
||||||
|
return failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int Run(string name, Func<bool> test)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!test())
|
||||||
|
{
|
||||||
|
Console.WriteLine("FAIL: " + name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
Console.WriteLine("PASS: " + name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("FAIL: " + name + " err=" + ex.Message);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TestDbToDeviceIgnoresSource()
|
||||||
|
{
|
||||||
|
return DbToDevicePlanning.ShouldIgnoreSourceDevice(TemplateTransferMode.DB_TO_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TestDeviceToDbRequiresSource()
|
||||||
|
{
|
||||||
|
return !DbToDevicePlanning.ShouldIgnoreSourceDevice(TemplateTransferMode.DEVICE_TO_DB);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TestDeviceToDeviceRequiresSource()
|
||||||
|
{
|
||||||
|
return !DbToDevicePlanning.ShouldIgnoreSourceDevice(TemplateTransferMode.DEVICE_TO_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TestDbToDeviceAllowsEmptySourceIp()
|
||||||
|
{
|
||||||
|
var job = new TemplateTransferJob("1", true, "", new List<string> { "192.168.90.223" },
|
||||||
|
new List<string> { "5" }, null);
|
||||||
|
var ids = DbToDevicePlanning.ResolveEmployeeIds(job, _ => new List<string>(), () => new List<string> { "5" });
|
||||||
|
return ids.Count == 1 && ids[0] == "5";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TestOtherModesRequireSource()
|
||||||
|
{
|
||||||
|
return TestDeviceToDbRequiresSource() && TestDeviceToDeviceRequiresSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TestEmpIdsFilter()
|
||||||
|
{
|
||||||
|
var job = new TemplateTransferJob("1", true, "10.0.1.3", new List<string> { "192.168.90.223" },
|
||||||
|
new List<string> { "5", "55", "96" }, null);
|
||||||
|
|
||||||
|
var ids = DbToDevicePlanning.ResolveEmployeeIds(
|
||||||
|
job,
|
||||||
|
_ => new List<string> { "999" },
|
||||||
|
() => new List<string> { "999" });
|
||||||
|
|
||||||
|
return ids.Count == 3
|
||||||
|
&& ids.Contains("5")
|
||||||
|
&& ids.Contains("55")
|
||||||
|
&& ids.Contains("96");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TestDepartmentIdsFallback()
|
||||||
|
{
|
||||||
|
var job = new TemplateTransferJob("1", true, "", new List<string> { "192.168.90.223" },
|
||||||
|
null, new List<string> { "10", "20" });
|
||||||
|
|
||||||
|
bool deptCalled = false;
|
||||||
|
var ids = DbToDevicePlanning.ResolveEmployeeIds(
|
||||||
|
job,
|
||||||
|
departmentIds =>
|
||||||
|
{
|
||||||
|
deptCalled = true;
|
||||||
|
return new List<string> { "100", "200" };
|
||||||
|
},
|
||||||
|
() => new List<string> { "300" });
|
||||||
|
|
||||||
|
return deptCalled && ids.Count == 2 && ids.Contains("100") && ids.Contains("200");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TestEmptyEmpIdsUsesRegistered()
|
||||||
|
{
|
||||||
|
var job = new TemplateTransferJob("1", true, "", new List<string> { "192.168.90.223" },
|
||||||
|
null, null);
|
||||||
|
|
||||||
|
var ids = DbToDevicePlanning.ResolveEmployeeIds(
|
||||||
|
job,
|
||||||
|
_ => new List<string>(),
|
||||||
|
() => new List<string> { "5", "55" });
|
||||||
|
|
||||||
|
return ids.Count == 2 && ids.Contains("5") && ids.Contains("55");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TestNormalizeEmployeeIds()
|
||||||
|
{
|
||||||
|
var ids = DbToDevicePlanning.NormalizeEmployeeIds(new[] { " 5 ", "5", "", null, "96" });
|
||||||
|
return ids.Count == 2 && ids[0] == "5" && ids[1] == "96";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TestTemplateLoadBatchCount673()
|
||||||
|
{
|
||||||
|
return DbToDevicePlanning.ComputeTemplateLoadBatchCount(673) == 14
|
||||||
|
&& DbToDevicePlanning.ComputeLastTemplateLoadBatchSize(673) == 23;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -97,7 +97,7 @@ namespace HanvonF710XAttendanceService
|
||||||
var enabled = TryParseBool((string)jobEl.Attribute("Enabled"), out var jEnabled) && jEnabled;
|
var enabled = TryParseBool((string)jobEl.Attribute("Enabled"), out var jEnabled) && jEnabled;
|
||||||
var sourceIp = (string)jobEl.Attribute("SourceIp") ?? (string)jobEl.Attribute("SourceIP") ?? "";
|
var sourceIp = (string)jobEl.Attribute("SourceIp") ?? (string)jobEl.Attribute("SourceIP") ?? "";
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(id) || string.IsNullOrWhiteSpace(sourceIp))
|
if (string.IsNullOrWhiteSpace(id))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -124,7 +124,20 @@ namespace HanvonF710XAttendanceService
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
ttConfig.Jobs.Add(new TemplateTransferJob(id.Trim(), enabled, sourceIp.Trim(), targets, empIds));
|
var departmentIdsAttr = (string)jobEl.Attribute("DepartmentIds")
|
||||||
|
?? (string)jobEl.Attribute("DepartmentIDs")
|
||||||
|
?? (string)jobEl.Attribute("DeptIds");
|
||||||
|
List<string> departmentIds = null;
|
||||||
|
if (!string.IsNullOrWhiteSpace(departmentIdsAttr))
|
||||||
|
{
|
||||||
|
departmentIds = departmentIdsAttr
|
||||||
|
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(v => v.Trim())
|
||||||
|
.Where(v => v.Length > 0)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
ttConfig.Jobs.Add(new TemplateTransferJob(id.Trim(), enabled, sourceIp.Trim(), targets, empIds, departmentIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
parsed.TemplateTransfer = ttConfig;
|
parsed.TemplateTransfer = ttConfig;
|
||||||
|
|
@ -188,13 +201,14 @@ namespace HanvonF710XAttendanceService
|
||||||
|
|
||||||
internal sealed class TemplateTransferJob
|
internal sealed class TemplateTransferJob
|
||||||
{
|
{
|
||||||
public TemplateTransferJob(string id, bool enabled, string sourceIp, List<string> targetIps, List<string> empIds)
|
public TemplateTransferJob(string id, bool enabled, string sourceIp, List<string> targetIps, List<string> empIds, List<string> departmentIds = null)
|
||||||
{
|
{
|
||||||
Id = id ?? "";
|
Id = id ?? "";
|
||||||
Enabled = enabled;
|
Enabled = enabled;
|
||||||
SourceIp = sourceIp ?? "";
|
SourceIp = sourceIp ?? "";
|
||||||
TargetIps = targetIps ?? new List<string>();
|
TargetIps = targetIps ?? new List<string>();
|
||||||
EmpIds = empIds ?? new List<string>();
|
EmpIds = empIds ?? new List<string>();
|
||||||
|
DepartmentIds = departmentIds ?? new List<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Id { get; }
|
public string Id { get; }
|
||||||
|
|
@ -202,6 +216,7 @@ namespace HanvonF710XAttendanceService
|
||||||
public string SourceIp { get; }
|
public string SourceIp { get; }
|
||||||
public List<string> TargetIps { get; }
|
public List<string> TargetIps { get; }
|
||||||
public List<string> EmpIds { get; }
|
public List<string> EmpIds { get; }
|
||||||
|
public List<string> DepartmentIds { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue