<?php

namespace loyalsoft;

class enum_Mail_Type extends Enum {

    /** 系统邮件 */
    const SysTemMail = 1;

    /** 好友租借费用 */
    const HireCoin = 2;
    const PvpLeagueReward = 3;
    const PvpRankReward = 4;

    /** 公会申请被拒绝系统邮件 */
    const GuildApplyRefuseMail = 5;

    /** 公会捐献结算 */
    const GuildDonateSettle = 6;

    /** 公会钻石礼包奖励 */
    const GuildCashGiftReward = 7;

    /** 公会邮件 */
    const GuildNoticeMail = 8;
}

/**
 * 邮件模块
 * @author gwang
 * @version 2.0.0 《言灵世界2》业务改版邮件系统 2020.8.13
 *           1.0.1 经过分析, 邮件系统有性能问题. 当邮件数量较多的时候, 如何优雅的处理,
 *             代码中无解决方案.
 *           1.0.0 created 邮件系统.
 */
class EmailProc {

    /**
     * 邮件最大容量100封
     */
    const MaxMailContent = 100;

    /**
     * 邮件最大过期时间7天
     */
    const MaxMailExpireTs = 604800;

    /**
     * 邮件日志表
     */
    const MailLog_TableName = 'tab_mailrecord';

    public static function procMain() {
        switch (req()->cmd) {
            case CmdCode::cmd_email_questEmailList:                             # 6701 刷新邮件列表状态
                return EmailProc::RefreshEmailList();
            case CmdCode::cmd_email_readAEmail:                                 # 6702 读取一封邮件
                return EmailProc::ReadEmail();
            case CmdCode::cmd_email_DrawReward:                                 # 6703 领取一封邮件附件(奖励)
                return EmailProc::DrawEmailAppendix();
            case CmdCode::cmd_email_DrawAllRewards:                             # 6704 领取所有邮件
                return EmailProc::DrawAllEmailAppendixes();
            case CmdCode::cmd_mail_delMailReaded:                               # 6705 删除已读邮件
                return EmailProc::DelReadedEmail();
            case CmdCode::cmd_mail_notReadMailNum:                              # 6706 请求邮件未处理的数量
                return EmailProc::queryNotReadEmails();
            case CmdCode::cmd_mail_sendTestItems:                               # 6707 发放测试用道具邮件
                return self::SendTestItems();
            default:
                return Resp::err(ErrCode::cmd_err);
        }
    }

// <editor-fold defaultstate="collapsed" desc="   API   ">

    /**
     * 6707  发放测试用道具
     */
    static function SendTestItems() {
//        Err(ErrCode::err_method_obsoleted, "此功能仅测试期间可用");
        list($itemId, $num) = req()->paras;
        self::SendTestItemsMail(req()->zoneid, req()->uid, $itemId, $num);
        $mails = self::getMailQueue(req()->zoneid, req()->uid);                 # 拉取邮件列表
        return Resp::ok(array('mailQueue' => $mails));
    }

    /**
     * 【6706】查询未处理邮件数量
     */
    static function queryNotReadEmails() {
        $uid = req()->uid;
        $zoneid = req()->zoneid;
        $n = 0;                                                                 # 计数器
        self::refreshSysMail($zoneid, $uid);                                    # 更新下系统邮件
        self::clearExpireMails($zoneid, $uid);                                  # 清理过期邮件, 以及超过容量的邮件
        $mails = self::getMailQueue($zoneid, $uid);
        foreach ($mails as $m) {
//            isEditor() and $m = new EmailModel();
            if (!$m->readts <= 0) {
                $n += 1;
            }
        }
        return Resp::ok(array('num' => $n));
    }

    /**
     * 【6701】拉取最新邮件列表
     */
    public static function RefreshEmailList() {
        $uid = req()->uid;
        $zoneid = req()->zoneid;
        self::refreshSysMail($zoneid, $uid);                                    # 更新下系统邮件
        self::clearExpireMails($zoneid, $uid);                                  # 清理过期邮件, 以及超过容量的邮件
        $mails = self::getMailQueue($zoneid, $uid);                             # 拉取邮件列表
        return Resp::ok(array('mailQueue' => $mails));                          # 返回值
    }

