using System.Net;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using WorkCameraExport.Models;
using WorkCameraExport.Services.Interfaces;
namespace WorkCameraExport.Services
{
///
/// API 服务类 - 处理与后端服务器的所有 HTTP 通信
/// 实现 Token 认证、请求重试、Token 自动刷新
///
public class ApiService : IApiService
{
private readonly HttpClient _httpClient;
private readonly ILogService? _logService;
private string _baseUrl = "";
private string _token = "";
private DateTime _tokenExpireTime = DateTime.MinValue;
private string _refreshToken = "";
private bool _disposed;
// 配置常量
private const int MaxRetryCount = 3;
private const int RetryDelayMs = 1000;
private const int TokenRefreshThresholdMinutes = 5;
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
Converters = { new FlexibleDateTimeConverter() }
};
public ApiService(ILogService? logService = null)
{
_logService = logService;
_httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(30)
};
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("WorkCameraExport/2.0 (Windows; .NET)");
}
///
/// 用于测试的构造函数,允许注入 HttpClient
///
internal ApiService(HttpClient httpClient, ILogService? logService = null)
{
_httpClient = httpClient;
_logService = logService;
}
#region 配置方法
///
/// 设置服务器地址
///
public void SetBaseUrl(string baseUrl)
{
_baseUrl = baseUrl.TrimEnd('/');
}
///
/// 设置认证 Token
///
public void SetToken(string token)
{
_token = token;
if (!string.IsNullOrEmpty(token))
{
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
// 默认设置 Token 有效期为 2 小时
_tokenExpireTime = DateTime.Now.AddHours(2);
}
else
{
_httpClient.DefaultRequestHeaders.Authorization = null;
_tokenExpireTime = DateTime.MinValue;
}
}
///
/// 设置 Token 及其过期时间
///
public void SetToken(string token, DateTime expireTime)
{
_token = token;
_tokenExpireTime = expireTime;
if (!string.IsNullOrEmpty(token))
{
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
}
else
{
_httpClient.DefaultRequestHeaders.Authorization = null;
}
}
///
/// 设置刷新 Token
///
public void SetRefreshToken(string refreshToken)
{
_refreshToken = refreshToken;
}
///
/// 获取当前 Token
///
public string GetToken() => _token;
///
/// 获取 Token 过期时间
///
public DateTime GetTokenExpireTime() => _tokenExpireTime;
///
/// 检查是否已登录
///
public bool IsLoggedIn => !string.IsNullOrEmpty(_token);
///
/// 检查 Token 是否即将过期
///
public bool IsTokenExpiringSoon =>
_tokenExpireTime != DateTime.MinValue &&
_tokenExpireTime.Subtract(DateTime.Now).TotalMinutes < TokenRefreshThresholdMinutes;
#endregion
#region 认证相关
///
/// 获取验证码
///
public async Task<(bool Success, string Message, CaptchaResponse? Data)> GetCaptchaAsync()
{
try
{
var response = await ExecuteWithRetryAsync(() => GetAsync("/captchaImage"));
if (response.IsSuccess && response.Data != null)
{
return (true, "获取成功", response.Data);
}
return (false, response.Msg ?? "获取验证码失败", null);
}
catch (Exception ex)
{
_logService?.Error($"获取验证码异常: {ex.Message}", ex);
return (false, $"获取验证码异常: {ex.Message}", null);
}
}
///
/// 用户登录
///
public async Task<(bool Success, string Message, LoginResponse? Data)> LoginAsync(
string username, string password, string code = "", string uuid = "")
{
try
{
var request = new LoginRequest
{
Username = username,
Password = password,
Code = code,
Uuid = uuid
};
var url = $"{_baseUrl}/login";
var json = JsonSerializer.Serialize(request, JsonOptions);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var httpResponse = await _httpClient.PostAsync(url, content);
var responseContent = await httpResponse.Content.ReadAsStringAsync();
var response = JsonSerializer.Deserialize>(responseContent, JsonOptions);
if (response != null && response.IsSuccess && !string.IsNullOrEmpty(response.Data))
{
var token = response.Data;
SetToken(token);
_logService?.Info($"用户 {username} 登录成功");
return (true, "登录成功", new LoginResponse { Token = token });
}
_logService?.Warn($"用户 {username} 登录失败: {response?.Msg}");
return (false, response?.Msg ?? "登录失败", null);
}
catch (Exception ex)
{
_logService?.Error($"登录异常: {ex.Message}", ex);
return (false, $"登录异常: {ex.Message}", null);
}
}
///
/// 刷新 Token
///
public async Task RefreshTokenAsync()
{
try
{
if (string.IsNullOrEmpty(_refreshToken))
{
_logService?.Warn("无刷新 Token,无法刷新");
return false;
}
var response = await PostAsync("/refreshToken", new { refreshToken = _refreshToken });
if (response.IsSuccess && response.Data != null)
{
SetToken(response.Data.Token, response.Data.ExpireTime);
if (!string.IsNullOrEmpty(response.Data.RefreshToken))
{
_refreshToken = response.Data.RefreshToken;
}
_logService?.Info("Token 刷新成功");
return true;
}
_logService?.Warn($"Token 刷新失败: {response.Msg}");
return false;
}
catch (Exception ex)
{
_logService?.Error($"Token 刷新异常: {ex.Message}", ex);
return false;
}
}
///
/// 退出登录
///
public void Logout()
{
_logService?.Info("用户退出登录");
SetToken("");
_refreshToken = "";
}
///
/// 获取当前用户信息(用于验证 Token 有效性)
///
public async Task<(bool Success, string Message, UserInfo? Data)> GetCurrentUserAsync()
{
try
{
var response = await GetAsync("/system/user/profile");
if (response.IsSuccess && response.Data?.User != null)
{
return (true, "获取成功", new UserInfo
{
UserId = response.Data.User.UserId,
UserName = response.Data.User.UserName ?? "",
NickName = response.Data.User.NickName ?? ""
});
}
return (false, response.Msg ?? "获取用户信息失败", null);
}
catch (Exception ex)
{
_logService?.Error($"获取用户信息异常: {ex.Message}", ex);
return (false, $"获取用户信息异常: {ex.Message}", null);
}
}
#endregion
#region 统计接口
///
/// 获取统计信息
///
public async Task<(bool Success, string Message, StatisticsDto? Data)> GetStatisticsAsync()
{
try
{
await EnsureTokenValidAsync();
var response = await ExecuteWithRetryAsync(() => GetAsync("/api/workrecord/statistics"));
if (response.IsSuccess && response.Data != null)
{
return (true, "获取成功", response.Data);
}
return (false, response.Msg ?? "获取统计信息失败", null);
}
catch (Exception ex)
{
_logService?.Error($"获取统计信息异常: {ex.Message}", ex);
return (false, $"获取统计信息异常: {ex.Message}", null);
}
}
#endregion
#region 工作记录 CRUD
///
/// 查询工作记录列表
///
public async Task<(bool Success, string Message, PagedData? Data)> GetWorkRecordsAsync(
WorkRecordQueryDto query)
{
try
{
await EnsureTokenValidAsync();
var queryParams = BuildQueryString(query);
var response = await ExecuteWithRetryAsync(() =>
GetPagedAsync($"/business/CamWorkrecord/list?{queryParams}"));
if (response.IsSuccess && response.Data != null)
{
// 调试日志:记录反序列化后的 Workers 数据
foreach (var record in response.Data.Result.Take(3)) // 只记录前3条
{
var workersInfo = record.Workers != null
? $"Workers数量={record.Workers.Count}, 名称=[{string.Join(", ", record.Workers.Select(w => w.WorkerName ?? "null"))}]"
: "Workers=null";
_logService?.Info($"[反序列化] 记录ID={record.Id}, {workersInfo}");
}
return (true, "查询成功", response.Data);
}
return (false, response.Msg ?? "查询失败", null);
}
catch (Exception ex)
{
_logService?.Error($"查询工作记录异常: {ex.Message}", ex);
return (false, $"查询异常: {ex.Message}", null);
}
}
///
/// 获取单条工作记录
///
public async Task<(bool Success, string Message, WorkRecordDto? Data)> GetWorkRecordAsync(int id)
{
try
{
await EnsureTokenValidAsync();
var response = await ExecuteWithRetryAsync(() =>
GetAsync($"/business/CamWorkrecord/{id}"));
if (response.IsSuccess && response.Data != null)
{
return (true, "获取成功", response.Data);
}
return (false, response.Msg ?? "获取失败", null);
}
catch (Exception ex)
{
_logService?.Error($"获取工作记录异常: {ex.Message}", ex);
return (false, $"获取异常: {ex.Message}", null);
}
}
///
/// 新增工作记录
///
public async Task<(bool Success, string Message, int? Id)> AddWorkRecordAsync(WorkRecordSaveDto record)
{
try
{
await EnsureTokenValidAsync();
var response = await ExecuteWithRetryAsync(() =>
PostAsync("/business/CamWorkrecord", record));
if (response.IsSuccess)
{
_logService?.Info($"新增工作记录成功,ID: {response.Data}");
return (true, "新增成功", response.Data);
}
return (false, response.Msg ?? "新增失败", null);
}
catch (Exception ex)
{
_logService?.Error($"新增工作记录异常: {ex.Message}", ex);
return (false, $"新增异常: {ex.Message}", null);
}
}
///
/// 更新工作记录
///
public async Task<(bool Success, string Message)> UpdateWorkRecordAsync(WorkRecordSaveDto record)
{
try
{
await EnsureTokenValidAsync();
var response = await ExecuteWithRetryAsync(() =>
PutAsync