419 lines
17 KiB
C#
419 lines
17 KiB
C#
|
|
using Microsoft.EntityFrameworkCore;
|
|||
|
|
using FutureMailAPI.Data;
|
|||
|
|
using FutureMailAPI.Models;
|
|||
|
|
using FutureMailAPI.DTOs;
|
|||
|
|
using System.Text.Json;
|
|||
|
|
|
|||
|
|
namespace FutureMailAPI.Services
|
|||
|
|
{
|
|||
|
|
public class PersonalSpaceService : IPersonalSpaceService
|
|||
|
|
{
|
|||
|
|
private readonly FutureMailDbContext _context;
|
|||
|
|
private readonly ILogger<PersonalSpaceService> _logger;
|
|||
|
|
|
|||
|
|
public PersonalSpaceService(FutureMailDbContext context, ILogger<PersonalSpaceService> logger)
|
|||
|
|
{
|
|||
|
|
_context = context;
|
|||
|
|
_logger = logger;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public async Task<ApiResponse<TimelineResponseDto>> GetTimelineAsync(int userId, TimelineQueryDto query)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var response = new TimelineResponseDto();
|
|||
|
|
var timelineDict = new Dictionary<string, TimelineDateDto>();
|
|||
|
|
|
|||
|
|
// 获取发送的邮件
|
|||
|
|
if (query.Type == TimelineType.ALL || query.Type == TimelineType.SENT)
|
|||
|
|
{
|
|||
|
|
var sentMailsQuery = _context.SentMails
|
|||
|
|
.Where(m => m.SenderId == userId)
|
|||
|
|
.Include(m => m.Recipient)
|
|||
|
|
.AsQueryable();
|
|||
|
|
|
|||
|
|
if (query.StartDate.HasValue)
|
|||
|
|
{
|
|||
|
|
sentMailsQuery = sentMailsQuery.Where(m => m.SentAt >= query.StartDate.Value);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (query.EndDate.HasValue)
|
|||
|
|
{
|
|||
|
|
sentMailsQuery = sentMailsQuery.Where(m => m.SentAt <= query.EndDate.Value);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var sentMails = await sentMailsQuery
|
|||
|
|
.OrderBy(m => m.SentAt)
|
|||
|
|
.ToListAsync();
|
|||
|
|
|
|||
|
|
foreach (var mail in sentMails)
|
|||
|
|
{
|
|||
|
|
var dateKey = mail.SentAt.ToString("yyyy-MM-dd");
|
|||
|
|
|
|||
|
|
if (!timelineDict.ContainsKey(dateKey))
|
|||
|
|
{
|
|||
|
|
timelineDict[dateKey] = new TimelineDateDto
|
|||
|
|
{
|
|||
|
|
Date = dateKey,
|
|||
|
|
Events = new List<TimelineEventDto>()
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var recipientName = mail.Recipient?.Username ?? "自己";
|
|||
|
|
var recipientAvatar = mail.Recipient?.Avatar;
|
|||
|
|
|
|||
|
|
timelineDict[dateKey].Events.Add(new TimelineEventDto
|
|||
|
|
{
|
|||
|
|
Type = TimelineEventType.SENT,
|
|||
|
|
MailId = mail.Id,
|
|||
|
|
Title = mail.Title,
|
|||
|
|
Time = mail.SentAt.ToString("HH:mm"),
|
|||
|
|
WithUser = new UserInfoDto
|
|||
|
|
{
|
|||
|
|
UserId = mail.RecipientId ?? 0,
|
|||
|
|
Username = recipientName,
|
|||
|
|
Avatar = recipientAvatar
|
|||
|
|
},
|
|||
|
|
Emotion = AnalyzeMailEmotion(mail.Content)
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取接收的邮件
|
|||
|
|
if (query.Type == TimelineType.ALL || query.Type == TimelineType.RECEIVED)
|
|||
|
|
{
|
|||
|
|
var receivedMailsQuery = _context.ReceivedMails
|
|||
|
|
.Where(r => r.RecipientId == userId)
|
|||
|
|
.Include(r => r.SentMail)
|
|||
|
|
.ThenInclude(m => m.Sender)
|
|||
|
|
.AsQueryable();
|
|||
|
|
|
|||
|
|
if (query.StartDate.HasValue)
|
|||
|
|
{
|
|||
|
|
receivedMailsQuery = receivedMailsQuery.Where(r => r.ReceivedAt >= query.StartDate.Value);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (query.EndDate.HasValue)
|
|||
|
|
{
|
|||
|
|
receivedMailsQuery = receivedMailsQuery.Where(r => r.ReceivedAt <= query.EndDate.Value);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var receivedMails = await receivedMailsQuery
|
|||
|
|
.OrderBy(r => r.ReceivedAt)
|
|||
|
|
.ToListAsync();
|
|||
|
|
|
|||
|
|
foreach (var receivedMail in receivedMails)
|
|||
|
|
{
|
|||
|
|
var dateKey = receivedMail.ReceivedAt.ToString("yyyy-MM-dd");
|
|||
|
|
|
|||
|
|
if (!timelineDict.ContainsKey(dateKey))
|
|||
|
|
{
|
|||
|
|
timelineDict[dateKey] = new TimelineDateDto
|
|||
|
|
{
|
|||
|
|
Date = dateKey,
|
|||
|
|
Events = new List<TimelineEventDto>()
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var senderName = receivedMail.SentMail.Sender.Username;
|
|||
|
|
var senderAvatar = receivedMail.SentMail.Sender.Avatar;
|
|||
|
|
|
|||
|
|
timelineDict[dateKey].Events.Add(new TimelineEventDto
|
|||
|
|
{
|
|||
|
|
Type = TimelineEventType.RECEIVED,
|
|||
|
|
MailId = receivedMail.SentMailId,
|
|||
|
|
Title = receivedMail.SentMail.Title,
|
|||
|
|
Time = receivedMail.ReceivedAt.ToString("HH:mm"),
|
|||
|
|
WithUser = new UserInfoDto
|
|||
|
|
{
|
|||
|
|
UserId = receivedMail.SentMail.SenderId,
|
|||
|
|
Username = senderName,
|
|||
|
|
Avatar = senderAvatar
|
|||
|
|
},
|
|||
|
|
Emotion = AnalyzeMailEmotion(receivedMail.SentMail.Content)
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 对每个日期的事件按时间排序
|
|||
|
|
foreach (var dateEntry in timelineDict)
|
|||
|
|
{
|
|||
|
|
dateEntry.Value.Events = dateEntry.Value.Events
|
|||
|
|
.OrderBy(e => e.Time)
|
|||
|
|
.ToList();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 按日期排序
|
|||
|
|
response.Timeline = timelineDict
|
|||
|
|
.OrderBy(kvp => kvp.Key)
|
|||
|
|
.Select(kvp => kvp.Value)
|
|||
|
|
.ToList();
|
|||
|
|
|
|||
|
|
return ApiResponse<TimelineResponseDto>.SuccessResult(response);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_logger.LogError(ex, "获取时间线时发生错误");
|
|||
|
|
return ApiResponse<TimelineResponseDto>.ErrorResult("获取时间线失败");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public async Task<ApiResponse<StatisticsResponseDto>> GetStatisticsAsync(int userId)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var response = new StatisticsResponseDto();
|
|||
|
|
|
|||
|
|
// 获取发送的邮件统计
|
|||
|
|
var sentMails = await _context.SentMails
|
|||
|
|
.Where(m => m.SenderId == userId)
|
|||
|
|
.ToListAsync();
|
|||
|
|
|
|||
|
|
response.TotalSent = sentMails.Count;
|
|||
|
|
|
|||
|
|
// 获取接收的邮件统计
|
|||
|
|
var receivedMails = await _context.ReceivedMails
|
|||
|
|
.Where(r => r.RecipientId == userId)
|
|||
|
|
.Include(r => r.SentMail)
|
|||
|
|
.ThenInclude(m => m.Sender)
|
|||
|
|
.ToListAsync();
|
|||
|
|
|
|||
|
|
response.TotalReceived = receivedMails.Count;
|
|||
|
|
|
|||
|
|
// 计算时间旅行时长(天)
|
|||
|
|
if (sentMails.Any())
|
|||
|
|
{
|
|||
|
|
var earliestDelivery = sentMails.Min(m => m.DeliveryTime);
|
|||
|
|
var latestDelivery = sentMails.Max(m => m.DeliveryTime);
|
|||
|
|
response.TimeTravelDuration = (latestDelivery - earliestDelivery).Days;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 找出最频繁的收件人
|
|||
|
|
var recipientCounts = sentMails
|
|||
|
|
.Where(m => m.RecipientId.HasValue)
|
|||
|
|
.GroupBy(m => m.RecipientId)
|
|||
|
|
.Select(g => new { RecipientId = g.Key, Count = g.Count() })
|
|||
|
|
.OrderByDescending(g => g.Count)
|
|||
|
|
.FirstOrDefault();
|
|||
|
|
|
|||
|
|
if (recipientCounts != null)
|
|||
|
|
{
|
|||
|
|
var recipient = await _context.Users
|
|||
|
|
.FirstOrDefaultAsync(u => u.Id == recipientCounts.RecipientId);
|
|||
|
|
|
|||
|
|
response.MostFrequentRecipient = recipient?.Username ?? "未知用户";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 找出最常见的年份(投递年份)
|
|||
|
|
if (sentMails.Any())
|
|||
|
|
{
|
|||
|
|
var yearCounts = sentMails
|
|||
|
|
.GroupBy(m => m.DeliveryTime.Year)
|
|||
|
|
.Select(g => new { Year = g.Key, Count = g.Count() })
|
|||
|
|
.OrderByDescending(g => g.Count)
|
|||
|
|
.FirstOrDefault();
|
|||
|
|
|
|||
|
|
response.MostCommonYear = yearCounts?.Year ?? DateTime.Now.Year;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生成关键词云
|
|||
|
|
var allContents = sentMails.Select(m => m.Content).ToList();
|
|||
|
|
allContents.AddRange(receivedMails.Select(r => r.SentMail.Content));
|
|||
|
|
|
|||
|
|
response.KeywordCloud = GenerateKeywordCloud(allContents);
|
|||
|
|
|
|||
|
|
// 生成月度统计
|
|||
|
|
response.MonthlyStats = GenerateMonthlyStats(sentMails, receivedMails);
|
|||
|
|
|
|||
|
|
return ApiResponse<StatisticsResponseDto>.SuccessResult(response);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_logger.LogError(ex, "获取统计数据时发生错误");
|
|||
|
|
return ApiResponse<StatisticsResponseDto>.ErrorResult("获取统计数据失败");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public async Task<ApiResponse<SubscriptionResponseDto>> GetSubscriptionAsync(int userId)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 在实际应用中,这里会从数据库或订阅服务中获取用户的订阅信息
|
|||
|
|
// 目前我们使用模拟数据
|
|||
|
|
|
|||
|
|
var user = await _context.Users
|
|||
|
|
.FirstOrDefaultAsync(u => u.Id == userId);
|
|||
|
|
|
|||
|
|
if (user == null)
|
|||
|
|
{
|
|||
|
|
return ApiResponse<SubscriptionResponseDto>.ErrorResult("用户不存在");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 模拟订阅数据
|
|||
|
|
var response = new SubscriptionResponseDto
|
|||
|
|
{
|
|||
|
|
Plan = SubscriptionPlan.FREE, // 默认为免费计划
|
|||
|
|
RemainingMails = 10 - await _context.SentMails.CountAsync(m => m.SenderId == userId && m.SentAt.Month == DateTime.Now.Month),
|
|||
|
|
MaxAttachmentSize = 5 * 1024 * 1024, // 5MB
|
|||
|
|
Features = new SubscriptionFeaturesDto
|
|||
|
|
{
|
|||
|
|
AdvancedTriggers = false,
|
|||
|
|
CustomCapsules = false,
|
|||
|
|
AIAssistant = true,
|
|||
|
|
UnlimitedStorage = false,
|
|||
|
|
PriorityDelivery = false
|
|||
|
|
},
|
|||
|
|
ExpireDate = null // 免费计划没有过期时间
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 如果用户创建时间超过30天,可以升级为高级用户(模拟)
|
|||
|
|
if (user.CreatedAt.AddDays(30) < DateTime.UtcNow)
|
|||
|
|
{
|
|||
|
|
response.Plan = SubscriptionPlan.PREMIUM;
|
|||
|
|
response.RemainingMails = 100;
|
|||
|
|
response.MaxAttachmentSize = 50 * 1024 * 1024; // 50MB
|
|||
|
|
response.Features.AdvancedTriggers = true;
|
|||
|
|
response.Features.CustomCapsules = true;
|
|||
|
|
response.Features.UnlimitedStorage = true;
|
|||
|
|
response.ExpireDate = DateTime.UtcNow.AddMonths(1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return ApiResponse<SubscriptionResponseDto>.SuccessResult(response);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_logger.LogError(ex, "获取订阅信息时发生错误");
|
|||
|
|
return ApiResponse<SubscriptionResponseDto>.ErrorResult("获取订阅信息失败");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public async Task<ApiResponse<UserProfileResponseDto>> GetUserProfileAsync(int userId)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var user = await _context.Users
|
|||
|
|
.FirstOrDefaultAsync(u => u.Id == userId);
|
|||
|
|
|
|||
|
|
if (user == null)
|
|||
|
|
{
|
|||
|
|
return ApiResponse<UserProfileResponseDto>.ErrorResult("用户不存在");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取订阅信息
|
|||
|
|
var subscriptionResult = await GetSubscriptionAsync(userId);
|
|||
|
|
|
|||
|
|
var response = new UserProfileResponseDto
|
|||
|
|
{
|
|||
|
|
Id = user.Id,
|
|||
|
|
Username = user.Username,
|
|||
|
|
Email = user.Email,
|
|||
|
|
Nickname = user.Nickname,
|
|||
|
|
Avatar = user.Avatar,
|
|||
|
|
CreatedAt = user.CreatedAt,
|
|||
|
|
LastLoginAt = user.LastLoginAt,
|
|||
|
|
Subscription = subscriptionResult.Data ?? new SubscriptionResponseDto()
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return ApiResponse<UserProfileResponseDto>.SuccessResult(response);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_logger.LogError(ex, "获取用户资料时发生错误");
|
|||
|
|
return ApiResponse<UserProfileResponseDto>.ErrorResult("获取用户资料失败");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private string AnalyzeMailEmotion(string content)
|
|||
|
|
{
|
|||
|
|
// 简单的情感分析
|
|||
|
|
var positiveKeywords = new[] { "开心", "快乐", "爱", "美好", "成功", "希望", "感谢", "幸福" };
|
|||
|
|
var negativeKeywords = new[] { "悲伤", "难过", "失败", "痛苦", "失望", "愤怒", "焦虑", "恐惧" };
|
|||
|
|
|
|||
|
|
var positiveCount = positiveKeywords.Count(keyword => content.Contains(keyword));
|
|||
|
|
var negativeCount = negativeKeywords.Count(keyword => content.Contains(keyword));
|
|||
|
|
|
|||
|
|
if (positiveCount > negativeCount)
|
|||
|
|
return "积极";
|
|||
|
|
else if (negativeCount > positiveCount)
|
|||
|
|
return "消极";
|
|||
|
|
else
|
|||
|
|
return "中性";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private List<KeywordCloudDto> GenerateKeywordCloud(List<string> contents)
|
|||
|
|
{
|
|||
|
|
// 简单的关键词提取
|
|||
|
|
var allWords = new List<string>();
|
|||
|
|
|
|||
|
|
foreach (var content in contents)
|
|||
|
|
{
|
|||
|
|
var words = content.Split(new[] { ' ', ',', '.', '!', '?', ';', ':', ',', '。', '!', '?', ';', ':' }, StringSplitOptions.RemoveEmptyEntries);
|
|||
|
|
allWords.AddRange(words.Where(word => word.Length > 3));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var wordCounts = allWords
|
|||
|
|
.GroupBy(word => word)
|
|||
|
|
.Select(g => new { Word = g.Key, Count = g.Count() })
|
|||
|
|
.OrderByDescending(g => g.Count)
|
|||
|
|
.Take(20)
|
|||
|
|
.ToList();
|
|||
|
|
|
|||
|
|
var maxCount = wordCounts.FirstOrDefault()?.Count ?? 1;
|
|||
|
|
|
|||
|
|
return wordCounts.Select(w => new KeywordCloudDto
|
|||
|
|
{
|
|||
|
|
Word = w.Word,
|
|||
|
|
Count = w.Count,
|
|||
|
|
Size = (int)((double)w.Count / maxCount * 10) + 1 // 1-10的大小
|
|||
|
|
}).ToList();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private List<MonthlyStatsDto> GenerateMonthlyStats(List<SentMail> sentMails, List<ReceivedMail> receivedMails)
|
|||
|
|
{
|
|||
|
|
var monthlyStats = new Dictionary<string, MonthlyStatsDto>();
|
|||
|
|
|
|||
|
|
// 处理发送的邮件
|
|||
|
|
foreach (var mail in sentMails)
|
|||
|
|
{
|
|||
|
|
var monthKey = mail.SentAt.ToString("yyyy-MM");
|
|||
|
|
|
|||
|
|
if (!monthlyStats.ContainsKey(monthKey))
|
|||
|
|
{
|
|||
|
|
monthlyStats[monthKey] = new MonthlyStatsDto
|
|||
|
|
{
|
|||
|
|
Month = monthKey,
|
|||
|
|
Sent = 0,
|
|||
|
|
Received = 0
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
monthlyStats[monthKey].Sent++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理接收的邮件
|
|||
|
|
foreach (var receivedMail in receivedMails)
|
|||
|
|
{
|
|||
|
|
var monthKey = receivedMail.ReceivedAt.ToString("yyyy-MM");
|
|||
|
|
|
|||
|
|
if (!monthlyStats.ContainsKey(monthKey))
|
|||
|
|
{
|
|||
|
|
monthlyStats[monthKey] = new MonthlyStatsDto
|
|||
|
|
{
|
|||
|
|
Month = monthKey,
|
|||
|
|
Sent = 0,
|
|||
|
|
Received = 0
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
monthlyStats[monthKey].Received++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 按月份排序,只返回最近12个月的数据
|
|||
|
|
return monthlyStats
|
|||
|
|
.OrderBy(kvp => kvp.Key)
|
|||
|
|
.TakeLast(12)
|
|||
|
|
.Select(kvp => kvp.Value)
|
|||
|
|
.ToList();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|