    /**
     * 【6702】读取一封邮件, 更新readts
     */
    public static function ReadEmail() {

        $uid = req()->uid;
        $zoneid = req()->zoneid;
        $mailId = req()->paras[0];                                              # 传递参数，邮件的id字符串
        my_Assert($mailId, ErrCode::email_wrongid);                             # 普通读取一封邮件的逻辑

        $mail = self::getMail($zoneid, $uid, $mailId);
        $mail->readts = now();
        self::updateMail($zoneid, $uid, $mail);                                 # 更新邮件数据
        self::logMailReaded(array($mailId), $uid, $zoneid);                     # 更新数据库中邮件的read记录
        return Resp::ok(array("rewardstr" => $mail->appendix));
    }

    /**
     * 【6703】 领取一封邮件的附件，更新drawedts
     * @return type
     */
    public static function DrawEmailAppendix() {
        $uid = req()->uid;
        $zoneid = req()->zoneid;
        $mailId = req()->paras[0];                                              # 传递参数，邮件的id字符串
        my_Assert($mailId, ErrCode::email_wrongid);                             # 邮件id

        $mail = self::getMail($zoneid, $uid, $mailId);                          # 取出对应的邮件数据
        my_Assert($mail->readts > 0, ErrCode::err_innerfault);                  # 未打开的不可能调用领取接口
        my_Assert($mail->isExistReward(), ErrCode::email_no_appendix);          # 先判断邮件，是否存在有效的奖励物品
        my_Assert($mail->drawedts <= 0, ErrCode::email_appendix_drawed);        # 邮件不可重复领取
        $err = StoreProc::AddMultiItemInStore($mail->appendix, 4);              # 发放奖励

        my_Assert(ErrCode::ok == $err, $err);                                   # 发奖成功
        $mail->drawedts = now();                                                # 更新领取时间戳
        self::updateMail($zoneid, $uid, $mail);                                 # 回写邮件数据
        if ($mail->sender_name == '系统') {
            TaskProc::OnDrawSysMail($mail->sender_uid);
        }
        self::logMailDrawed(array($mailId), $uid, $zoneid);                     # 更新数据库中邮件的领取记录
        UserProc::updateUserInfo();
        return Resp::ok(array(#                                                 # 同步数据
                    'store' => ctx()->store,
                    'hero' => ctx()->heros,
                    'baseInfo' => ctx()->base(),
                    'num' => 1,
                    'reward' => $mail->appendix
        ));
    }

    /**
     * 【6704】 领取所有邮件附件奖励
     * @return type
     */
    public static function DrawAllEmailAppendixes() {
        $uid = req()->uid;
        $zoneid = req()->zoneid;
        $arr = ArrayInit();                                                     # 记录奖励字符串
        $rewardEmailIds = ArrayInit();                                          # 记录领取邮件id
        $mails = self::getMailQueue($zoneid, $uid);                             # 提取邮件列表
        // 有奖励的邮件，全部处理，领取奖励。无奖励的邮件，不处理。因为全部领取邮件功能只限于处理有奖励的物品，无奖励的不管
        foreach ($mails as $m) {                                                # 遍历所有邮件
            isEditor() and $m = new Ins_Email;
            if ($m->isExistReward()) {                                          # 先判断邮件，是否存在附件
                if ($m->drawedts > 0) {                                         # 已领取邮件跳过
                    continue;
                }
                $err = StoreProc::AddMultiItemInStore($m->appendix, 4);
                my_Assert(ErrCode::ok == $err, $err);                           # 发奖成功
                $m->drawedts = now();                                           # 更新下领取时间戳
                if ($m->readts <= 0) {                                          # 更新下读取时间戳
                    $m->readts = now();
                }
                $arr[] = $m->appendix;
                $rewardEmailIds[] = $m->mailId;
                if ($m->sender_name == '系统') {
                    TaskProc::OnDrawSysMail($m->sender_uid);
                }
            } //  else                                                          # 无附件的邮件不管
        }
        $asocMails = array();
        foreach ($mails as $mm) {                                               # 转为关联数组
//            isEditor() and $mm = new EmailModel;
            $asocMails[$mm->mailId] = $mm;
        }
        $n = count($rewardEmailIds);                                            # 计数,领取邮件数量
        if ($n > 0) {
            self::updateMails($zoneid, $uid, $asocMails);                       # 批量更新邮件
            self::logMailDrawed($rewardEmailIds, $uid, $zoneid);                # 更新数据库中邮件的领取记录
        }
        $reward = implode(';', $arr);                                           # 拼接下奖励字符串
        UserProc::updateUserInfo();
        return Resp::ok(array(#                                                 # 同步数据
                    'baseInfo' => ctx()->base(),
                    'store' => ctx()->store,
                    'hero' => ctx()->heros,
                    'num' => $n,
                    'reward' => $reward
        ));
    }

