using System; 
 | 
using System.Collections.Generic; 
 | 
using System.Linq; 
 | 
using UnityEngine; 
 | 
using LitJson; 
 | 
  
 | 
  
 | 
public class MailManager : GameSystemManager<MailManager> 
 | 
{ 
 | 
    /// <summary> 
 | 
    /// 邮件数据字典,存储所有邮件详情 
 | 
    /// Key: 邮件GUID(唯一标识) 
 | 
    /// Value: 邮件详细数据(MailData结构体) 
 | 
    /// </summary> 
 | 
    Dictionary<string, MailData> mailDataDict = new Dictionary<string, MailData>(); 
 | 
    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; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    /// <summary> 
 | 
    /// 更新邮件红点提示状态 
 | 
    /// </summary> 
 | 
    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<string> GetMailList(MailCategory mailCategory) 
 | 
    { 
 | 
        var result = new List<string>(); 
 | 
        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<string>(); 
 | 
        foreach (var kvp in mailDataDict) 
 | 
        { 
 | 
            if (kvp.Value.Category == mailCategory) 
 | 
            { 
 | 
                result.Add(kvp.Key); 
 | 
            } 
 | 
        } 
 | 
        return result.Count; 
 | 
    } 
 | 
  
 | 
  
 | 
    public List<string> GetSortMailScrList(MailCategory mailCategory) 
 | 
    { 
 | 
        List<string> resList = GetMailList(mailCategory); 
 | 
        return resList.OrderByDescending(guid => mailDataDict[guid].CreateDateTime).ToList(); 
 | 
    } 
 | 
  
 | 
    /// <summary> 
 | 
    /// 更新邮件列表数据 
 | 
    /// </summary> 
 | 
    /// <param name="vNetData">从服务器接收的邮件列表数据包</param> 
 | 
    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(); 
 | 
    } 
 | 
  
 | 
    /// <summary> 
 | 
    /// 更新邮件状态变更 
 | 
    /// </summary> 
 | 
    /// <param name="vNetData">从服务器接收的邮件状态变更数据包</param> 
 | 
    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(); 
 | 
    } 
 | 
  
 | 
  
 | 
    /// <summary> 
 | 
    /// 阅读邮件 
 | 
    /// </summary> 
 | 
    /// <param name="GUID">邮件GUID</param> 
 | 
    public void ReadMail(string GUID) 
 | 
    { 
 | 
        if (string.IsNullOrEmpty(GUID)) 
 | 
        { 
 | 
            Debug.LogError("阅读邮件GUID不能为空"); 
 | 
            return; 
 | 
        } 
 | 
        SendRequestMail(GUID, 0); 
 | 
    } 
 | 
  
 | 
    /// <summary> 
 | 
    /// 领取邮件 
 | 
    /// </summary> 
 | 
    /// <param name="GUID">邮件GUID,为空时批量领取所有邮件</param> 
 | 
    public void ClaimMail(string GUID = null) 
 | 
    { 
 | 
        SendRequestMail(GUID, 1); 
 | 
    } 
 | 
  
 | 
    /// <summary> 
 | 
    /// 删除邮件 
 | 
    /// </summary> 
 | 
    /// <param name="GUID">邮件GUID,为空时批量删除已领取或无物品的已读邮件</param> 
 | 
    public void DeleteMail(string GUID = null) 
 | 
    { 
 | 
        SendRequestMail(GUID, 2); 
 | 
    } 
 | 
  
 | 
    /// <summary> 
 | 
    /// 发送邮件请求到服务器 
 | 
    /// </summary> 
 | 
    /// <param name="GUID">邮件GUID</param> 
 | 
    /// <param name="ReqType">请求类型 0-设置已读,1-领取邮件,2-删除邮件</param> 
 | 
    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<MailItemData> Items;        //物品信息 
 | 
  
 | 
    public bool HasAward() 
 | 
    { 
 | 
        return Items != null && Items.Count > 0; 
 | 
    } 
 | 
  
 | 
    /// <summary> 
 | 
    /// 判断是否为模板类型邮件 
 | 
    /// </summary> 
 | 
    /// <returns>true-模板邮件, false-普通邮件</returns> 
 | 
    public bool IsTemplateMail() 
 | 
    { 
 | 
        if (string.IsNullOrEmpty(Title)) 
 | 
            return false; 
 | 
  
 | 
        return Title.StartsWith("<T>") && Title.EndsWith("</T>"); 
 | 
    } 
 | 
  
 | 
    /// <summary> 
 | 
    /// 获取模板邮件的模板key 
 | 
    /// </summary> 
 | 
    /// <returns>模板key,如果不是模板邮件返回空字符串</returns> 
 | 
    public string GetTemplateKey() 
 | 
    { 
 | 
        if (!IsTemplateMail()) 
 | 
            return string.Empty; 
 | 
  
 | 
        // 模板格式: <T>key</T> 
 | 
        // 跳过开头的"<T>" (3字符) 
 | 
        // 取中间部分,总长度减去"<T>"和"</T>"共7字符 
 | 
        const int prefixLength = 3; // "<T>"长度 
 | 
        const int suffixLength = 4; // "</T>"长度 
 | 
        return Title.Substring(prefixLength, Title.Length - (prefixLength + suffixLength)); 
 | 
    } 
 | 
  
 | 
    /// <summary> 
 | 
    /// 获取模板邮件的参数列表 
 | 
    /// </summary> 
 | 
    /// <returns>参数列表,如果不是模板邮件或参数为空则返回空列表</returns> 
 | 
    public List<string> GetTemplateParams() 
 | 
    { 
 | 
        if (!IsTemplateMail() || string.IsNullOrEmpty(Text)) 
 | 
            return new List<string>(); 
 | 
  
 | 
        try 
 | 
        { 
 | 
            List<string> result = JsonMapper.ToObject<List<string>>(Text); 
 | 
            // 如果解析结果为null(例如Text的内容是 "null" 字符串),则返回一个空列表以防后续出错 
 | 
            return result ?? new List<string>(); 
 | 
        } 
 | 
        catch (Exception ex) 
 | 
        { 
 | 
            Debug.LogError($"使用 LitJson 解析邮件参数失败。 Text: {Text}, 错误: {ex.Message}"); 
 | 
            return new List<string>(); 
 | 
        } 
 | 
    } 
 | 
} 
 |