using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using LitJson; public class MailManager : GameSystemManager { /// /// 邮件数据字典,存储所有邮件详情 /// Key: 邮件GUID(唯一标识) /// Value: 邮件详细数据(MailData结构体) /// Dictionary mailDataDict = new Dictionary(); public MailCategory nowMailCategory = MailCategory.Personal; public Redpoint parentRedpoint = new Redpoint(MainRedDot.MailRepoint); public Redpoint tabRedpoint0; public Redpoint tabRedpoint1; public event Action OnUpdateMailListEvent;// 更新邮件列表数据 public event Action OnUpdateMailStateChangeEvent;// 更新邮件状态变更 public readonly string dateFormat = "yyyy.MM.dd"; public string nowUuid = string.Empty; public int personalMailMaxLimitCount; public override void Init() { tabRedpoint0 = new Redpoint(MainRedDot.MailRepoint, GetTabRedpointId(MailCategory.Personal)); tabRedpoint1 = new Redpoint(MainRedDot.MailRepoint, GetTabRedpointId(MailCategory.Global)); DTC0102_tagCDBPlayer.beforePlayerDataInitializeEvent += OnBeforePlayerDataInitializeEvent; personalMailMaxLimitCount = int.Parse(FuncConfigConfig.Get("PersonalMail").Numerical1); } public override void Release() { DTC0102_tagCDBPlayer.beforePlayerDataInitializeEvent -= OnBeforePlayerDataInitializeEvent; } public void OnBeforePlayerDataInitializeEvent() { mailDataDict.Clear(); } public bool HasNoReadMail(MailCategory category) { var list = GetMailList(category); if (list.IsNullOrEmpty()) return false; foreach (var guid in list) { if (mailDataDict.TryGetValue(guid, out var mailData) && mailData.MailState == 1) return true; } return false; } public int GetTabRedpointId(MailCategory type) { return MainRedDot.MailRepoint * 10 + (int)type; } public void UpdatePersonalRedPoint() { if (tabRedpoint0 == null) return; tabRedpoint0.state = RedPointState.None; if (HasNoReadMail(MailCategory.Personal)) { tabRedpoint0.state = RedPointState.Simple; } } public void UpdateGlobalRedPoint() { if (tabRedpoint1 == null) return; tabRedpoint1.state = RedPointState.None; if (HasNoReadMail(MailCategory.Global)) { tabRedpoint1.state = RedPointState.Simple; } } /// /// 更新邮件红点提示状态 /// public void UpdateRedPoint() { UpdatePersonalRedPoint(); UpdateGlobalRedPoint(); } public bool TryGetMailData(string uuid, out MailData mailData) { return mailDataDict.TryGetValue(uuid, out mailData); } public string FormatCreateMailTime(DateTime createDateTime) { TimeSpan diff = TimeUtility.ServerNow - createDateTime; if (diff.TotalHours <= 24) { int hours = (int)Math.Floor(diff.TotalHours); return Language.Get("Mail05", hours); } else { return createDateTime.ToString(dateFormat); } } // 返回有效期天数(正数=剩余天数,负数=已过期天数) public float GetMailExpiryDays(DateTime createDateTime, int limitDays) { // 计算到期日当天的0点时间 DateTime expiryDate = createDateTime.Date.AddDays(limitDays); DateTime serverNow = TimeUtility.ServerNow; TimeSpan remainingTime = expiryDate - serverNow; return (float)remainingTime.TotalDays; } public string FormatMailExpiryDays(DateTime createDateTime, int limitDays) { string result = string.Empty; float days = GetMailExpiryDays(createDateTime, limitDays); // 防护:处理因客户端与服务器时间戳的微小差异,导致剩余时间略大于有效期上限的情况。 // 将计算出的剩余天数限制在邮件的有效期天数之内。 if (days > limitDays) { days = limitDays; } if (days > 1.0f) { // 大于1天:按向上取整的天数显示。 int daysRounded = (int)MathF.Ceiling(days); result = Language.Get("Mail07", daysRounded); } else if (days > 0.0f) { // 小于1天(但未过期):按精确的小时/分钟显示。 TimeSpan t = TimeSpan.FromDays(days); result = Language.Get("Mail12", StringUtility.Contact(" ", Language.Get("Mail13", t.Hours, t.Minutes))); } else { // (预留)已过期:按负数向下取整(向更小的负数取整,以获得已过期天数的整数绝对值) // MathF.Floor(-1.01f) -> -2.0f // MathF.Floor(-0.5f) -> -1.0f // float expiredDaysFloor = MathF.Floor(days); // int expiredDays = (int)expiredDaysFloor; //直接显示已过期 result = Language.Get("Mail11"); ; } return result; } public List GetMailList(MailCategory mailCategory) { var result = new List(); foreach (var kvp in mailDataDict) { if (kvp.Value.Category == mailCategory) { result.Add(kvp.Key); } } return result; } public int GetMailCount(MailCategory mailCategory) { var result = new List(); foreach (var kvp in mailDataDict) { if (kvp.Value.Category == mailCategory) { result.Add(kvp.Key); } } return result.Count; } public List GetSortMailScrList(MailCategory mailCategory) { List resList = GetMailList(mailCategory); return resList.OrderByDescending(guid => mailDataDict[guid].CreateDateTime).ToList(); } /// /// 更新邮件列表数据 /// /// 从服务器接收的邮件列表数据包 public void UpdateMailList(HA362_tagMCMailList vNetData) { if (vNetData == null) return; // 更新邮件数据字典 foreach (var mail in vNetData.MailList) { var category = vNetData.IsServerMail == 1 ? MailCategory.Global : MailCategory.Personal; var mailData = new MailData(); mailData.Category = category; mailData.GUID = mail.GUID; mailData.Type = mail.Type; mailData.CreateTime = mail.CreateTime; mailData.CreateDateTime = Convert.ToDateTime(UIHelper.GetTime(mail.CreateTime)); mailData.LimitDays = mail.LimitDays; mailData.Title = mail.Title; mailData.Text = mail.Text; mailData.MailState = mail.MailState; mailData.Items = mail.Items?.Select(i => new MailItemData { ItemID = i.ItemID, Count = i.Count, IsBind = i.IsBind, UserData = i.UserData }).ToList(); if (!mailData.Items.IsNullOrEmpty()) { mailData.Items = mailData.Items .OrderByDescending(item => ItemConfig.HasKey((int)item.ItemID) ? ItemConfig.Get((int)item.ItemID).ItemColor : 0) // 按品质降序,未知物品排在最后 .ThenBy(item => item.Count) // 品质相同按数量升序 .ToList(); } mailDataDict[mail.GUID] = mailData; } UpdateRedPoint(); OnUpdateMailListEvent?.Invoke(); } /// /// 更新邮件状态变更 /// /// 从服务器接收的邮件状态变更数据包 public void UpdateMailStateChange(HA363_tagMCMailStateChange vNetData) { if (vNetData == null || vNetData.MailList == null) return; foreach (var stateChange in vNetData.MailList) { if (mailDataDict.TryGetValue(stateChange.GUID, out var mailData)) { if (stateChange.MailState == 4) { mailDataDict.Remove(stateChange.GUID); continue; } mailData.MailState = stateChange.MailState; mailDataDict[stateChange.GUID] = mailData; } } UpdateRedPoint(); OnUpdateMailStateChangeEvent?.Invoke(); } /// /// 阅读邮件 /// /// 邮件GUID public void ReadMail(string GUID) { if (string.IsNullOrEmpty(GUID)) { Debug.LogError("阅读邮件GUID不能为空"); return; } SendRequestMail(GUID, 0); } /// /// 领取邮件 /// /// 邮件GUID,为空时批量领取所有邮件 public void ClaimMail(string GUID = null) { SendRequestMail(GUID, 1); } /// /// 删除邮件 /// /// 邮件GUID,为空时批量删除已领取或无物品的已读邮件 public void DeleteMail(string GUID = null) { SendRequestMail(GUID, 2); } /// /// 发送邮件请求到服务器 /// /// 邮件GUID /// 请求类型 0-设置已读,1-领取邮件,2-删除邮件 public void SendRequestMail(string GUID, byte ReqType) { CA537_tagCMRequestMail pack = new CA537_tagCMRequestMail(); pack.GUID = GUID; pack.ReqType = ReqType; GameNetSystem.Instance.SendInfo(pack); } //没有一个可领取的个人邮件 public bool IsCanHaveMail() { var personalMails = GetMailList(MailCategory.Personal); return personalMails.Any(guid => mailDataDict.TryGetValue(guid, out var mail) && mail.MailState != 3 && mail.Items != null && mail.Items.Count > 0 ); } } public enum MailCategory { Personal = 0, // 个人邮件 Global = 1 // 全服邮件 } public class MailItemData { public uint ItemID; //物品ID public uint Count; //数量 public byte IsBind; //是否绑定 public string UserData; //自定义数据 } public class MailData { public MailCategory Category; public string GUID; //邮件GUID public byte Type; //邮件类型,暂时默认0 public string CreateTime; //创建时间 public DateTime CreateDateTime;//创建时间 public byte LimitDays; //有效天数 public string Title; //标题 public string Text; //内容 public byte MailState; //邮件状态: 0-未知;1-未读;2-已读;3-已领; public List Items; //物品信息 public bool HasAward() { return Items != null && Items.Count > 0; } /// /// 判断是否为模板类型邮件 /// /// true-模板邮件, false-普通邮件 public bool IsTemplateMail() { if (string.IsNullOrEmpty(Title)) return false; return Title.StartsWith("") && Title.EndsWith(""); } /// /// 获取模板邮件的模板key /// /// 模板key,如果不是模板邮件返回空字符串 public string GetTemplateKey() { if (!IsTemplateMail()) return string.Empty; // 模板格式: key // 跳过开头的"" (3字符) // 取中间部分,总长度减去""和""共7字符 const int prefixLength = 3; // ""长度 const int suffixLength = 4; // ""长度 return Title.Substring(prefixLength, Title.Length - (prefixLength + suffixLength)); } /// /// 获取模板邮件的参数列表 /// /// 参数列表,如果不是模板邮件或参数为空则返回空列表 public List GetTemplateParams() { if (!IsTemplateMail() || string.IsNullOrEmpty(Text)) return new List(); try { List result = JsonMapper.ToObject>(Text); // 如果解析结果为null(例如Text的内容是 "null" 字符串),则返回一个空列表以防后续出错 return result ?? new List(); } catch (Exception ex) { Debug.LogError($"使用 LitJson 解析邮件参数失败。 Text: {Text}, 错误: {ex.Message}"); return new List(); } } }