    /**
     * 【6705】 删除所有已读邮件
     * @return type
     */
    public static function DelReadedEmail() {
        $uid = req()->uid;
        $zoneid = req()->zoneid;
        $mails = self::getMailQueue($zoneid, $uid);                             # 取邮件队列
        $n = 0;                                                                 # 记个数,看一共删除几封邮件
        foreach ($mails as $m) {
            isEditor() and $m = new Ins_Email;
            if ($m->readts <= 0) {                                              # 未读邮件跳过
                continue;
            }
            if ($m->isExistReward()) {                                          # 包含附件,
                if (!$m->isDrawed()) {                                          # 且附件尚未领取的,不能删除,跳过
                    continue;
                }
            }
            self::delMail($zoneid, $uid, $m->mailId);                           # 规则: 附件领取后就可以删除邮件
            $n++;
        }
        return Resp::ok(array('num' => $n));                                    # 返回删除数量
    }

// </editor-fold>
//
// <editor-fold defaultstate="collapsed" desc="  插入邮件">

    /**
     * 系统邮件 - 发送世界boss结算奖励
     * @param type $zoneid
     * @param type $uid
     * @param type $index 排名
     * @param type $rewardName 奖励阶段名称
     * @param type $reward 奖励内容
     */
    public static function SendWorldBossReward($zoneid, $uid, $index, $rewardName, $reward) {
        $mail = new Ins_Email(null, enum_Mail_Type::SysTemMail, "世界boss奖励", #
                "恭喜您在世界boss战斗中输出伤害排{$index}名,获得{$rewardName}奖励!", $reward);
        self::InsertMail($zoneid, $uid, $mail);
    }

    /**
     * 系统邮件 - 发送月卡奖励
     * @param type $zoneid
     * @param type $uid
     * @param type $rank
     */
    public static function SendMonthlyVIPDailyReward($zoneid, $uid, $cardName, $reward) {
        $mail = new Ins_Email(null, enum_Mail_Type::SysTemMail, "月卡奖励", #
                $cardName . "每日奖励", $reward);
        self::InsertMail($zoneid, $uid, $mail);
    }

    /**
     * 系统邮件 - 发送竞技场赛季排行榜奖励
     * @param type $zoneid
     * @param type $uid
     * @param type $rank
     */
    public static function SendPvpRankReward_Season($zoneid, $uid, $rank) {
        foreach (GameConfig::pvp_rankreward() as $rkrwd) {
            isEditor() and $rkrwd = new \sm_pvp_rankreward();
            if ($rank >= $rkrwd->minRank and $rank <= $rkrwd->maxRank) {
                $arr = explode(',', $rkrwd->reward_season);
                $itemid = $arr[0];
                $num = $arr[1];
                $mail = new Ins_Email(null, enum_Mail_Type::SysTemMail, "竞技场赛季上榜奖励", #
                        "恭喜您在上赛季的竞技场战斗中获得总榜" . $rkrwd->rankName . "的奖励", #
                        "$itemid,$num");
                self::InsertMail($zoneid, $uid, $mail);
                break;
            }
        }
    }

    /**
     * 系统邮件 - 发送竞技场每日排行榜上榜奖励
     * @param type $zoneid
     * @param type $uid
     * @param type $rank
     */
    public static function SendPvpRankReward_Lastday($zoneid, $uid, $rank) {
        foreach (GameConfig::pvp_rankreward() as $rkrwd) {
            isEditor() and $rkrwd = new \sm_pvp_rankreward();
            if ($rank >= $rkrwd->minRank and $rank <= $rkrwd->maxRank) {
                $arr = explode(',', $rkrwd->reward_day);
                $itemid = $arr[0];
                $num = $arr[1];
                $mail = new Ins_Email(null, enum_Mail_Type::SysTemMail, "竞技场每日上榜奖励", #
                        "恭喜您在昨天的竞技场战斗中获得总榜" . $rkrwd->rankName . "的奖励", #
                        "$itemid,$num");
                self::InsertMail($zoneid, $uid, $mail);
                break;
            }
        }
    }

