HanvonAttendanceService/HanvonHttpApiClient.cs

397 lines
14 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
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);
return new HanvonHttpApiClient(machine.MachineIp, httpPort, MachineScope.GetDevicePassword());
}
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 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;
}
}
private Dictionary<string, object> Post(Dictionary<string, object> payload)
{
string body = _json.Serialize(payload);
var req = (HttpWebRequest)WebRequest.Create(_baseUrl);
req.Method = "POST";
req.ContentType = "application/json; charset=utf-8";
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))
{
string text = reader.ReadToEnd();
if (string.IsNullOrWhiteSpace(text))
{
return new Dictionary<string, object>();
}
return _json.Deserialize<Dictionary<string, object>>(text)
?? new Dictionary<string, object>();
}
}
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 = !string.IsNullOrWhiteSpace(name) ? name.Trim() : enroll.Trim();
if (string.IsNullOrWhiteSpace(acNo)) return;
list.Add(new HttpLogRecord
{
EnrollId = enroll,
Name = name,
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);
}
}
}