using ToolHub.Models; using Microsoft.AspNetCore.Http; using System.Security.Claims; namespace ToolHub.Services; /// /// 工具基类服务,提供统计数据和用户访问记录功能 /// public abstract class BaseToolService { protected readonly IFreeSql _freeSql; protected readonly IHttpContextAccessor _httpContextAccessor; protected BaseToolService(IFreeSql freeSql, IHttpContextAccessor httpContextAccessor) { _freeSql = freeSql; _httpContextAccessor = httpContextAccessor; } /// /// 记录工具访问 /// protected async Task RecordToolAccessAsync(int toolId, string accessType = "view", int duration = 0) { try { var httpContext = _httpContextAccessor.HttpContext; if (httpContext == null) return; var userId = GetCurrentUserId(); var sessionId = GetSessionId(); var ipAddress = GetClientIpAddress(); var userAgent = GetUserAgent(); var referer = GetReferer(); var access = new UserToolAccess { UserId = userId, ToolId = toolId, SessionId = sessionId, IpAddress = ipAddress, UserAgent = userAgent, Referer = referer, AccessType = accessType, Duration = duration }; await _freeSql.Insert(access).ExecuteAffrowsAsync(); // 更新工具总访问量 await _freeSql.Update() .Where(t => t.Id == toolId) .Set(t => t.ViewCount + 1) .ExecuteAffrowsAsync(); // 更新日统计数据 await UpdateDailyStatisticsAsync(toolId, accessType); } catch (Exception ex) { // 记录错误但不影响主流程 Console.WriteLine($"记录工具访问失败: {ex.Message}"); } } /// /// 更新日统计数据 /// protected async Task UpdateDailyStatisticsAsync(int toolId, string accessType) { var today = DateTime.Today; var stats = await _freeSql.Select() .Where(s => s.ToolId == toolId && s.Date == today) .FirstAsync(); if (stats == null) { // 创建新的日统计记录 stats = new ToolStatistics { ToolId = toolId, Date = today }; await _freeSql.Insert(stats).ExecuteAffrowsAsync(); } // 更新统计数据 var updateQuery = _freeSql.Update() .Where(s => s.Id == stats.Id); switch (accessType.ToLower()) { case "view": updateQuery.Set(s => s.DailyViews + 1); break; case "favorite": updateQuery.Set(s => s.DailyFavorites + 1); break; case "share": updateQuery.Set(s => s.DailyShares + 1); break; case "download": updateQuery.Set(s => s.DailyDownloads + 1); break; } updateQuery.Set(s => s.UpdatedAt, DateTime.Now); await updateQuery.ExecuteAffrowsAsync(); } /// /// 获取工具统计数据 /// protected async Task GetToolStatisticsAsync(int toolId, DateTime date) { return await _freeSql.Select() .Where(s => s.ToolId == toolId && s.Date == date) .FirstAsync(); } /// /// 获取工具访问记录 /// protected async Task> GetToolAccessRecordsAsync(int toolId, DateTime? startDate = null, DateTime? endDate = null, int limit = 100) { var query = _freeSql.Select() .Include(ua => ua.User) .Where(ua => ua.ToolId == toolId); if (startDate.HasValue) query = query.Where(ua => ua.CreatedAt >= startDate.Value); if (endDate.HasValue) query = query.Where(ua => ua.CreatedAt <= endDate.Value.AddDays(1)); return await query .OrderByDescending(ua => ua.CreatedAt) .Take(limit) .ToListAsync(); } /// /// 获取用户访问的工具记录 /// protected async Task> GetUserToolAccessRecordsAsync(int userId, DateTime? startDate = null, DateTime? endDate = null, int limit = 100) { var query = _freeSql.Select() .Include(ua => ua.Tool) .Where(ua => ua.UserId == userId); if (startDate.HasValue) query = query.Where(ua => ua.CreatedAt >= startDate.Value); if (endDate.HasValue) query = query.Where(ua => ua.CreatedAt <= endDate.Value); return await query .OrderByDescending(ua => ua.CreatedAt) .Take(limit) .ToListAsync(); } /// /// 获取热门工具(基于访问量) /// protected async Task> GetPopularToolsAsync(int count = 10, DateTime? startDate = null) { var query = _freeSql.Select() .Include(t => t.Category) .Where(t => t.IsActive); if (startDate.HasValue) { // 基于指定日期后的访问量排序 var toolAccessData = _freeSql.Select() .Where(ua => ua.CreatedAt >= startDate.Value) .GroupBy(ua => ua.ToolId) .Select(g => new { ToolId = g.Key, Count = g.Count() }) .OrderByDescending(x => x.Count) .Take(count) .ToList(); var toolIds = toolAccessData.Select(x => x.ToolId).ToList(); if (toolIds.Any()) { query = query.Where(t => toolIds.Contains(t.Id)); } } else { // 基于总访问量排序 query = query.OrderByDescending(t => t.ViewCount); } return await query.Take(count).ToListAsync(); } /// /// 获取当前用户ID /// protected int? GetCurrentUserId() { var httpContext = _httpContextAccessor.HttpContext; if (httpContext?.User?.Identity?.IsAuthenticated == true) { var userIdClaim = httpContext.User.FindFirst(ClaimTypes.NameIdentifier); if (userIdClaim != null && int.TryParse(userIdClaim.Value, out int userId)) { return userId; } } return null; } /// /// 获取会话ID /// protected string? GetSessionId() { string sessionId = string.Empty; try { var httpContext = _httpContextAccessor.HttpContext; return httpContext?.Session?.Id; } catch (Exception ex) { Console.WriteLine($"获取会话ID失败: {ex.Message}"); return sessionId; } } /// /// 获取客户端IP地址 /// protected string? GetClientIpAddress() { var httpContext = _httpContextAccessor.HttpContext; if (httpContext == null) return null; // 尝试从各种头部获取真实IP var ip = httpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault() ?? httpContext.Request.Headers["X-Real-IP"].FirstOrDefault() ?? httpContext.Connection.RemoteIpAddress?.ToString(); return ip; } /// /// 获取用户代理 /// protected string? GetUserAgent() { var httpContext = _httpContextAccessor.HttpContext; return httpContext?.Request.Headers["User-Agent"].FirstOrDefault(); } /// /// 获取来源页面 /// protected string? GetReferer() { var httpContext = _httpContextAccessor.HttpContext; return httpContext?.Request.Headers["Referer"].FirstOrDefault(); } /// /// 计算平均停留时间 /// protected async Task UpdateAverageDurationAsync(int toolId) { var today = DateTime.Today; var avgDuration = await _freeSql.Select() .Where(ua => ua.ToolId == toolId && ua.CreatedAt >= today && ua.Duration > 0) .AvgAsync(ua => ua.Duration); if (avgDuration > 0) { await _freeSql.Update() .Where(s => s.ToolId == toolId && s.Date == today) .Set(s => s.AverageDuration, (decimal)avgDuration) .ExecuteAffrowsAsync(); } } /// /// 获取工具使用趋势数据 /// protected async Task> GetToolUsageTrendAsync(int toolId, int days = 30) { var endDate = DateTime.Today; var startDate = endDate.AddDays(-days + 1); var stats = await _freeSql.Select() .Where(s => s.ToolId == toolId && s.Date >= startDate && s.Date <= endDate) .OrderBy(s => s.Date) .ToListAsync(); var result = new List(); for (var date = startDate; date <= endDate; date = date.AddDays(1)) { var dayStats = stats.FirstOrDefault(s => s.Date == date); result.Add(new { Date = date.ToString("yyyy-MM-dd"), Views = dayStats?.DailyViews ?? 0, UniqueViews = dayStats?.DailyUniqueViews ?? 0, Favorites = dayStats?.DailyFavorites ?? 0, Shares = dayStats?.DailyShares ?? 0, Downloads = dayStats?.DailyDownloads ?? 0, AverageDuration = dayStats?.AverageDuration ?? 0 }); } return result; } }