    /**
     * 删档内侧补偿邮件(充值金额2倍返还钻石)
     * @param type $zoneid
     * @param type $uid
     * @param type $rmbNum
     */
    public static function SendDelTestMail($zoneid, $uid, $rmbNum) {
        $itemid = META_CASH_ITEMID;                                             # 钻石
        $num = $rmbNum * 2 * 10;                                                # 2倍返还
        $mail = new Ins_Email(null, enum_Mail_Type::SysTemMail, "首轮删档测试钻石返还", #
                "感谢您在首轮删档测试活动中充值{$rmbNum}元. 特此返还{$num}钻石, 请查收. 祝您生活愉快!"
                , "$itemid, $num");
        self::InsertMail($zoneid, $uid, $mail);
    }

    /**
     * 测试道具邮件
     * @param type $zoneid
     * @param type $uid
     * @param type $itemid
     * @param type $num
     */
    public static function SendTestItemsMail($zoneid, $uid, $itemid, $num) {
        $mail = new Ins_Email(null, enum_Mail_Type::SysTemMail, "内部测试-道具发放邮件", #
                "请领取", "$itemid, $num");
        self::InsertMail($zoneid, $uid, $mail);
    }

    //----------------拍卖邮件

    /**
     * 竞拍得到道具邮件
     * @param type $zoneid
     * @param type $uid
     * @param type $itemid
     * @param type $num
     */
    public static function SendAuctionItemsMail($zoneid, $uid, $itemid, $num) {
        $name = GameConfig::item_base_getItem($itemid)->name;

        $mail = new Ins_Email(null, enum_Mail_Type::SysTemMail, "拍卖邮件", #
                "恭喜竞拍" . $name . "成功请领取", "$itemid, $num");
        self::InsertMail($zoneid, $uid, $mail);
    }

    /**
     * 流拍邮件
     * @param type $zoneid
     * @param type $uid
     * @param type $itemid
     * @param type $num
     */
    public static function SendAuctionItemsStreamingMail($zoneid, $uid, $itemid, $num) {
        $name = GameConfig::item_base_getItem($itemid)->name;

        $mail = new Ins_Email(null, enum_Mail_Type::SysTemMail, "拍卖邮件", #
                $name . "拍品没有人竞拍，点击回收", "$itemid, $num");
        self::InsertMail($zoneid, $uid, $mail);
    }

    /**
     * 取消出售邮件
     * @param type $zoneid
     * @param type $uid
     * @param type $itemid
     * @param type $num
     */
    public static function SendCantSellAuctionItemsMail($zoneid, $uid, $itemid, $num) {
        $name = GameConfig::item_base_getItem($itemid)->name;

        $mail = new Ins_Email(null, enum_Mail_Type::SysTemMail, "拍卖邮件", #
                $name . "拍品取消出售，请点击领取", "$itemid, $num");
        self::InsertMail($zoneid, $uid, $mail);
    }

    /**
     * 竞拍成功，钻石邮件
     * @param type $zoneid
     * @param type $uid
     * @param type $itemid
     * @param type $num
     */
    public static function SendAuctionCashMail($zoneid, $uid, $itemid, $cash, $num) {
        $name = GameConfig::item_base_getItem($itemid)->name;

        $mail = new Ins_Email(null, enum_Mail_Type::SysTemMail, "拍卖邮件", #
                "您上架" . $name . "被竞拍成功,请领取钻石", "$cash,$num");
        self::InsertMail($zoneid, $uid, $mail);
    }

    /**
     * 竞价被超出
     * @param type $zoneid
     * @param type $uid
     * @param type $itemid
     * @param type $cash
     * @param type $num
     */
    public static function SendRefundAuctionCash($zoneid, $uid, $itemid, $cash, $num) {
        $name = GameConfig::item_base_getItem($itemid)->name;

        $mail = new Ins_Email(null, enum_Mail_Type::SysTemMail, "拍卖退款邮件", #
                $name . "拍品出价已经被超出，系统返还您的出价钻石", "$cash,$num");
        self::InsertMail($zoneid, $uid, $mail);
    }

