HanvonAttendanceService/HanvonHttpApiClient.cs

984 lines
35 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web.Script.Serialization;
namespace HanvonF710XAttendanceService
{
/// <summary>
/// F710X / AI Biometric web API client (POST http://{ip}/api).
/// Auth: each request includes the device web UI password.
/// </summary>
internal sealed class HanvonHttpApiClient
{
private readonly string _baseUrl;
private readonly string _password;
private readonly int _timeoutMs;
private readonly JavaScriptSerializer _json = new JavaScriptSerializer { MaxJsonLength = int.MaxValue };
public HanvonHttpApiClient(string machineIp, int httpPort = 80, string password = null, int timeoutMs = 60000)
{
if (string.IsNullOrWhiteSpace(machineIp)) throw new ArgumentException("machineIp required");
int port = httpPort > 0 ? httpPort : 80;
_baseUrl = "http://" + machineIp.Trim() + (port == 80 ? "" : ":" + port) + "/api";
_password = password ?? MachineScope.GetDevicePassword() ?? "";
_timeoutMs = timeoutMs > 0 ? timeoutMs : 60000;
}
public static HanvonHttpApiClient ForMachine(AttendanceMachine machine)
{
int httpPort = GetIntSetting("DEVICE_HTTP_PORT", 80);
int timeoutMs = MachineUserDeleteSettings.GetHttpTimeoutMs();
return new HanvonHttpApiClient(machine.MachineIp, httpPort, MachineScope.GetDevicePassword(), timeoutMs);
}
public bool TryLogin(out string error)
{
error = null;
try
{
var resp = Post(new Dictionary<string, object>
{
{ "cmd", "login" },
{ "username", MachineScope.GetDeviceUsername() },
{ "password", _password },
{ "rememberMe", false }
});
if (IsSuccess(resp)) return true;
error = GetMsg(resp) ?? "login failed";
return false;
}
catch (Exception ex)
{
error = ex.Message;
return false;
}
}
public List<HttpLogRecord> GetLogs(DateTime? from, DateTime? to, out string error)
{
error = null;
var all = new List<HttpLogRecord>();
try
{
int index = 0;
for (int guard = 0; guard < 10000; guard++)
{
var req = new Dictionary<string, object>
{
{ "cmd", "getlog" },
{ "password", _password },
{ "index", index }
};
if (from.HasValue) req["from"] = from.Value.ToString("yyyy-MM-dd HH:mm:ss");
if (to.HasValue) req["to"] = to.Value.ToString("yyyy-MM-dd HH:mm:ss");
var resp = Post(req);
if (!IsSuccess(resp))
{
error = GetMsg(resp) ?? "getlog failed";
return all;
}
var batch = ParseLogRecords(resp);
all.AddRange(batch);
int count = GetInt(resp, "count");
int toIdx = GetInt(resp, "to");
if (count <= 0 || toIdx <= 0 || count <= toIdx || batch.Count == 0)
{
break;
}
index = toIdx + 1;
}
return all;
}
catch (Exception ex)
{
error = ex.Message;
return all;
}
}
public List<HttpUserRecord> GetUserList(out string error)
{
error = null;
var all = new List<HttpUserRecord>();
try
{
int stn = 1;
for (int guard = 0; guard < 10000; guard++)
{
var resp = Post(new Dictionary<string, object>
{
{ "cmd", "getuserlist" },
{ "password", _password },
{ "stn", stn }
});
if (!IsSuccess(resp))
{
error = GetMsg(resp) ?? "getuserlist failed";
return all;
}
var batch = ParseUserRecords(resp);
all.AddRange(batch);
int count = GetInt(resp, "count");
int toIdx = GetInt(resp, "to");
if (count <= 0 || batch.Count == 0 || (toIdx > 0 && toIdx >= count))
{
break;
}
stn = 0;
}
return all;
}
catch (Exception ex)
{
error = ex.Message;
return all;
}
}
public bool DeleteUsers(IEnumerable<string> enrollIds, out string error)
{
error = null;
try
{
var list = new List<object>();
foreach (var id in enrollIds)
{
if (string.IsNullOrWhiteSpace(id)) continue;
if (int.TryParse(id.Trim(), out int n)) list.Add(n);
else list.Add(id.Trim());
}
if (list.Count == 0) return true;
var resp = Post(new Dictionary<string, object>
{
{ "cmd", "deleteusers" },
{ "password", _password },
{ "list", list }
});
if (IsSuccess(resp)) return true;
error = GetMsg(resp) ?? "deleteusers failed";
return false;
}
catch (Exception ex)
{
error = ex.Message;
return false;
}
}
public bool CleanLog(out string error)
{
error = null;
try
{
var resp = Post(new Dictionary<string, object>
{
{ "cmd", "cleanlog" },
{ "password", _password }
});
if (IsSuccess(resp)) return true;
error = GetMsg(resp) ?? "cleanlog failed";
return false;
}
catch (Exception ex)
{
error = ex.Message;
return false;
}
}
public static string NormalizeFaceTemplatePayload(byte[] blob, out string pushMode)
{
pushMode = "record";
if (blob == null || blob.Length == 0) return null;
if (blob.Length >= 3 && blob[0] == 0xFF && blob[1] == 0xD8 && blob[2] == 0xFF)
{
pushMode = "face";
return Convert.ToBase64String(blob);
}
string text = Encoding.UTF8.GetString(blob).Trim();
if (string.IsNullOrWhiteSpace(text)) return null;
if (text.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
{
int comma = text.IndexOf(',');
if (comma >= 0) text = text.Substring(comma + 1);
}
text = text.Replace("\r", "").Replace("\n", "").Trim();
if (string.IsNullOrWhiteSpace(text)) return null;
try
{
byte[] decoded = Convert.FromBase64String(text);
if (decoded.Length >= 3 && decoded[0] == 0xFF && decoded[1] == 0xD8 && decoded[2] == 0xFF)
{
pushMode = "face";
}
}
catch
{
// Biometric templates from getuserinfo may not decode as standard base64.
}
return text;
}
public bool TryEnsureUser(int enrollId, string name, out string error)
{
error = null;
try
{
var resp = Post(new Dictionary<string, object>
{
{ "cmd", "setuserinfo" },
{ "password", _password },
{ "enrollid", enrollId },
{ "name", name ?? enrollId.ToString() },
{ "department", "" },
{ "shiftid", 1 },
{ "admin", 0 },
{ "pwd", 0 },
{ "card", 0 },
{ "zoneid", 0 },
{ "groupid", 0 },
{ "access_times", 0 },
{ "verifymode", 0 },
{ "birthday", "" },
{ "starttime", "" },
{ "endtime", "" },
{ "userprofile", "" }
});
if (IsSuccess(resp)) return true;
error = GetMsg(resp) ?? "setuserinfo failed";
return false;
}
catch (Exception ex)
{
error = ex.Message;
return false;
}
}
public bool TrySetFaceTemplateRecord(int enrollId, string templatePayload, string pushMode, out string error)
{
error = null;
if (string.IsNullOrWhiteSpace(templatePayload))
{
error = "template record is empty";
return false;
}
try
{
var payload = new Dictionary<string, object>
{
{ "cmd", "setuserinfo" },
{ "password", _password },
{ "enrollid", enrollId }
};
if (string.Equals(pushMode, "face", StringComparison.OrdinalIgnoreCase))
{
payload["face"] = templatePayload;
}
else
{
payload["backupnum"] = GetIntSetting("DEVICE_FACE_BACKUPNUM", 50);
payload["record"] = templatePayload;
}
var resp = Post(payload);
if (IsSuccess(resp)) return true;
error = GetMsg(resp) ?? "setuserinfo template push failed";
return false;
}
catch (Exception ex)
{
error = ex.Message;
return false;
}
}
public bool TrySetFacePhotoOnly(int enrollId, string faceBase64, out string error)
{
error = null;
if (string.IsNullOrWhiteSpace(faceBase64))
{
error = "face photo is empty";
return false;
}
try
{
var resp = Post(new Dictionary<string, object>
{
{ "cmd", "setuserinfo" },
{ "password", _password },
{ "enrollid", enrollId },
{ "face", faceBase64 }
});
if (IsSuccess(resp)) return true;
error = GetMsg(resp) ?? "setuserinfo face push failed";
return false;
}
catch (Exception ex)
{
error = ex.Message;
return false;
}
}
public bool TrySetUserWithFacePhoto(int enrollId, string name, string faceBase64, out string error)
{
error = null;
if (string.IsNullOrWhiteSpace(faceBase64))
{
error = "face photo is empty";
return false;
}
try
{
var resp = Post(new Dictionary<string, object>
{
{ "cmd", "setuserinfo" },
{ "password", _password },
{ "enrollid", enrollId },
{ "name", name ?? enrollId.ToString() },
{ "department", "" },
{ "shiftid", 1 },
{ "admin", 0 },
{ "pwd", 0 },
{ "card", 0 },
{ "zoneid", 0 },
{ "groupid", 0 },
{ "access_times", 0 },
{ "verifymode", 8 },
{ "birthday", "" },
{ "starttime", "" },
{ "endtime", "" },
{ "userprofile", "" },
{ "face", faceBase64 }
});
if (IsSuccess(resp)) return true;
error = GetMsg(resp) ?? "setuserinfo face push failed";
return false;
}
catch (Exception ex)
{
error = ex.Message;
return false;
}
}
public bool TryGetFaceRecord(int enrollId, out string recordBase64, out string name, out string error)
{
recordBase64 = null;
name = null;
error = null;
try
{
var resp = Post(new Dictionary<string, object>
{
{ "cmd", "getuserinfo" },
{ "password", _password },
{ "enrollid", enrollId },
{ "backupnum", GetIntSetting("DEVICE_FACE_BACKUPNUM", 50) }
});
if (!IsSuccess(resp))
{
error = GetMsg(resp) ?? "getuserinfo failed";
return false;
}
name = GetString(resp, "name");
recordBase64 = GetString(resp, "record");
if (string.IsNullOrWhiteSpace(recordBase64))
{
error = "no face/record payload";
return false;
}
return true;
}
catch (Exception ex)
{
error = ex.Message;
return false;
}
}
public bool TryGetUserProfile(int enrollId, out string faceflag, out string photourl, out string name, out string error)
{
faceflag = null;
photourl = null;
name = null;
error = null;
try
{
var resp = Post(new Dictionary<string, object>
{
{ "cmd", "getuserinfo" },
{ "password", _password },
{ "enrollid", enrollId }
});
if (!IsSuccess(resp))
{
error = GetMsg(resp) ?? "getuserinfo failed";
return false;
}
name = GetString(resp, "name");
faceflag = GetString(resp, "faceflag");
photourl = GetString(resp, "photourl");
return true;
}
catch (Exception ex)
{
error = ex.Message;
return false;
}
}
public static bool ProfileIndicatesFaceEnrolled(string faceflag, string photourl)
{
if (!string.IsNullOrWhiteSpace(photourl))
{
return true;
}
if (string.IsNullOrWhiteSpace(faceflag))
{
return false;
}
if (string.Equals(faceflag, "0", StringComparison.OrdinalIgnoreCase)
|| string.Equals(faceflag, "false", StringComparison.OrdinalIgnoreCase))
{
return false;
}
if (int.TryParse(faceflag.Trim(), out int flagValue))
{
return flagValue > 0;
}
return true;
}
public bool UserHasFacePhoto(int enrollId, out string faceflag, out string photourl, out string error)
{
faceflag = null;
photourl = null;
error = null;
if (!TryGetUserProfile(enrollId, out faceflag, out photourl, out _, out error))
{
return false;
}
if (ProfileIndicatesFaceEnrolled(faceflag, photourl))
{
return true;
}
error = "no face enrolled";
return false;
}
public bool TryUploadDbToDeviceTemplate(int enrollId, string name, DbToDeviceUploadPlan plan, out string error)
{
error = null;
if (plan == null || !plan.HasUploadPayload)
{
error = "template payload is empty";
return false;
}
int delayMs = GetIntSetting("TEMPLATE_DEVICE_DELAY_MS", 300);
if (string.Equals(plan.PushMode, "face", StringComparison.OrdinalIgnoreCase))
{
if (!TryEnsureUser(enrollId, name, out error))
{
return false;
}
if (delayMs > 0)
{
System.Threading.Thread.Sleep(delayMs);
}
return TrySetFacePhotoOnly(enrollId, plan.Payload, out error);
}
if (!TryEnsureUser(enrollId, name, out error))
{
return false;
}
if (delayMs > 0)
{
System.Threading.Thread.Sleep(delayMs);
}
if (!TrySetFaceTemplateRecord(enrollId, plan.Payload, plan.PushMode, out error))
{
return false;
}
return true;
}
public bool VerifyDbToDeviceTemplate(int enrollId, DbToDeviceUploadPlan plan, out string faceflag, out string photourl, out string error)
{
faceflag = null;
photourl = null;
error = null;
if (plan != null && plan.UseProfileVerification)
{
return UserHasFacePhoto(enrollId, out faceflag, out photourl, out error);
}
if (UserHasFaceTemplate(enrollId, out error))
{
return true;
}
return false;
}
public bool TryRestoreUserWithFaceTemplate(int enrollId, string name, string templatePayload, string pushMode, out string error)
{
error = null;
if (string.IsNullOrWhiteSpace(templatePayload))
{
error = "template record is empty";
return false;
}
if (!TryEnsureUser(enrollId, name, out error))
{
return false;
}
int delayMs = GetIntSetting("TEMPLATE_DEVICE_DELAY_MS", 300);
if (delayMs > 0)
{
System.Threading.Thread.Sleep(delayMs);
}
if (!TrySetFaceTemplateRecord(enrollId, templatePayload, pushMode, out error))
{
string altMode = string.Equals(pushMode, "face", StringComparison.OrdinalIgnoreCase) ? "record" : "face";
if (!TrySetFaceTemplateRecord(enrollId, templatePayload, altMode, out error))
{
return false;
}
}
return true;
}
public bool UserHasFaceTemplate(int enrollId, out string error)
{
return TryGetFaceRecord(enrollId, out _, out _, out error);
}
public HttpExchangeResult PostDetailed(Dictionary<string, object> payload)
{
var result = new HttpExchangeResult
{
Url = _baseUrl,
Method = "POST",
RequestContentType = "application/json; charset=utf-8"
};
string body = _json.Serialize(payload ?? new Dictionary<string, object>());
result.RequestBodySanitized = HanvonHttpDiagnostics.SanitizeJsonBody(body);
try
{
var req = (HttpWebRequest)WebRequest.Create(_baseUrl);
req.Method = "POST";
req.ContentType = result.RequestContentType;
req.Timeout = _timeoutMs;
req.ReadWriteTimeout = _timeoutMs;
req.KeepAlive = false;
byte[] bytes = Encoding.UTF8.GetBytes(body);
req.ContentLength = bytes.Length;
using (var stream = req.GetRequestStream())
{
stream.Write(bytes, 0, bytes.Length);
}
using (var resp = (HttpWebResponse)req.GetResponse())
using (var reader = new StreamReader(resp.GetResponseStream(), Encoding.UTF8))
{
result.StatusCode = (int)resp.StatusCode;
result.ResponseHeaders = FormatResponseHeaders(resp);
string text = reader.ReadToEnd();
result.ResponseBody = HanvonHttpDiagnostics.SanitizeJsonBody(text);
result.ParsedResponse = ParseResponseBody(text);
}
}
catch (WebException wex)
{
result.Error = wex.Message;
if (wex.Response is HttpWebResponse errResp)
{
result.StatusCode = (int)errResp.StatusCode;
result.ResponseHeaders = FormatResponseHeaders(errResp);
using (var reader = new StreamReader(errResp.GetResponseStream() ?? Stream.Null, Encoding.UTF8))
{
string text = reader.ReadToEnd();
result.ResponseBody = HanvonHttpDiagnostics.SanitizeJsonBody(text);
result.ParsedResponse = ParseResponseBody(text);
}
}
}
catch (Exception ex)
{
result.Error = ex.Message;
}
return result;
}
public void DiagnoseDbToDeviceEmployee(
int enrollId,
string empId,
string displayName,
string templatePayload,
string pushMode,
TextWriter logWriter)
{
if (logWriter == null) throw new ArgumentNullException(nameof(logWriter));
logWriter.WriteLine($"[DB_TO_DEVICE] DIAG emp={empId} enrollid={enrollId} target={_baseUrl}");
logWriter.WriteLine($"[DB_TO_DEVICE] DIAG template {HanvonHttpDiagnostics.DescribeTemplatePayload(templatePayload, pushMode)}");
var login = PostDetailed(new Dictionary<string, object>
{
{ "cmd", "login" },
{ "username", MachineScope.GetDeviceUsername() },
{ "password", _password },
{ "rememberMe", false }
});
HanvonHttpDiagnostics.WriteExchange(logWriter, "LOGIN", empId, login);
var listBefore = PostDetailed(new Dictionary<string, object>
{
{ "cmd", "getuserlist" },
{ "password", _password },
{ "stn", 1 }
});
HanvonHttpDiagnostics.WriteExchange(logWriter, "USERLIST_BEFORE", empId, listBefore);
var profileBefore = PostDetailed(new Dictionary<string, object>
{
{ "cmd", "getuserinfo" },
{ "password", _password },
{ "enrollid", enrollId }
});
HanvonHttpDiagnostics.WriteExchange(logWriter, "VERIFY_PROFILE_BEFORE", empId, profileBefore);
var recordBefore = PostDetailed(new Dictionary<string, object>
{
{ "cmd", "getuserinfo" },
{ "password", _password },
{ "enrollid", enrollId },
{ "backupnum", GetIntSetting("DEVICE_FACE_BACKUPNUM", 50) }
});
HanvonHttpDiagnostics.WriteExchange(logWriter, "VERIFY_RECORD_BEFORE", empId, recordBefore);
var ensurePayload = new Dictionary<string, object>
{
{ "cmd", "setuserinfo" },
{ "password", _password },
{ "enrollid", enrollId },
{ "name", displayName ?? enrollId.ToString() },
{ "department", "" },
{ "shiftid", 1 },
{ "admin", 0 },
{ "pwd", 0 },
{ "card", 0 },
{ "zoneid", 0 },
{ "groupid", 0 },
{ "access_times", 0 },
{ "verifymode", 0 },
{ "birthday", "" },
{ "starttime", "" },
{ "endtime", "" },
{ "userprofile", "" }
};
var ensure = PostDetailed(ensurePayload);
HanvonHttpDiagnostics.WriteExchange(logWriter, "SEND ensure_user setuserinfo", empId, ensure);
var templatePayloadReq = new Dictionary<string, object>
{
{ "cmd", "setuserinfo" },
{ "password", _password },
{ "enrollid", enrollId }
};
string templateField;
if (string.Equals(pushMode, "face", StringComparison.OrdinalIgnoreCase))
{
templatePayloadReq["face"] = templatePayload;
templateField = "face";
}
else
{
templatePayloadReq["backupnum"] = GetIntSetting("DEVICE_FACE_BACKUPNUM", 50);
templatePayloadReq["record"] = templatePayload;
templateField = "record";
}
logWriter.WriteLine($"[DB_TO_DEVICE] emp={empId} SEND template_field={templateField} enrollid={enrollId} employee_number={displayName}");
var push = PostDetailed(templatePayloadReq);
HanvonHttpDiagnostics.WriteExchange(logWriter, "SEND template setuserinfo", empId, push);
int delayMs = GetIntSetting("TEMPLATE_DEVICE_DELAY_MS", 300);
if (delayMs > 0) System.Threading.Thread.Sleep(delayMs);
var profileAfter = PostDetailed(new Dictionary<string, object>
{
{ "cmd", "getuserinfo" },
{ "password", _password },
{ "enrollid", enrollId }
});
HanvonHttpDiagnostics.WriteExchange(logWriter, "VERIFY profile getuserinfo (no backupnum)", empId, profileAfter);
var recordAfter = PostDetailed(new Dictionary<string, object>
{
{ "cmd", "getuserinfo" },
{ "password", _password },
{ "enrollid", enrollId },
{ "backupnum", GetIntSetting("DEVICE_FACE_BACKUPNUM", 50) }
});
HanvonHttpDiagnostics.WriteExchange(logWriter, "VERIFY record getuserinfo backupnum=50", empId, recordAfter);
var listAfter = PostDetailed(new Dictionary<string, object>
{
{ "cmd", "getuserlist" },
{ "password", _password },
{ "stn", 1 }
});
HanvonHttpDiagnostics.WriteExchange(logWriter, "USERLIST_AFTER", empId, listAfter);
var users = GetUserList(out string listErr);
var match = users.FirstOrDefault(u =>
string.Equals(u.DeviceUserId, empId, StringComparison.OrdinalIgnoreCase)
|| string.Equals(u.SerialNumber, empId, StringComparison.OrdinalIgnoreCase)
|| string.Equals(u.Name, empId, StringComparison.OrdinalIgnoreCase));
string recordValue = GetString(recordAfter.ParsedResponse, "record");
HanvonHttpDiagnostics.WriteResultSummary(
logWriter,
empId,
match != null,
match?.DeviceUserId,
match?.Name,
null,
IsSuccess(profileAfter.ParsedResponse),
GetString(profileAfter.ParsedResponse, "faceflag"),
GetString(profileAfter.ParsedResponse, "photourl"),
!string.IsNullOrWhiteSpace(recordValue),
recordValue?.Length ?? 0,
HanvonHttpDiagnostics.DescribeTemplatePayload(templatePayload, pushMode));
logWriter.WriteLine($"[DB_TO_DEVICE] emp={empId} SEND_OK_MEANS ensure_result={IsSuccess(ensure.ParsedResponse)} template_result={IsSuccess(push.ParsedResponse)} (Hanvon JSON result=true only, not proof of stored face)");
logWriter.WriteLine($"[DB_TO_DEVICE] emp={empId} VERIFY_FAIL_REASON={(string.IsNullOrWhiteSpace(recordValue) ? "getuserinfo backupnum=50 returned empty record field" : "record present")}");
if (!string.IsNullOrWhiteSpace(listErr))
{
logWriter.WriteLine($"[DB_TO_DEVICE] getuserlist_err={listErr}");
}
}
private Dictionary<string, object> Post(Dictionary<string, object> payload)
{
var detailed = PostDetailed(payload);
if (detailed.ParsedResponse != null && detailed.ParsedResponse.Count > 0)
{
return detailed.ParsedResponse;
}
if (!string.IsNullOrWhiteSpace(detailed.Error) && detailed.StatusCode == 0)
{
throw new IOException(detailed.Error);
}
return detailed.ParsedResponse ?? new Dictionary<string, object>();
}
private static Dictionary<string, object> ParseResponseBody(string text)
{
if (string.IsNullOrWhiteSpace(text))
{
return new Dictionary<string, object>();
}
try
{
return new JavaScriptSerializer { MaxJsonLength = int.MaxValue }
.Deserialize<Dictionary<string, object>>(text) ?? new Dictionary<string, object>();
}
catch
{
return new Dictionary<string, object>();
}
}
private static string FormatResponseHeaders(HttpWebResponse resp)
{
if (resp?.Headers == null) return "";
var pairs = new List<string>();
foreach (string key in resp.Headers.AllKeys)
{
pairs.Add(key + "=" + resp.Headers[key]);
}
return string.Join("; ", pairs);
}
private static bool IsSuccess(Dictionary<string, object> resp)
{
if (resp == null) return false;
if (!resp.TryGetValue("result", out object r) || r == null) return false;
if (r is bool b) return b;
return string.Equals(r.ToString(), "true", StringComparison.OrdinalIgnoreCase);
}
private static string GetMsg(Dictionary<string, object> resp)
{
return GetString(resp, "msg") ?? GetString(resp, "reason");
}
private static string GetString(Dictionary<string, object> resp, string key)
{
if (resp == null || !resp.TryGetValue(key, out object v) || v == null) return null;
return v.ToString();
}
private static int GetInt(Dictionary<string, object> resp, string key)
{
if (resp == null || !resp.TryGetValue(key, out object v) || v == null) return 0;
if (v is int i) return i;
if (v is long l) return (int)l;
if (v is decimal d) return (int)d;
int.TryParse(v.ToString(), out int n);
return n;
}
private static List<HttpLogRecord> ParseLogRecords(Dictionary<string, object> resp)
{
var list = new List<HttpLogRecord>();
if (resp == null || !resp.TryGetValue("record", out object rec) || rec == null) return list;
if (rec is ArrayList arr)
{
foreach (var item in arr) AddLog(list, item as Dictionary<string, object>);
}
else if (rec is object[] oa)
{
foreach (var item in oa) AddLog(list, item as Dictionary<string, object>);
}
return list;
}
private static void AddLog(List<HttpLogRecord> list, Dictionary<string, object> row)
{
if (row == null) return;
string enroll = GetString(row, "enrollid") ?? "";
string name = GetString(row, "name") ?? "";
string timeStr = GetString(row, "time") ?? "";
if (!DateTime.TryParse(timeStr, out DateTime time)) return;
string acNo = enroll.Trim();
if (string.IsNullOrWhiteSpace(acNo)) return;
list.Add(new HttpLogRecord
{
EnrollId = enroll.Trim(),
Name = name.Trim(),
AcNo = acNo,
CheckTime = time,
Mode = GetInt(row, "mode"),
InOut = GetInt(row, "inout"),
Event = GetInt(row, "event")
});
}
private static List<HttpUserRecord> ParseUserRecords(Dictionary<string, object> resp)
{
var list = new List<HttpUserRecord>();
if (resp == null || !resp.TryGetValue("record", out object rec) || rec == null) return list;
IEnumerable items = null;
if (rec is ArrayList arr) items = arr;
else if (rec is object[] oa) items = oa;
if (items == null) return list;
foreach (var item in items)
{
var row = item as Dictionary<string, object>;
if (row == null) continue;
string id = GetString(row, "id") ?? "";
string name = GetString(row, "name") ?? "";
string serial = !string.IsNullOrWhiteSpace(name) ? name.Trim() : id.Trim();
if (string.IsNullOrWhiteSpace(serial)) continue;
list.Add(new HttpUserRecord
{
DeviceUserId = id.Trim(),
Name = name.Trim(),
SerialNumber = serial
});
}
return list;
}
private static int GetIntSetting(string key, int defaultValue)
{
try
{
var raw = ConfigurationManager.AppSettings[key];
if (string.IsNullOrWhiteSpace(raw)) return defaultValue;
return int.TryParse(raw.Trim(), out var v) && v > 0 ? v : defaultValue;
}
catch { return defaultValue; }
}
}
internal sealed class HttpLogRecord
{
public string EnrollId { get; set; }
public string Name { get; set; }
public string AcNo { get; set; }
public DateTime CheckTime { get; set; }
public int Mode { get; set; }
public int InOut { get; set; }
public int Event { get; set; }
}
internal sealed class HttpUserRecord
{
public string DeviceUserId { get; set; }
public string Name { get; set; }
public string SerialNumber { get; set; }
}
internal static class DeviceProtocol
{
public static bool UseHttp()
{
var p = ConfigurationManager.AppSettings["DEVICE_PROTOCOL"] ?? "HTTP";
return string.Equals(p.Trim(), "HTTP", StringComparison.OrdinalIgnoreCase)
|| string.Equals(p.Trim(), "WEB", StringComparison.OrdinalIgnoreCase)
|| string.Equals(p.Trim(), "API", StringComparison.OrdinalIgnoreCase);
}
}
}