cmd) { case CmdCode::cmd_email_questEmailList: # 6701 刷新邮件列表状态 return EmailProc::RefreshEmailList($req); case CmdCode::cmd_email_readAllEmail: # 6702 读取邮件 return EmailProc::ReadEmail($req); case CmdCode::cmd_email_delMmail: # 6703 删除邮件 return EmailProc::deleteMail($req); case CmdCode::cmd_email_test: # 6704 测试 return self::test($req); case CmdCode::cmd_mail_delMmailAnyConditions: # 6705 删除邮件 没有条件限制 return EmailProc::deleteMailNoCondition($req); case CmdCode::cmd_mail_notReadMailNum: # 6706 请求邮件未处理的数量 return EmailProc::queryNotReadEmails($req); default: return Resp::err(ErrCode::cmd_err); } } /** * * @param type $req */ static function queryNotReadEmails($req) { $uid = $req->uid; $zoneid = $req->zoneid; $n = 0; //删除全部标记为已读的,且没有奖励内容的邮件 $mails = ArrayInit(); $mails = self::getMailQueue($zoneid, $uid); foreach ($mails as $m) { if (!$m->isDrawed()) { $n += 1; } } return Resp::ok(array('num' => $n)); } /** * * @param Req $req */ static function test($req) { if (GAME_ONLINE) { return; } $zonid = $req->zoneid; $uid = $req->uid; $itemid = 399001; # 钻石 $num = 3; $mail = new EmailModel(null, enum_Mail_Type::SysTemMail, # "测试", "测试有好礼", $itemid, $num, "系统", "系统"); for ($i = 0; $i < 100; $i++) { $mailId = self::InsertMail($zonid, $uid, $mail); } $req->paras[0] = $mailId; // self:: ReadEmail($req); return Resp::ok('ok'); } /** * 刷新邮件列表状态 * @param Req $req */ public static function RefreshEmailList($req) { $uid = $req->uid; $zoneid = $req->zoneid; $mailId = $req->paras[0]; # Ps. 对比mailid, 推送更新包, # # 第一次mailid传0, 以后传最新id, # # 这样服务端发送的是增量包,节约流量 self::refreshSysMail($zoneid, $uid); # 更新下系统邮件 self::clearExpireMails($zoneid, $uid); # 清理过期邮件, 以及超过容量的邮件 $mails = self::getMailQueue($zoneid, $uid, $mailId); # 拉取邮件列表 return Resp::ok(array('mailQueue' => $mails)); # 返回值 } /** * 读取一封邮件, 注意: 有删除所有邮件,但是没有一件领取好吗? 要不也加上吧 * @param Req $req */ public static function ReadEmail($req) { $uid = $req->uid; $zoneid = $req->zoneid; $mailId = $req->paras[0]; # 传递参数,邮件的id字符串 $arr = ArrayInit(); $rewardEmailIds = ArrayInit(); $notrewardEmails = ArrayInit(); $mails = ArrayInit(); if ($mailId) { # 普通读取一封邮件的逻辑 $mail = self::getMail($zoneid, $uid, $mailId); if (!$mail) { return Resp::ok(array('err' => -1, 'msg' => 'mail not found!')); } $mails[] = $mail; } else { # 非有效mailid,执行一键领取 $mails = self::getMailQueue($zoneid, $uid); } ////如果进行的是全部领取操作 ////是 1 ,有奖励的邮件,全部处理,领取奖励。无奖励的邮件,不处理。因为全部领取邮件功能只限于处理有奖励的物品,无奖励的不管 /// 非 2 ,有奖励就领取。 无奖励,则要进行标记为已读。 //// 综上所述: 无论是全部还是单个邮件, ////有奖励的邮件肯定要领取的,还得删掉。 无奖励的看情况, 一键领取模式则不处理,否则就标记为已读;但是不需要删掉。 foreach ($mails as $m) { //先判断邮件,是否存在有效的奖励物品 if ($m->isExistReward()) { $err = StoreProc::AddMultiItemInStore($req, $m->getRewardStr(), 4); if (ErrCode::ok == $err) { # 发奖成功 $arr[] = $m->getRewardStr(); $rewardEmailIds[] = $m->mailId; self::delMail($zoneid, $uid, $m->mailId); # 规则: 领取后就会删除邮件,需要跟策划确认 } else { # 发放物品失败或部分失败 // todo: how to deal this case? } } else { if ($mailId != 0) { $notrewardEmails[] = $m; } } } foreach ($notrewardEmails as $t_mail) { #回写个人邮件的无奖励邮件的读取时间戳 $t_mail->drawedts = now(); self::updateMail($zoneid, $uid, $t_mail); } self::logMailDrawed($rewardEmailIds, $uid, $zoneid); # 更新数据库中邮件的领取记录 $reward = implode(';', $arr); # 拼接下奖励字符串 return Resp::ok(array('err' => 0, 'store' => $req->userInfo->game->store, 'hero' => $req->userInfo->game->heros, 'reward' => $reward)); } /** * 删除邮件 * @param Req $req */ static function deleteMail($req) { $uid = $req->uid; $zoneid = $req->zoneid; $mailId = $req->paras[0]; # 邮件id, 0 or null:删除所有邮件 $n = 0; if (!$mailId) { # null or 0 删除所有邮件 // $n = self::clearMailQueue($zoneid, $uid); //上面操作太霸道,而且也不正确 $n = 0; //删除全部标记为已读的,且没有奖励内容的邮件 $mails = ArrayInit(); $mails = self::getMailQueue($zoneid, $uid); foreach ($mails as $m) { if ($m->isDrawed() && !$m->isExistReward()) { self::delMail($zoneid, $uid, $m->mailId); $n += 1; } } } else { # 传过来的是一个mailid $n = self::delMail($zoneid, $uid, $mailId); } return Resp::ok(array('del' => $n)); # 返回删除的邮件数量 } /** * 删除邮件 没有条件限制 * @param type $req */ static function deleteMailNoCondition($req) { $uid = $req->uid; $zoneid = $req->zoneid; $mailIdStr = $req->paras[0]; $mids = explode(',', $req->paras[0]); // 逗号分割uid字符串 $n = 0; foreach ($mids as $mailId) { self::delMail($zoneid, $uid, $mailId); $n += 1; } return Resp::ok(array('del' => $n)); # 返回删除的邮件数量 } // /** // * 租借好友英雄进行战斗,发送雇佣费用 // * @param int $zoneid // * @param string $uid_friend // * @param string $uid // * @param int $amt // */ // public static function SendHireCoinFromFight($zoneid, $uid_friend, $nickname, $uid, $amt) { // $g = glc(); // $mail = new EmailModel(null, enum_Mail_Type::SysTemMail, "收入-好友租用", # // sprintf($g->User_Friends_HireFriendForBattle_MailContents, $nickname), # // META_GOLD_ITEMID, # # 好友收益需要扣除系统税率. // (int) ($amt * (1 - $g->User_Friends_HireFriendForBattle_Tax)), # // $uid, $nickname, now(), # // now() + $g->User_Friends_HireFriendForBattle_MailExpireTs); // self::InsertMail($zoneid, $uid_friend, $mail); // } /** * 系统 - 发送pvp阶段奖励 * @param type $zoneid * @param type $uid * @param type $leagueId */ public static function SendPvpLeagueReward($zoneid, $uid, $leagueId) { $leagueRwd = GameConfig::pvp_leaguereward_getItem($leagueId); for ($i = 1; $i <= 3; $i++) { $r = "reward$i"; $rdstr = $leagueRwd->$r; if ($rdstr != "") { $arr = explode(',', $rdstr); $itemid = $arr[0]; $num = $arr[1]; $mail = new EmailModel(null, enum_Mail_Type::SysTemMail, "竞技场阶段奖励-$leagueId-$i", # "恭喜您在上周的竞技场战斗中获得" . $leagueRwd->leagueName . "阶段的奖励", # $itemid, $num); 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 EmailModel(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 EmailModel(null, enum_Mail_Type::SysTemMail, "竞技场每日上榜奖励", # "恭喜您在昨天的竞技场战斗中获得总榜" . $rkrwd->rankName . "的奖励", # $itemid, $num); self::InsertMail($zoneid, $uid, $mail); break; } } } /** * 系统邮件 - 发送pvp排行榜蝉联冠军 * @param type $zoneid * @param type $uid * @param type $rank */ public static function SendPvpChanlianReward($zoneid, $uid) { $arr = explode(',', glc()->PVP_Chanlian_RewardStr); $itemid = $arr[0]; $num = intval($arr[1]); $mail = new EmailModel(null, enum_Mail_Type::SysTemMail, "PVP蝉联冠军奖励", # "恭喜您连续3周霸占PVP排行榜总榜第一名, 获得蝉联冠军的殊荣, 并获得系统给予您的特殊奖励.", # $itemid, $num); self::InsertMail($zoneid, $uid, $mail); } /** * 删档内侧补偿邮件 * @param type $zoneid * @param type $uid * @param type $name * @param type $content */ public static function SendDelTestMail($zoneid, $uid, $name, $content) { $arr = explode(',', $content); $itemid = $arr[0]; $num = intval($arr[1]); $mail = new EmailModel(null, enum_Mail_Type::SysTemMail, "删档内侧补偿", # "感谢您在之前的删档内侧活动中充值购买$name.", # $itemid, $num * 2); # 2倍返还 self::InsertMail($zoneid, $uid, $mail); } /** * 系统邮件 - 公会申请被拒,通知玩家 * @param type $zoneid * @param type $uid * @param type $rank */ public static function SendGuildApplyRefusedMail($zoneid, $uid, $guildName) { $mail = new EmailModel(null, enum_Mail_Type::GuildApplyRefuseMail, "公会申请结果", # "很遗憾" . $guildName . "公会拒绝了您的加入申请", # 0, 0, "公会"); 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 EmailModel(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 EmailModel(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 EmailModel(null, enum_Mail_Type::GuildNoticeMail, $title, # $content, # 0, 0, "公会"); self::InsertMail($zoneid, $uid, $mail); } // /** * 插入邮件 * @param int $zoneid * @param string $uid * @param EmailModel $mail */ private 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中 return $mail->mailId; } /** * * @param type $zoneid * @param type $uid * @param EmailModel $mail */ private static function updateMail($zoneid, $uid, $mail) { gMem()->hset(MemKey_User::Mail_Queue_hash($zoneid, $uid), $mail->mailId, $mail); } private static function logMail($zoneid, $uid, $mail) { $data = array( 'mailId' => $mail->mailId, 'zoneid' => $zoneid, 'itemid' => $mail->itemid, 'num' => $mail->num, 'type' => $mail->type, 'name_sender' => $mail->name_sender, 'uid_sender' => $mail->uid_sender, 'uid_to' => $uid, # # this mail is send to me 啊. 'title' => $mail->title, 'content' => $mail->content, 'tag' => $mail->tag, 'startts' => $mail->startts, 'endts' => $mail->endts ); daoInst()->insert('tab_mailrecord')->data($data)->exec(); } /** * 更新邮件的领取记录 * @param type $ids */ static function logMailDrawed($ids, $uid, $zoneid) { // Err("这里邮件id不是唯一, 需要联合uid一起改.--晨叶反馈于2020.5.21"); daoInst()->update(self::MailLog_TableName)->data(array( 'drawedts' => now() ))->where('mailId')->in($ids)-> andWhere('zoneid')->eq($zoneid)-> andWhere('uid_to')->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); # 系统邮件处理记录,在数据集过大以前, 个人认为全部获取比较高效 --王刚,所以系统邮件不能太多呢 foreach ($sysMailQueue as $sysId => $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 EmailModel($sysMail); # 创建邮件 $mail->type = enum_Mail_Type::SysTemMail; # 设置邮件类型为系统邮件 $mail->uid_sender = $mail->name_sender = "系统"; # 设置发送者昵称和uid为系统 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) { return $v->endts < $ts; # 选出过期邮件 }); if (count($del)) { # >0 $mem->hdel($key, array_keys($del)); # 批量清除 } } /** * 获取邮件序列[自动提取id>mailid的邮件] * @param int $zoneid * @param string $uid * @param int $mailId * @return array */ private static function getMailQueue($zoneid, $uid, $mailId = 0) { $key = MemKey_User::Mail_Queue_hash($zoneid, $uid); $keys = gMem()->hkeys($key); # 拉取所有id $fkeys = array_filter($keys, function($v)use($mailId) { # 过滤邮件 return $v > $mailId; # id 大于更新id }); $skeys = sort($fkeys); # 排序 $mkeys = array_slice($fkeys, 0, 50); # 取固定数量 $mails = (array) gMem()->hmget($key, $mkeys); # mget $ret = array_map(function($v) { return new EmailModel($v); # 装箱 }, $mails); return array_values($ret); } /** * 获取邮件 * @param int $zoneid * @param string $uid * @param int $mailId * @return EmailModel */ private static function getMail($zoneid, $uid, $mailId) { $m = gMem()->hget(MemKey_User::Mail_Queue_hash($zoneid, $uid), $mailId); if ($m) { $m = new EmailModel($m); } return $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; } // }