    /**
     * 拍品取消出售，竞拍者钻石回退邮件
     * @param type $zoneid
     * @param type $uid
     * @param type $itemid
     * @param type $cash
     * @param type $num
     */
    public static function SendCantSellAuctionItemsBidCash($zoneid, $uid, $itemid, $cash, $num) {
        $name = GameConfig::item_base_getItem($itemid)->name;

        $mail = new Ins_Email(null, enum_Mail_Type::SysTemMail, "拍卖退款邮件", #
                $name . "拍品取消出售，系统返还您的出价钻石", "$cash,$num");
        self::InsertMail($zoneid, $uid, $mail);
    }

    /**
     * 一口价拍走拍品，竞拍者钻石回退
     * @param type $zoneid
     * @param type $uid
     * @param type $itemid
     * @param type $cash
     * @param type $num
     */
    public static function SendAuctionItemsBidCashFallback($zoneid, $uid, $itemid, $cash, $num) {
        $name = GameConfig::item_base_getItem($itemid)->name;

        $mail = new Ins_Email(null, enum_Mail_Type::SysTemMail, "拍卖退款邮件", #
                $name . "拍品已经被别人拍走，系统返还您的出价钻石", "$cash,$num");
        self::InsertMail($zoneid, $uid, $mail);
    }

    public static function SendPaiHangRewardMail_fpower($zoneid, $uid, $index, $reward) {
        $mail = new Ins_Email(null, enum_Mail_Type::SysTemMail, "战力榜排名奖励", #
                "恭喜您在战力榜获得{$index}名,获得奖励!", $reward);
        self::InsertMail($zoneid, $uid, $mail);
    }

    public static function SendPaiHangRewardMail_level($zoneid, $uid, $index, $reward) {
        $mail = new Ins_Email(null, enum_Mail_Type::SysTemMail, "等级榜排名奖励", #
                "恭喜您在等级榜获得{$index}名,获得奖励!", $reward);
        self::InsertMail($zoneid, $uid, $mail);
    }

// </editor-fold>
//
// <editor-fold defaultstate="collapsed" desc="   公会邮件   ">

    /**
     * 系统邮件 - 公会申请被拒，通知玩家
     * @param type $zoneid
     * @param type $uid
     * @param type $rank
     */
    public static function SendGuildApplyRefusedMail($zoneid, $uid, $guildName) {
        $mail = new Ins_Email(null, enum_Mail_Type::GuildApplyRefuseMail, "公会申请结果", #
                "很遗憾" . $guildName . "公会拒绝了您的加入申请", "", "公会");
        self::InsertMail($zoneid, $uid, $mail);
    }

    /**
     * 系统邮件 - 公会捐献碎片结算
     * @param type $zoneid
     * @param type $uid
     * @param type $rank
     */
    public static function SendGuildDonateSettle($zoneid, $uid, $chipArr) {
        $itemId = $chipArr[0];
        $itemNum = $chipArr[1];
        $content = $itemNum == null ? "请求捐献结算时间截止，没有得到捐献碎片" : "请求捐献结算时间截止，领取已获得碎片";
        $mail = new Ins_Email(null, enum_Mail_Type::GuildDonateSettle, "公会捐献碎片结算", #
                $content, "$itemId, $itemNum", "公会");
        self::InsertMail($zoneid, $uid, $mail);
    }

    /**
     * 系统邮件 - 公会钻石礼包奖励结算
     * @param type $zoneid
     * @param type $uid
     * @param type $rank
     */
    public static function SendGuildCashGiftReward($zoneid, $uid, $itemId, $num) {
        $mail = new Ins_Email(null, enum_Mail_Type::GuildCashGiftReward, "公会礼包回赠", #
                "系统回赠礼品", "$itemId, $num", "公会");
        self::InsertMail($zoneid, $uid, $mail);
    }

    /**
     * 公会公告邮件
     * @param type $zoneid
     * @param type $uid
     * @param type $content
     */
    public static function SendGuildNoticeMail($zoneid, $uid, $title, $content) {
        $mail = new Ins_Email(null, enum_Mail_Type::GuildNoticeMail, $title, #
                $content, "", "公会");
        self::InsertMail($zoneid, $uid, $mail);
    }

// </editor-fold>
//
// <editor-fold defaultstate="collapsed" desc="    辅助方法    ">

    /**
     * 插入邮件
     * @param int $zoneid
     * @param string $uid
     * @param Ins_Email $mail
     */
    public static function InsertMail($zoneid, $uid, $mail) {
        $mem = gMem();
        $key_id = MemKey_User::Mail_CurId_int($zoneid, $uid);
        $key_queue = MemKey_User::Mail_Queue_hash($zoneid, $uid);
        $mail->insertts = now();
        $mail->mailId = $mem->increment($key_id);
        if (!$mem->hsetnx($key_queue, $mail->mailId, $mail)) {                  # 重试下
            $mail->mailId = $mem->increment($key_id);
            if (!$mem->hsetnx($key_queue, $mail->mailId, $mail)) {
                // todo: 重试都没能成功的话, 记录下日志,  log err;
                CLog::err('create sysmail failed! id:' . JsonUtil::encode($mail), "EmailProc");
            }
        }
        self::logMail($zoneid, $uid, $mail);                                    # 将邮件写入Mysql中
//        CornerSignEventProc::OnNewMails();                                      # 角标更新
        return $mail->mailId;
    }

    /**
     * 回写mail数据
     * @param type $zoneid
     * @param type $uid
     * @param Ins_Email $mail
     */
    private static function updateMail($zoneid, $uid, $mail) {
        gMem()->hset(MemKey_User::Mail_Queue_hash($zoneid, $uid), $mail->mailId, $mail);
    }

    /**
     * 批量回写mail数据
     * @param type $zoneid
     * @param type $uid
     * @param dic<mailId, EmailModel> $mails
     */
    private static function updateMails($zoneid, $uid, $mails) {
        gMem()->hmset(MemKey_User::Mail_Queue_hash($zoneid, $uid), $mails);
    }

    /**
     *
     * @param type $zoneid
     * @param type $uid
     * @param Ins_Email $mail
     */
    private static function logMail($zoneid, $uid, $mail) {
        $data = array(
            'mailId' => $mail->mailId,
            'zoneid' => $zoneid,
            'appendix' => $mail->appendix,
            'type' => $mail->type,
            'sender_name' => $mail->sender_name,
            'sender_uid' => $mail->sender_uid,
            'to_uid' => $uid, #                                                 # this mail is send to me 啊.
            'title' => $mail->title,
            'content' => $mail->content,
            'tag' => $mail->tag
        );
        daoInst()->insert('tab_mailrecord')->data($data)->exec();
    }

    /**
     * 更新邮件的领取记录
     * @version  1 这里邮件id不是唯一, 需要联合uid一起改.--晨叶反馈于2020.5.21
     *            0 created at 言灵世界1.0
     * @param type $ids
     */
    static function logMailDrawed($ids, $uid, $zoneid) {
        daoInst()->update(self::MailLog_TableName)->data(array(
                    'drawedts' => now()
                ))->where('mailId')->in($ids)->
                andWhere('zoneid')->eq($zoneid)->
                andWhere('to_uid')->eq($uid)->
                exec();
    }

    /**
     * 更新邮件的读取/打开记录
     * @version  1 这里邮件id不是唯一, 需要联合uid一起改.--晨叶反馈于2020.5.21
     *            0 created at 言灵世界1.0
     * @param type $ids
     */
    static function logMailReaded($ids, $uid, $zoneid) {
        daoInst()->update(self::MailLog_TableName)->data(array(
                    'readts' => now()
                ))->where('mailId')->in($ids)->
                andWhere('zoneid')->eq($zoneid)->
                andWhere('to_uid')->eq($uid)->
                exec();
    }

    /**
     * 刷新系统派发邮件到玩家邮件列表
     * @param int $zoneid
     * @param string $uid
     * @return void
     */
    private static function refreshSysMail($zoneid, $uid) {
        $mem = gMem();
        $sysMailQueue = GameConfig::sysmail();                                  # 取系统消息列表
        if (!$sysMailQueue) {
            return;                                                             # 系统消息为空
        }
        $key = MemKey_User::Mail_SysRecord_set($zoneid, $uid);                  # memkey
        $record = $mem->smembers($key);                                         # 系统邮件处理记录,在数据集过大以前, 个人认为全部获取比较高效 --王刚,所以系统邮件不能太多呢(后面取已处理最大id,做优化吧)
        foreach ($sysMailQueue as $sysId => $sysMail) {
            isEditor() and $sysMail = new \sm_sysmail();                        # 辅助方法.
            if (!StlUtil::arrayContains($record, $sysId)) {                     # 判断尚未处理此邮件
                $ts = now();
                if ($ts > $sysMail->endts) {                                    # 已经过期的系统邮件
                    $mem->sadd($key, $sysId);                                   # 记录已经领取此邮件, 但不插入玩家邮件列表了.
                    continue;                                                   # 继续处理下一封
                }
                if ($ts < $sysMail->startts) {
                    continue;                                                   # 系统邮件尚未生效, 跳过
                }                                                               # else => 有效期内,继续处理
                $mail = new Ins_Email($sysMail);                                # 创建邮件
                $mail->type = enum_Mail_Type::SysTemMail;                       # 设置邮件类型为系统邮件
                $mail->sender_uid = $sysMail->id;                               # 设置uid为系统邮件id
                $mail->sender_name = "系统";                                     #  设置发送者昵称为系统
                self::InsertMail($zoneid, $uid, $mail);                         # 插入邮件
                $mem->sadd($key, $sysId);                                       # 记录已经领取此邮件
            }
        }
    }

    /**
     * 清理过期邮件
     * @param string $zoneid
     * @param string $uid
     * @return void
     */
    private static function clearExpireMails($zoneid, $uid) {
        $ts = now();
        $mem = gMem();
        $key = MemKey_User::Mail_Queue_hash($zoneid, $uid);
        $arr = $mem->hgetall($key);                                             # 所有邮件
        $del = array_filter((array) $arr, function ($v) use ($ts) {
            $email = new Ins_Email($v);
            return $email->ExpireTs() < $ts;                                    # 选出过期邮件
        });
        if (count($del)) {                                                      # >0
            $mem->hdel($key, array_keys($del));                                 # 批量清除
        }
    }

    /**
     * 获取邮件序列
     * @param int $zoneid
     * @param string $uid
     * @return array
     */
    private static function getMailQueue($zoneid, $uid) {
        $key = MemKey_User::Mail_Queue_hash($zoneid, $uid);                     # redis-key:= 玩家邮件列表
        $keys = gMem()->hkeys($key);                                            # 拉取所有id
//        var_dump($keys);
        sort($keys);                                                            # 排序
        $mkeys = array_slice($keys, 0, self::MaxMailContent);                   # 取固定数量100
//        var_dump($mkeys);
        $mails = (array) gMem()->hmget($key, $mkeys);                           # mget
        $ret = array_map(function ($v) {
            return new Ins_Email($v);                                           # 装箱
        }, $mails);
        return array_values($ret);
    }

    /**
     * 获取邮件
     * @param int $zoneid
     * @param string $uid
     * @param int $mailId
     * @return Ins_Email
     */
    private static function getMail($zoneid, $uid, $mailId) {
        $m = gMem()->hget(MemKey_User::Mail_Queue_hash($zoneid, $uid), $mailId);
        my_Assert($m != null, ErrCode::email_not_found);
        return new Ins_Email($m);
    }

    /**
     * 删除邮件
     * @param int $zoneid
     * @param string $uid
     * @param int $mailId
     * @return int 删除的邮件数量, (0,1)
     */
    private static function delMail($zoneid, $uid, $mailId) {
        return gMem()->hdel(MemKey_User::Mail_Queue_hash($zoneid, $uid), $mailId);
    }

    /**
     * 清空邮件列表
     * @param int $zoneid
     * @param string $uid
     * @return int 删除邮件的条数, (0,n)
     */
    private static function clearMailQueue($zoneid, $uid) {
        $key = MemKey_User::Mail_Queue_hash($zoneid, $uid);
        $n = gMem()->hlen($key);                                                # 先看总共有多少邮件, Ps.返回值要用
        gMem()->delete($key);                                                   # 删除邮件列表
        return $n;
    }

// </editor-fold>
}
