<?php

namespace addons\fastim\library;

use think\Db;
use addons\fastim\library\CommonCode;

/**
 * 公共类
 */
class Common
{

    /**
     * 将数组通过拼命的首字排序
     * @param array $arr 待排序数组
     * @param string $field 排序依据字段
     * @return array
     */
    public static function initialPinyinArrSort($arr, $field)
    {
        $capitalPreg = '/\b[A-Z]+\b/'; // 匹配大写字母

        $initialPinyinArr     = [];
        $initialPinyinSpecial = [];

        foreach ($arr as $key => $value) {
            $arr[$key]['initial'] = self::initialPinyin($arr[$key][$field]);
            if (preg_match($capitalPreg, $arr[$key]['initial'])) {
                $initialPinyinArr[$arr[$key]['initial']][] = $arr[$key];
            } else {
                $arr[$key]['initial']   = '#';
                $initialPinyinSpecial[] = $arr[$key];
            }
        }

        $initialPinyinIndex = array_keys($initialPinyinArr);
        sort($initialPinyinIndex);

        if ($initialPinyinSpecial) {
            $initialPinyinIndex[]  = '#';
            $initialPinyinArr['#'] = $initialPinyinSpecial;
        }

        return [
            'initialPinyinArr'   => $initialPinyinArr,
            'initialPinyinIndex' => $initialPinyinIndex
        ];
    }

    /**
     * 获得字符串首字的拼音字母
     * @param string $chinese
     * @return string 首拼字母
     */
    public static function initialPinyin($chinese)
    {
        $chinese = \fast\Pinyin::get(mb_substr($chinese, 0, 1), true);
        return ucfirst($chinese);
    }

    /**
     * 格式化用户职业
     * @param int $occupation
     */
    public static function formatOccupation($occupation)
    {
        $occupationLang = [
            0  => '保密',
            1  => '计算机/互联网/通信',
            2  => '生产/工艺/制造',
            3  => '医疗/护理/制药',
            4  => '金融/银行/投资/保险',
            5  => '商业/服务业/个体经营',
            6  => '文化/广告/传媒',
            7  => '娱乐/艺术/表演',
            8  => '律师/法务',
            9  => '教育/培训',
            10 => '公务员/行政/事业单位',
            11 => '模特',
            12 => '空姐',
            13 => '学生',
            14 => '其他'
        ];

        if ($occupation === false) {
            return $occupationLang;
        } elseif (array_key_exists($occupation, $occupationLang)) {
            return $occupationLang[$occupation];
        } else {
            return '';
        }
    }

    /**
     * 从知识库获取内容
     * @param string $keywords 关键词
     * @param int $id
     * @return array|bool
     */
    public static function getKbs($keywords, $id = null)
    {
        if (!is_string($keywords)) {
            return false;
        }

        if ($id) {
            $kbs = Db::name('fastim_kbs')->where('id', $id)->where('status', '1')->where('deletetime', null)->find();
            return $kbs;
        }

        $kbs = Db::name('fastim_kbs')
            ->field('id,title')
            ->where('title|keyword', 'like', '%' . $keywords . '%')
            ->where('status', '1')
            ->where('deletetime', null)
            ->order('weigh desc')
            ->limit(6)
            ->select();
        return $kbs;
    }

    /**
     * 建立与指定用户的会话
     * @param  [type] $type        会话类型:single=单聊,group=群聊,service=服务号
     * @param  [type] $userId      用户
     * @param  [type] $sessionUser 对话用户
     * @return array|bool 会话资料
     */
    public static function imSession($type, $userId, $sessionUser)
    {
        if ($type == 'single') {
            $imSession = Db::name('fastim_session')
                ->where('type', 'single')
                ->where('(user_one=:uid1 AND user_two=:sid1) OR (user_one=:sid2 AND user_two=:uid2)')
                ->bind([
                    'uid1' => $userId,
                    'sid1' => $sessionUser,
                    // 防止报错,tp-bug?
                    'uid2' => $userId,
                    'sid2' => $sessionUser
                ])
                ->find();
        } else {
            $imSession = Db::name('fastim_session')
                ->where('type', $type)
                ->where('user_one', $userId)
                ->where('chat_id', $sessionUser)
                ->find();
        }

        if (!$imSession) {
            // 建立
            $insert = [
                'type'       => $type,
                'user_one'   => $userId,
                'user_two'   => ($type == 'single') ? $sessionUser : 0,
                'chat_id'    => ($type == 'single') ? 0 : $sessionUser,
                'createtime' => time()
            ];
            if (Db::name('fastim_session')->insert($insert)) {
                $insert['id']     = Db::name('fastim_session')->getLastInsID();
                $insert['create'] = true;// 标记建立
                $imSession        = $insert;
            } else {
                return false;
            }
        } elseif ($imSession['deleteuser'] == $userId && $type != 'service') {
            Db::name('fastim_session')->where('id', $imSession['id'])->update([
                'deleteuser' => 0
            ]);
        }
        return $imSession;
    }


    /**
     * 服务号聊天记录
     * @param array $sessionInfo 会话资料
     * @param array $page 页码
     * @param int $uid 加载用户
     * @return array              聊天记录
     */
    public static function loadServiceRecord($sessionInfo, $page, $uid)
    {
        $resultArr = [
            'agree'     => '已同意',
            'refuse'    => '已拒绝',
            'autoagree' => '已自动同意'
        ];

        // 好友申请
        if ($sessionInfo['chat_id'] == 1) {
            $message = Db::name('fastim_message')
                ->where('type', 'friend_apply')
                ->where('sender_id|recipient_id', $uid)
                ->order('createtime desc')
                ->limit($page['min'], $page['pageCount'])
                ->select();
            foreach ($message as $key => $value) {
                $message[$key]['message']    = json_decode($value['message'], true);
                $userId                      = ($value['sender_id'] == $uid) ? $value['recipient_id'] : $value['sender_id'];
                $message[$key]['userInfo']   = self::getImUserInfo($userId);
                $message[$key]['friendship'] = Db::name('fastim_friendship')
                    ->where('user_id', $uid)
                    ->where('friend_id', $userId)
                    ->value('user_id');
            }

            $windowType = 'new_friends_message';
        } elseif ($sessionInfo['chat_id'] == 2) {
            // 群通知
            $message = Db::name('fastim_message')
                ->whereIn('type', 'group_apply,group_notice,group_invitation')
                ->where('sender_id|recipient_id', $uid)
                ->order('createtime desc')
                ->limit($page['min'], $page['pageCount'])
                ->select();
            foreach ($message as $key => $value) {
                $message[$key]['message'] = json_decode($value['message'], true);

                // 群通知消息
                $message[$key]['groupChatInfo'] = self::getImGroupChat($message[$key]['group_id']);

                if ($value['type'] == 'group_invitation') {
                    $message[$key]['invitationUser'] = self::getImUserInfo($message[$key]['message']['invitation_user']);
                    $message[$key]['invitedUser']    = self::getImUserInfo($message[$key]['message']['invited_user']);

                    $message[$key]['message'] = array_merge($message[$key]['message'], self::invitationMessageResult($message[$key]));

                } elseif ($value['type'] == 'group_apply' && $value['recipient_id'] == $uid) {
                    // x申请加入群聊y，获取发送人信息
                    $message[$key]['userInfo'] = self::getImUserInfo($value['sender_id']);
                } elseif ($value['type'] == 'group_notice') {
                    // x退出了群聊
                    if ($message[$key]['message']['action'] == 'quit') {
                        $message[$key]['userInfo'] = self::getImUserInfo($message[$key]['message']['quit_user']);
                    } elseif ($message[$key]['message']['action'] == 'refuse') {
                        $message[$key]['userInfo'] = self::getImUserInfo($message[$key]['message']['refuse_user']);
                    }
                }
            }

            $windowType = 'group_notice';
        } elseif ($sessionInfo['chat_id'] == 3) {
            // 客服消息
            $message = Db::name('fastim_message')
                ->where('session_id', $sessionInfo['id'])
                ->where(function ($query) use ($uid) {
                    $query->where('recipient_id', $uid)->where('status', '<', 4);
                    $query->whereOr('sender_id', $uid);
                })
                ->order('createtime desc,id desc')
                ->limit($page['min'], $page['pageCount'])
                ->select();

            foreach ($message as $key => &$value) {
                if ($value['sender_id']) {
                    $value['csr'] = self::getImUserInfo($value['sender_id']);
                }
                $value['sender'] = ($value['recipient_id'] == $uid) ? 'you' : 'me';
                if (in_array($value['type'], CommonCode::$messageJsonTypes)) {
                    $value['message'] = json_decode($value['message'], true);
                } else {
                    $value['message'] = htmlspecialchars_decode($value['message']);
                }

                if ($value['type'] == 'voice') {
                    // 获取语音播放状态
                    $value['voice_playback'] = Db::name('fastim_reading_log')
                        ->where('type', 1)
                        ->where('user_id', $uid)
                        ->where('data_id', $value['id'])
                        ->value('id');
                }
            }

            $windowType = 'csr_message';
        }

        return [
            'message'    => $message,
            'windowType' => $windowType
        ];
    }

    /**
     * 邀请好友入群消息的 result 处理
     * result:0=未处理,1=同意,2=拒绝,3=失效
     * @param array $message 消息
     * @return array
     */
    public static function invitationMessageResult($message)
    {
        if ($message['message']['identity'] == 'admin') {
            $leaderResult = $message['message']['result'];
            $userResult   = 0;

            $invitedUserSession = Common::imSession('service', $message['message']['invited_user'], 2);
            $invitationMessage  = Db::name('fastim_message')
                ->where('type', 'group_invitation')
                ->where('session_id', $invitedUserSession['id'])
                ->select();
            foreach ($invitationMessage as $mkey => $mvalue) {
                $mvalue['message'] = json_decode($mvalue['message'], true);
                if (isset($mvalue['message']['admin_message_id']) && $mvalue['message']['admin_message_id'] == $message['id']) {
                    $userResult = $mvalue['message']['result'];
                    break;
                }
            }
        } else {
            $userResult   = $message['message']['result'];
            $leaderResult = 0;

            $invitationMessage = Db::name('fastim_message')
                ->where('id', $message['message']['admin_message_id'])
                ->find();
            if ($invitationMessage) {
                $invitationMessage['message'] = json_decode($invitationMessage['message'], true);
                $leaderResult                 = $invitationMessage['message']['result'];
            }
        }

        return [
            'user_result'   => $userResult,
            'leader_result' => $leaderResult
        ];
    }

    /**
     * 阅读服务号消息
     * @param int $chatId 服务号ID
     * @param int $uid 阅读用户
     * @param int $sessionId 会话ID
     */
    public static function readServiceMessage($chatId, $uid, $sessionId)
    {
        if ($chatId == 1) {
            // 好友申请
            Db::name('fastim_message')
                ->where('type', 'friend_apply')
                ->where('recipient_id', $uid)
                ->where('status', 0)
                ->update(['status' => 1]);
        } elseif ($chatId == 2) {
            // 群通知
            Db::name('fastim_message')
                ->whereIn('type', 'group_apply,group_notice,group_invitation')
                ->where('recipient_id', $uid)
                ->where('status', 0)
                ->update(['status' => 1]);
        } elseif ($chatId == 3) {
            Db::name('fastim_message')
                ->where('session_id', $sessionId)
                ->where('recipient_id', $uid)
                ->where('status', 0)
                ->update(['status' => 1]);
        }
    }

    /**
     * 群聊聊天记录
     * @param array $sessionInfo 会话资料
     * @param array $page 页码
     * @param int $uid 加载用户
     * @return array              聊天记录
     */
    public static function loadGroupChatRecord($sessionInfo, $page, $uid)
    {
        // 用户入群时间
        $userJoinTime = Db::name('fastim_group_user')
            ->where('group_id', $sessionInfo['chat_id'])
            ->where('user_id', $uid)
            ->value('jointime');
        $userJoinTime = $userJoinTime ? $userJoinTime : 0;// 防null无法比较大小

        $message = Db::name('fastim_message')
            ->where('group_id', $sessionInfo['chat_id'])
            ->where(function ($query) use ($uid) {
                // 失败的消息不显示给其他群成员
                $query->where('sender_id', $uid);
                $query->whereOr('status', '<', 4);
            })
            ->where(function ($query) use ($sessionInfo, $userJoinTime, $uid) {
                $typeSql = CommonCode::nonPrivilegedMessage(true);
                if ($sessionInfo['sessionUser']['history_message'] == 0) {
                    // 以下代码虽看起来重复判断了`createtime`，但没有逻辑问题，改动后部分情景下会计算错误
                    $query->where("type {$typeSql} OR (type='system' AND (recipient_id={$uid} OR (recipient_id=0 AND createtime>={$userJoinTime})))")
                        ->where('createtime', '>=', $userJoinTime);
                } else {
                    // 群通知:只显示发送时间大于用户加入时间的或直接发送给当前用户的群系统消息
                    $query->where("type {$typeSql} OR (type='system' AND (recipient_id={$uid} OR (recipient_id=0 AND createtime>={$userJoinTime})))");
                }
            })
            ->order('createtime desc,id desc')
            ->limit($page['min'], $page['pageCount'])
            ->select();

        foreach ($message as $key => $value) {
            $senderCollection[]      = $value['sender_id'];
            $message[$key]['sender'] = ($value['sender_id'] == $uid) ? 'me' : 'you';
            if (in_array($value['type'], CommonCode::$messageJsonTypes)) {
                $message[$key]['message'] = json_decode($value['message'], true);
            } else {
                $message[$key]['message'] = htmlspecialchars_decode($value['message']);
            }

            if ($value['type'] == 'voice') {
                // 获取语音播放状态
                $message[$key]['voice_playback'] = Db::name('fastim_reading_log')
                    ->where('type', 1)
                    ->where('user_id', $uid)
                    ->where('data_id', $value['id'])
                    ->value('id');
            }
        }

        // 取得所有发送人的资料
        if (isset($senderCollection) && $senderCollection) {
            $senderCollection = array_unique($senderCollection);
            foreach ($senderCollection as $key => $value) {
                $senderInfo[$value] = self::getImUserInfo($value, false, $uid);
                // 取得用户群聊昵称
                $groupUserNickname = Db::name('fastim_group_user')
                    ->where('group_id', $sessionInfo['chat_id'])
                    ->where('user_id', $value)
                    ->value('nickname');
                if ($groupUserNickname) {
                    $senderInfo[$value]['nickname'] = $groupUserNickname;
                    // 必要时重新生成头像
                    $defaultAvatar                = preg_match('/^data:image/', $senderInfo[$value]['avatar']);
                    $senderInfo[$value]['avatar'] = self::avatarSrc([
                        $defaultAvatar ? '' : $senderInfo[$value]['avatar']
                    ], $groupUserNickname);
                }
            }
            foreach ($message as $key => $value) {
                if (array_key_exists($value['sender_id'], $senderInfo)) {
                    $message[$key]['userInfo'] = $senderInfo[$value['sender_id']];
                } else {
                    // 未查到用户资料
                    $message[$key]['userInfo'] = [
                        'id'     => 0,
                        'avatar' => self::avatarSrc(false, '')
                    ];
                }
            }
        }

        return $message;
    }

    /**
     * 格式化群聊公告
     * @param array $unreadFixedMsg 群聊公告资料
     */
    public static function formatGroupChatNotice($unreadFixedMsg, $userId, $record = false)
    {
        if ($unreadFixedMsg) {
            $unreadFixedMsg['images']         = $unreadFixedMsg['images'] ? explode(',', trim($unreadFixedMsg['images'], ',')) : '';
            $unreadFixedMsg['createtime']     = self::formatTimeAlong($unreadFixedMsg['createtime']);
            $unreadFixedMsg['publisher']      = self::getImUserInfo($unreadFixedMsg['publisher'], false, $userId);
            $unreadFixedMsg['reading_number'] = Db::name('fastim_reading_log')
                ->where('type', 0)
                ->where('group_id', $unreadFixedMsg['group_id'])
                ->where('data_id', $unreadFixedMsg['id'])
                ->count();

            if (isset($unreadFixedMsg['receipt']) && $unreadFixedMsg['receipt']) {
                // 是否已确认
                $log = Db::name('fastim_reading_log')
                    ->field('id, receipt')
                    ->where('type', 0)
                    ->where('group_id', $unreadFixedMsg['group_id'])
                    ->where('user_id', $userId)
                    ->where('data_id', $unreadFixedMsg['id'])
                    ->find();
                if ($log && $log['receipt']) {
                    $unreadFixedMsg['receipted'] = true;
                }

                // 多少人已确认
                $unreadFixedMsg['receiptedCount'] = Db::name('fastim_reading_log')
                    ->where('type', 0)
                    ->where('group_id', $unreadFixedMsg['group_id'])
                    ->where('data_id', $unreadFixedMsg['id'])
                    ->where('receipt', 1)
                    ->count();
            }

            if ($record) {
                // 记录阅读
                self::readGroupChatNotice($unreadFixedMsg['group_id'], $userId, $unreadFixedMsg['id']);
            }
            return $unreadFixedMsg;
        }
        return false;
    }

    public static function readGroupChatNotice($groupId, $userId, $dataId, $type = '', $receipt = false)
    {
        if ($type == 'all') {
            $unreadFixedMsg = Db::name('fastim_group_chat_notice')
                ->alias('cn')
                ->field('cn.*,r.id AS rid')
                ->join('fastim_reading_log r', 'r.type=0 AND r.user_id=:user_id AND r.data_id=cn.id', 'LEFT')
                ->where('cn.group_id', $groupId)
                ->where('cn.deletetime', null)
                ->where('r.id', null)
                ->bind([
                    'user_id' => $userId
                ])
                ->order('createtime desc')
                ->select();
            $insert         = false;
            foreach ($unreadFixedMsg as $item) {
                $insert[] = [
                    'type'        => 0,
                    'group_id'    => $groupId,
                    'user_id'     => $userId,
                    'data_id'     => $item['id'],
                    'receipt'     => $receipt ? 1 : 0,
                    'createtime'  => time(),
                    'confirmtime' => $receipt ? time() : null
                ];
            }
            if ($insert) {
                Db::name('fastim_reading_log')->insertAll($insert);
            }
            return true;
        }

        $reading = Db::name('fastim_reading_log')
            ->where('type', 0)
            ->where('group_id', $groupId)
            ->where('user_id', $userId)
            ->where('data_id', $dataId)
            ->value('id');
        if ($reading) {
            return true;
        }
        Db::name('fastim_reading_log')
            ->insert([
                'type'        => 0,
                'group_id'    => $groupId,
                'user_id'     => $userId,
                'data_id'     => $dataId,
                'receipt'     => $receipt ? 1 : 0,
                'createtime'  => time(),
                'confirmtime' => $receipt ? time() : null
            ]);
        return true;
    }

    /**
     * 单聊聊天记录
     * @param int $session_id 会话ID
     * @param array $page 页码
     * @param int $uid 加载记录的用户
     * @return array|bool             聊天记录
     */
    public static function loadSingleChatRecord($session_id, $page, $uid)
    {
        $message = Db::name('fastim_message')
            ->where('session_id', $session_id)
            ->where('deleteuser IS NULL OR deleteuser!=:uid')
            ->where(function ($query) use ($uid) {
                $query->where('recipient_id', $uid)->where('status', '<', 4);
                $query->whereOr('sender_id', $uid);
            })
            ->bind([
                'uid' => $uid
            ])
            ->order('createtime desc,id desc')
            ->limit($page['min'], $page['pageCount'])
            ->select();

        foreach ($message as $key => &$value) {
            $value['sender'] = ($value['sender_id'] == $uid) ? 'me' : 'you';
            if (in_array($value['type'], CommonCode::$messageJsonTypes)) {
                $value['message'] = json_decode($value['message'], true);
            } else {
                $value['message'] = htmlspecialchars_decode($value['message']);
            }

            if ($value['type'] == 'voice') {
                // 获取语音播放状态
                $value['voice_playback'] = Db::name('fastim_reading_log')
                    ->where('type', 1)
                    ->where('user_id', $uid)
                    ->where('data_id', $value['id'])
                    ->value('id');
            }
        }
        return $message;
    }

    /**
     * 将消息记录按时间进行分组
     * @param array $message 消息记录
     * @return array
     */
    public static function groupByTime($message, $orderBy = 'asc')
    {
        if (!$message) {
            return [];
        }

        $messageTemp = [];
        $createtime  = $message[0]['createtime'];
        foreach ($message as $key => $value) {

            if ($orderBy == 'asc') {
                $diff = $createtime - $value['createtime'];
            } else {
                $diff = $value['createtime'] - $createtime;
            }

            if ($diff < 3600) {
                $messageTemp[$createtime][] = $value;
            } else {
                $createtime                 = $value['createtime'];
                $messageTemp[$createtime][] = $value;
            }
        }
        unset($message);
        foreach ($messageTemp as $key => $value) {
            $message[] = [
                'datetime' => self::formatTime($key),
                'data'     => $value
            ];
        }
        unset($messageTemp);
        return $message;
    }

    /**
     * 获取分组详情
     * @param int $id 分组ID
     * @return array
     */
    public static function getGroupInfo($id)
    {
        // type分组类型:0=好友,1=群聊,2=收藏
        $groupNames = [
            'all_friends'    => [
                'type' => 0,
                'name' => '全部好友'
            ],
            'common'         => [
                'type' => 0,
                'name' => '常用联系人'
            ],
            'all_group'      => [
                'type' => 1,
                'name' => '全部群聊'
            ],
            'all_collection' => [
                'type' => 2,
                'name' => '全部收藏'
            ],
            'default'        => [
                'type' => 2,
                'name' => '笔记'
            ],
            'image'          => [
                'type' => 2,
                'name' => '图片'
            ],
            'audio'          => [
                'type' => 2,
                'name' => '音频',
            ],
            'video'          => [
                'type' => 2,
                'name' => '视频',
            ],
            'file'           => [
                'type' => 2,
                'name' => '文件'
            ],
            'link'           => [
                'type' => 2,
                'name' => '链接'
            ],
            'other'          => [
                'type' => 2,
                'name' => '其他'
            ]
        ];

        if (array_key_exists($id, $groupNames)) {
            $groupNames[$id]['id'] = $id;
            return $groupNames[$id];
        } else {
            return Db::name('fastim_group')->where('id', $id)->find();
        }
    }

    /**
     * 获取分组列表
     * @param int $uid 获取用户
     * @param int $type 分组类型
     * @return array       用户的分组
     */
    public static function getGroups($uid, $type)
    {
        if ($type == 0) {
            // 好友分组
            $default = [
                [
                    'id'   => 'common',
                    'name' => '常用联系人'
                ],
                [
                    'id'   => 'all_friends',
                    'name' => '全部好友'
                ]
            ];
        } elseif ($type == 1) {
            // 群聊分组
            $default = [
                [
                    'id'   => 'all_group',
                    'name' => '全部群聊'
                ]
            ];
        } elseif ($type == 2) {
            // 收藏分组
            $default = [
                [
                    'id'   => 'all_collection',
                    'name' => '全部收藏'
                ],
                [
                    'id'   => 'default',
                    'name' => '笔记'
                ],
                [
                    'id'   => 'image',
                    'name' => '图片'
                ],
                [
                    'id'   => 'audio',
                    'name' => '音频'
                ],
                [
                    'id'   => 'video',
                    'name' => '视频'
                ],
                [
                    'id'   => 'file',
                    'name' => '文件'
                ],
                [
                    'id'   => 'link',
                    'name' => '链接'
                ],
                [
                    'id'   => 'other',
                    'name' => '其他'
                ]
            ];
        }

        $group = Db::name('fastim_group')->where('user_id', $uid)->where('type', $type)->order('weigh asc')->select();

        $res = array_merge($default, $group);
        return $res ? $res : [];
    }

    /**
     * 获取最后一条未读消息
     * @param int $getUserId 获取用户
     * @param bool $initializeTime 初始化请求时间(用于标记只读取:用户已进入网站但未连接ws期间的消息)
     * @return array|bool
     */
    public static function getUnreadMessage($getUserId, $initializeTime = false)
    {
        $message = Db::name('fastim_message')
            ->alias('m')
            ->field('m.*,gc.history_message,gu.last_read_id,gu.jointime')
            ->join('fastim_group_chat gc', 'm.group_id=gc.id', 'LEFT')
            ->join('fastim_group_user gu', "gu.group_id=gc.id AND gu.user_id='{$getUserId}'", 'LEFT')
            ->where("m.group_id=0 AND m.recipient_id='{$getUserId}' AND m.status=0")
            ->whereOr(function ($query) use ($getUserId) {
                $query->where('gu.user_id', '>', 0)
                    ->where('m.id>gu.last_read_id')
                    ->where(function ($query) use ($getUserId) {
                        $typeSql       = CommonCode::nonPrivilegedMessage(true);
                        $systemTypeSql = CommonCode::nonPrivilegedMessage(true, ['system']);
                        $query->where("gc.history_message=0 AND m.createtime>=gu.jointime AND m.type {$systemTypeSql} AND (m.recipient_id='{$getUserId}' OR m.recipient_id=0)");
                        $query->whereOr("gc.history_message=1 AND (m.type {$typeSql} OR ((m.type='system' AND m.recipient_id='{$getUserId}') OR (m.recipient_id=0 AND m.createtime>=gu.jointime)))");
                    })
                    ->where('m.status', 0)
                    ->where('m.sender_id', '<>', $getUserId);
            })
            ->where(function ($query) use ($initializeTime) {
                if ($initializeTime) {
                    $query->where('m.createtime', '>', $initializeTime);
                }
            })
            ->order('m.createtime DESC')
            ->find();

        if (!$message) {
            return false;
        }

        if ($message['session_id']) {
            $sessionInfo = self::sessionInfo($message['session_id'], $getUserId);
        } elseif ($message['group_id']) {
            // 群系统消息无session_id
            $sessionInfo = self::imSession('group', $getUserId, $message['group_id']);
            $sessionInfo = self::sessionInfo($sessionInfo['id'], $getUserId);
        }

        $message = self::formatLastMessage($message, $getUserId, $sessionInfo['type']);
        return [
            'messageData' => $message,
            'sessionInfo' => $sessionInfo,
        ];
    }

    /**
     * 获取未读消息数
     * @param int $getUserId 获取用户
     * @param array $sessionInfo 会话详情
     * @param int $sessionId 会话ID
     * @return int 未读消息数
     */
    public static function getUnreadMessagesNumber($getUserId, $sessionInfo = null, $sessionId = 0)
    {
        if (!$sessionInfo) {
            $sessionInfo = self::sessionInfo($sessionId, $getUserId);
        }

        $number = 0;

        if ($sessionInfo['type'] == 'single') {
            $number = Db::name('fastim_message')
                ->where('session_id', $sessionInfo['id'])
                ->where('recipient_id', $getUserId)
                ->where('status', 0)
                ->count('id');
        } elseif ($sessionInfo['type'] == 'group') {
            $groupUserInfo = Db::name('fastim_group_user')
                ->where('group_id', $sessionInfo['chat_id'])
                ->where('user_id', $getUserId)
                ->find();
            $userJoinTime  = (isset($groupUserInfo['jointime']) && $groupUserInfo['jointime']) ? (int)$groupUserInfo['jointime'] : 0;

            $historyMessage = 1;// 是否允许查看入群前的消息
            if (isset($sessionInfo['sessionUser'])) {
                $historyMessage = $sessionInfo['sessionUser']['history_message'];
            } elseif (isset($sessionInfo['pushUser'])) {
                $historyMessage = $sessionInfo['pushUser']['history_message'];
            }

            $number = Db::name('fastim_message')
                ->where('group_id', $sessionInfo['chat_id'])
                ->where(function ($query) use ($historyMessage, $userJoinTime, $getUserId) {
                    $typeSql = CommonCode::nonPrivilegedMessage(true);
                    if ($historyMessage == 0) {
                        // 以下代码虽看起来重复判断了`createtime`，但没有逻辑问题，改动后部分情景下会计算错误
                        $query->where("type {$typeSql} OR (type='system' AND (recipient_id={$getUserId} OR (recipient_id=0 AND createtime>={$userJoinTime})))")
                            ->where('createtime', '>=', $userJoinTime);
                    } else {
                        // 群通知:只显示发送时间大于用户加入时间的或直接发送给当前用户的群系统消息
                        $query->where("type {$typeSql} OR (type='system' AND (recipient_id={$getUserId} OR (recipient_id=0 AND createtime>={$userJoinTime})))");
                    }
                })
                ->where('id', '>', $groupUserInfo['last_read_id'] ? $groupUserInfo['last_read_id'] : 0)
                ->where('status', 0)
                ->where('sender_id', '<>', $getUserId)
                ->count('id');
        } elseif ($sessionInfo['type'] == 'service') {

            if ($sessionInfo['chat_id'] == 1) {
                $number = Db::name('fastim_message')
                    ->where('type', 'friend_apply')
                    ->where('recipient_id', $getUserId)
                    ->where('status', 0)
                    ->count('id');
            } elseif ($sessionInfo['chat_id'] == 2) {
                $number = Db::name('fastim_message')
                    ->whereIn('type', 'group_apply,group_notice,group_invitation')
                    ->where('recipient_id', $getUserId)
                    ->where('status', 0)
                    ->count('id');
            } elseif ($sessionInfo['chat_id'] == 3) {
                $number = Db::name('fastim_message')
                    ->where('session_id', $sessionInfo['id'])
                    ->where('recipient_id', $getUserId)
                    ->where('status', 0)
                    ->count('id');
            }
        }

        return ($number < 99) ? $number : '99+';
    }

    /**
     * 获取一个会话的最后一条消息
     * @param int $getUserId 获取用户
     * @param array $sessionInfo 会话资料
     * @param int $sessionId 会话ID
     * @return array
     */
    public static function getLastMessage($getUserId = 0, $sessionInfo = null, $sessionId = 0)
    {
        if (!$sessionInfo) {
            $sessionInfo = self::sessionInfo($sessionId, $getUserId);
        }

        $lastMessage = false;

        if ($sessionInfo['type'] == 'single') {
            $lastMessage = Db::name('fastim_message')
                ->field('type, sender_id, message, createtime')
                ->where('session_id', $sessionInfo['id'])
                ->where('deleteuser IS NULL OR deleteuser!=:uid')
                ->where(function ($query) use ($getUserId) {
                    $query->where('recipient_id', $getUserId)->where('status', '<', 4);
                    $query->whereOr('sender_id', $getUserId);
                })
                ->bind([
                    'uid' => $getUserId
                ])
                ->order('createtime desc,id desc')
                ->find();
        } elseif ($sessionInfo['type'] == 'group') {
            // 用户入群时间
            $userJoinTime = Db::name('fastim_group_user')
                ->where('group_id', $sessionInfo['chat_id'])
                ->where('user_id', $getUserId)
                ->value('jointime');
            $userJoinTime = $userJoinTime ? $userJoinTime : 0;// 防null无法比较大小

            $historyMessage = 1;// 是否允许查看入群前的消息
            if (isset($sessionInfo['sessionUser'])) {
                $historyMessage = $sessionInfo['sessionUser']['history_message'];
            } elseif (isset($sessionInfo['pushUser'])) {
                $historyMessage = $sessionInfo['pushUser']['history_message'];
            }

            $lastMessage = Db::name('fastim_message')
                ->field('group_id, type, sender_id, message, createtime')
                ->where('group_id', $sessionInfo['chat_id'])
                ->where(function ($query) use ($getUserId) {
                    $query->where('sender_id', $getUserId);
                    $query->whereOr('status', '<', 4);
                })
                ->where(function ($query) use ($historyMessage, $userJoinTime, $getUserId) {

                    $typeSql = CommonCode::nonPrivilegedMessage(true);
                    if ($historyMessage == 0) {
                        // 以下代码虽看起来重复判断了`createtime`，但没有逻辑问题，改动后部分情景下会计算错误
                        $query->where("type {$typeSql} OR (type='system' AND (recipient_id={$getUserId} OR (recipient_id=0 AND createtime>={$userJoinTime})))")
                            ->where('createtime', '>=', $userJoinTime);
                    } else {
                        // 群通知:只显示发送时间大于用户加入时间的或直接发送给当前用户的群系统消息
                        $query->where("type {$typeSql} OR (type='system' AND (recipient_id={$getUserId} OR (recipient_id=0 AND createtime>={$userJoinTime})))");
                    }
                })
                ->order('createtime desc,id desc')
                ->find();

            // 取得未读群通知
            $unreadFixedMsg = Db::name('fastim_group_chat_notice')
                ->alias('cn')
                ->field('cn.*,r.id AS rid')
                ->join('fastim_reading_log r', 'r.type=0 AND r.user_id=:user_id AND r.data_id=cn.id', 'LEFT')
                ->where('cn.group_id', $sessionInfo['chat_id'])
                ->where('cn.deletetime', null)
                ->where('r.id', null)
                ->bind([
                    'user_id' => $getUserId
                ])
                ->count('*');
            if ($unreadFixedMsg) {
                $unreadFixedMsg = [
                    'message' => '有新的公告',
                    'count'   => $unreadFixedMsg
                ];
            } else {
                // 取得未读@消息
                $unreadFixedMsg = Db::name('fastim_reading_log')
                    ->where('type', 2)
                    ->where('group_id', $sessionInfo['chat_id'])
                    ->where('user_id', $getUserId)
                    ->where('receipt', 0)
                    ->order('createtime asc')
                    ->find();
                if ($unreadFixedMsg) {
                    $unreadFixedMsg = [
                        'message' => '有人@你',
                        'info'    => $unreadFixedMsg
                    ];
                }
            }
        } elseif ($sessionInfo['type'] == 'service') {
            if ($sessionInfo['chat_id'] == 1) {
                // 好友验证
                $lastMessage = Db::name('fastim_message')
                    ->field('type, sender_id, recipient_id, message, createtime')
                    ->where('type', 'friend_apply')
                    ->where('sender_id|recipient_id', $getUserId)
                    ->order('createtime desc')
                    ->find();
            } elseif ($sessionInfo['chat_id'] == 2) {
                // 群通知
                $lastMessage = Db::name('fastim_message')
                    ->whereIn('type', 'group_apply,group_notice,group_invitation')
                    ->where('sender_id|recipient_id', $getUserId)
                    ->order('createtime desc,id desc')
                    ->find();
            } elseif ($sessionInfo['chat_id'] == 3) {
                // 客服消息
                $lastMessage = Db::name('fastim_message')
                    ->where('session_id', $sessionInfo['id'])
                    ->where('sender_id|recipient_id', $getUserId)
                    ->order('createtime desc,id desc')
                    ->find();
            }
        }

        if ($lastMessage) {
            return [
                'last_time'        => self::formatTimeAlong($lastMessage['createtime']),
                'last_message'     => self::formatLastMessage($lastMessage, $getUserId, $sessionInfo['type']),
                'unread_fixed_msg' => isset($unreadFixedMsg) ? $unreadFixedMsg : ''
            ];
        } else {
            $bio = '';
            if (isset($sessionInfo['sessionUser'])) {
                $bio = $sessionInfo['sessionUser']['bio'];
            } elseif (isset($sessionInfo['pushUser'])) {
                $bio = $sessionInfo['pushUser']['bio'];
            }

            return [
                'last_time'        => self::formatTimeAlong($sessionInfo['createtime']),
                'last_message'     => $bio,
                'unread_fixed_msg' => isset($unreadFixedMsg) ? $unreadFixedMsg : ''
            ];
        }
    }

    /**
     * 格式化最后消息
     * @param array $message 消息数据
     * @param integer $getUserId 获取消息人,群通知和好友申请时传递
     * @param string $type 会话类型,如果是群聊会话,则在消息前带上发信人昵称
     * @return string
     */
    public static function formatLastMessage($message, $getUserId = 0, $type = 'single')
    {
        $types = [
            'image' => '[图片]',
            'audio' => '[音频]',
            'video' => '[视频]',
            'file'  => '[文件]',
            'link'  => '[链接]',
            'voice' => '[语音消息]'
        ];
        if ($message['type'] == 'default') {
            // 匹配所有的img标签,去除所有其他标签
            $message['message'] = htmlspecialchars_decode($message['message']);
            $preg               = '/<img.*?title="(.+?)".*?>/is';
            preg_match_all($preg, $message['message'], $result, PREG_PATTERN_ORDER);
            if (isset($result[1][0])) {
                foreach ($result[1] as $key => $value) {
                    $result[1][$key] = "[{$value}]";
                }
                $message['message'] = strip_tags(str_replace($result[0], $result[1], $message['message']));
            } else {
                $preg = '/<img.*?src="(.+?)".*?>/is';
                preg_match_all($preg, $message['message'], $result, PREG_PATTERN_ORDER);
                $message['message'] = strip_tags(str_replace($result[0], '[图片]', $message['message']));
            }

            if ($type == 'group' && isset($message['sender_id'])) {
                if (isset($message['group_id'])) {
                    $groupUserNickname = Db::name('fastim_group_user')
                        ->where('group_id', $message['group_id'])
                        ->where('user_id', $message['sender_id'])
                        ->value('nickname');
                }
                if (!isset($groupUserNickname) || !$groupUserNickname) {
                    $senderInfo        = self::getImUserInfo($message['sender_id']);
                    $groupUserNickname = $senderInfo['nickname'];
                }
                $message['message'] = $groupUserNickname . ':' . $message['message'];
            }

        } elseif (array_key_exists($message['type'], $types)) {
            $message['message'] = $types[$message['type']];
        } elseif ($message['type'] == 'system') {
            // 系统消息
            if (!is_array($message['message'])) {
                $message['message'] = json_decode($message['message'], true);
            }
            $message['message'] = $message['message']['message'];
        } elseif ($message['type'] == 'group_apply') {
            // 入群申请
            $info = self::getImGroupChat($message['group_id']);
            if ($message['sender_id'] == $getUserId) {
                $message['message'] = '申请加入群聊' . $info['nickname'];
            } else {
                $userInfo           = self::getImUserInfo($message['sender_id']);
                $message['message'] = $userInfo['nickname'] . '申请加入群聊' . $info['nickname'];
            }
        } elseif ($message['type'] == 'group_notice') {
            // 群通知
            if (!is_array($message['message'])) {
                $message['message'] = json_decode($message['message'], true);
            }
            $info = self::getImGroupChat($message['group_id']);

            if ($message['message']['action'] == 'quit') {
                $userInfo           = self::getImUserInfo($message['message']['quit_user']);
                $message['message'] = $userInfo['nickname'] . '退出了群聊' . $info['nickname'];
            } elseif ($message['message']['action'] == 'dismiss') {
                $message['message'] = $message['message']['notice'] . $info['nickname'];
            } elseif ($message['message']['action'] == 'refuse') {
                $userInfo = self::getImUserInfo($message['message']['refuse_user']);

                // getUserId 获取消息的用户:被邀请人、申请人、管理员、邀请人
                // $message['message']['refuse_user'] 实际被拒绝的用户
                // $message['sender_id'] 执行拒绝操作的用户
                // $info['leader'] 群主

                if ($getUserId == $message['message']['refuse_user']) {
                    if ($message['sender_id'] == $info['leader']) {
                        $message['message'] = '管理员拒绝你加入群聊' . $info['nickname'];
                    } elseif ($message['sender_id'] == $getUserId) {
                        $message['message'] = '你拒绝了加入群聊' . $info['nickname'];
                    }
                } elseif ($getUserId == $info['leader']) {
                    if ($message['sender_id'] == $info['leader']) {
                        $message['message'] = '你拒绝了' . $userInfo['nickname'] . '加入群聊' . $info['nickname'];
                    } else {
                        $message['message'] = $userInfo['nickname'] . '拒绝加入群聊' . $info['nickname'];
                    }
                } else {
                    if ($message['sender_id'] == $info['leader']) {
                        $message['message'] = '管理员拒绝了' . $userInfo['nickname'] . '加入群聊' . $info['nickname'];
                    } else {
                        $message['message'] = $userInfo['nickname'] . '拒绝加入群聊' . $info['nickname'];
                    }
                }
            } elseif ($message['message']['action'] == 'propose') {
                $message['message'] = '管理员已将你请出群聊' . $info['nickname'];
            } else {
                $message['message'] = $message['message']['notice'];
            }
        } elseif ($message['type'] == 'group_invitation') {
            // 邀请好友入群
            if (!is_array($message['message'])) {
                $message['message'] = json_decode($message['message'], true);
            }
            $info           = self::getImGroupChat($message['group_id']);
            $invitationUser = self::getImUserInfo($message['message']['invitation_user']);
            if ($message['message']['identity'] == 'admin') {
                $invitedUser        = self::getImUserInfo($message['message']['invited_user']);
                $message['message'] = $invitationUser['nickname'] . '邀请' . $invitedUser['nickname'] . '加入群聊' . $info['nickname'];
            } else {
                $message['message'] = $invitationUser['nickname'] . '邀请你加入群聊' . $info['nickname'];
            }
        } elseif ($message['type'] == 'friend_apply') {
            // 好友申请
            if (!is_array($message['message'])) {
                $message['message'] = json_decode($message['message'], true);
            }
            if ($message['sender_id'] == $getUserId) {
                $info = self::getImUserInfo($message['recipient_id']);
                if ($message['message']['method'] == 'notice') {
                    if ($message['message']['result'] == 'refuse') {
                        $message['message'] = '你已经拒绝了' . $info['nickname'] . '的好友申请。';
                    } else {
                        $message['message'] = $message['message']['msg'];
                    }
                } elseif ($message['message']['method'] == 'apply') {
                    $message['message'] = '申请添加' . $info['nickname'] . '为好友';
                }
            } else {
                $info = self::getImUserInfo($message['sender_id']);
                if ($message['message']['method'] == 'notice') {
                    if ($message['message']['result'] == 'refuse') {
                        $message['message'] = $info['nickname'] . '拒绝了你的好友申请。';
                    } else {
                        $message['message'] = $message['message']['msg'];
                    }
                } elseif ($message['message']['method'] == 'apply') {
                    $message['message'] = $info['nickname'] . '申请添加你为好友';
                }
            }
        } elseif ($message['type'] == 'kbs_list') {
            if (!is_array($message['message'])) {
                $message['message'] = json_decode($message['message'], true);
            }
            return $message['message']['title'];
        } elseif ($message['type'] == 'group_chat_notice') {
            // 群公告
            if (!is_array($message['message'])) {
                $message['message'] = json_decode($message['message'], true);
            }
            $userInfo           = self::getImUserInfo($message['sender_id']);
            $message['message'] = $userInfo['nickname'] . ':' . $message['message']['content'];
        }
        return $message['message'];
    }

    /**
     * 格式化会话时间-按天顺时针格式化
     * @param int time 时间戳
     * @return string
     */
    public static function formatTimeAlong($time = null)
    {
        if (!$time) {
            return date('H:i');
        }

        $nowDate  = getdate(time());
        $timeDate = getdate($time);

        if (($nowDate['year'] === $timeDate['year']) && ($nowDate['yday'] === $timeDate['yday'])) {
            return date('H:i', $time);
        } else {
            return self::formatTime($time);
        }
    }

    /**
     * 格式化时间-按时间差逆时针格式化
     * @param int time 时间戳
     * @return string
     */
    public static function formatTime($time = null)
    {
        $nowTime = time();
        $time    = ($time === null || $time > $nowTime || $time == $nowTime) ? $nowTime - 1 : intval($time);
        $lang    = [
            '%d second%s ago' => '%d秒前',
            '%d minute%s ago' => '%d分钟前',
            '%d hour%s ago'   => '%d小时前',
            '%d day%s ago'    => '%d天前',
            '%d week%s ago'   => '%d周前',
            '%d month%s ago'  => '%d月前',
            '%d year%s ago'   => '%d年前',
        ];
        \think\Lang::set($lang);
        $date = \fast\Date::human($time);
        return $date;
    }

    /**
     * 群聊屏蔽状态
     * @param  [type] $id  群聊
     * @param  [type] $uid 用户
     * @return [type]      屏蔽状态
     */
    public static function getGroupChatShield($id, $uid)
    {
        $shield = Db::name('fastim_group_user')
            ->where('group_id', $id)
            ->where('user_id', $uid)
            ->value('block_messages');
        return $shield ? true : false;
    }

    /**
     * 获得用户资料
     * @param int $id 用户ID
     * @param bool $detail 获得详细的资料
     * @param int $getUserId 获取人ID(用于验证权限)
     * @return array|bool
     */
    public static function getImUserInfo($id, $detail = false, $getUserId = false)
    {

        if ($detail) {
            $info = Db::name('fastim_user')
                ->alias('u')
                ->field('u.id,u.type,u.avatar,u.nickname,u.mobile,u.email,u.age,u.birthday,u.occupation,u.company,u.bio,u.gender,u.status,u.welcome_msg,fu.avatar as fu_avatar,fu.nickname as fu_nickname,fu.bio as fu_bio,fu.gender as fu_gender,fu.email as fu_email,fu.mobile as fu_mobile,fu.birthday as fu_birthday,a.avatar as a_avatar,a.nickname as a_nickname,a.email as a_email')
                ->join('user fu', 'u.user_id=fu.id', 'LEFT')
                ->join('admin a', 'u.admin_id=a.id', 'LEFT')
                ->where('u.id', $id)
                ->find();
        } else {
            $info = Db::name('fastim_user')
                ->alias('u')
                ->field('u.id,u.type,u.avatar,u.nickname,u.status,u.bio,u.gender,u.welcome_msg,fu.avatar as fu_avatar,fu.nickname as fu_nickname,fu.bio as fu_bio,fu.gender as fu_gender,a.avatar as a_avatar,a.nickname as a_nickname')
                ->join('user fu', 'u.user_id=fu.id', 'LEFT')
                ->join('admin a', 'u.admin_id=a.id', 'LEFT')
                ->where('u.id', $id)
                ->find();
        }

        if ($info) {

            if ($getUserId) {
                $friends = Db::name('fastim_friendship')->where('user_id', $getUserId)->where('friend_id', $id)->find();
                if ($friends) {
                    if ($friends['top_contacts']) {
                        $info['group'] = 'common';
                    } elseif ($friends['group'] == 0) {
                        $info['group'] = 'all_friends';
                    } else {
                        $info['group'] = $friends['group'];
                    }
                }
            }
            $info['friend'] = ((isset($friends) && $friends) || $id == $getUserId) ? true : false;

            if ($detail) {
                $info['email']    = self::trueAttr([
                    $info['email'],
                    $info['fu_email'],
                    $info['a_email']
                ]);
                $info['mobile']   = self::trueAttr([
                    $info['mobile'],
                    $info['fu_mobile'],
                    ''
                ]);
                $info['birthday'] = self::trueAttr([
                    $info['birthday'],
                    $info['fu_birthday'],
                    ''
                ]);
                unset($info['fu_email'], $info['a_email'], $info['fu_mobile'], $info['fu_birthday']);
                // 资料隐私性检查
                if ($getUserId != $id) {
                    $userConfig = self::imUserConfig($id);
                    $friendship = Db::name('fastim_friendship')
                        ->where('user_id', $id)
                        ->where('friend_id', $getUserId)
                        ->find();

                    // 隐私性设置:0=全部,1=好友,2=仅自己
                    $privacy = [
                        'mobile_privacy'     => 'mobile',
                        'email_privacy'      => 'email',
                        'occupation_privacy' => 'occupation',
                        'company_privacy'    => 'company'
                    ];
                    foreach ($privacy as $key => $value) {
                        if (($userConfig[$key] == 1 && !$friendship) || $userConfig[$key] == 2) {
                            $info[$value] = '';
                        }
                    }
                    if (($userConfig['age_privacy'] == 1 && !$friendship) || $userConfig['age_privacy'] == 2) {
                        $info['age'] = $info['birthday'] = '';
                    }
                }
            }
            $info['remark']          = $friends['remark'] ?? '';
            $info['nickname_origin'] = self::trueAttr(self::nicknameSort([
                $info['nickname'],
                $info['fu_nickname'],
                $info['a_nickname']
            ], $info['id']));
            $info['nickname']        = self::trueAttr(self::nicknameSort([
                $info['remark'],
                $info['nickname_origin']
            ], $info['id']));
            $info['avatar']          = self::avatarSrc([
                $info['avatar'],
                $info['fu_avatar'],
                $info['a_avatar']
            ], $info['nickname']);
            $info['bio']             = self::trueAttr([
                $info['bio'],
                $info['fu_bio'],
                '这家伙很懒，什么也没写！'
            ]);
            $info['status']          = self::imUserStatus($info['status'], ($id == $getUserId ? false : true));
            $info['gender']          = self::trueAttr([
                $info['gender'],
                $info['fu_gender'],
                '0'
            ]);
            $info['gender']          = self::imUserGender($info['gender']);
            unset($info['fu_avatar'], $info['a_avatar'], $info['fu_nickname'], $info['a_nickname'], $info['fu_bio'], $info['fu_gender']);
            return $info;
        }

        return false;
    }

    /**
     * IM用户状态转义
     * @param int $status 数字状态
     * @param int $desensitization 脱敏
     * @return array
     */
    public static function imUserStatus($status, $desensitization = true)
    {
        $statusArr = [
            0 => '离线',
            1 => '在线',
            2 => '忙碌',
            3 => '隐身'
        ];

        if ($desensitization) {
            $status       = ($status == 3) ? 0 : $status;
            $statusArr[3] = '离线';
        }

        return [
            'value'   => $status,
            'chinese' => $statusArr[$status] ?? '未知'
        ];
    }

    /**
     * IM用户性别转义
     * @param int $gender 数字性别
     * @return string
     */
    public static function imUserGender($gender)
    {
        $genderArr = [
            'secrecy' => [
                'chinese' => '保密',
                'value'   => 'secrecy'
            ],
            'male'    => [
                'chinese' => '男',
                'value'   => 'male'
            ],
            'female'  => [
                'chinese' => '女',
                'value'   => 'female'
            ]
        ];

        return $genderArr[$gender] ?? $genderArr[0];
    }

    /**
     * 获取群聊资料
     * @param int $id 群聊ID
     * @return array|bool
     */
    public static function getImGroupChat($id)
    {
        $info = Db::name('fastim_group_chat')->where('id', $id)->find();

        if (!$info) {
            return false;
        }

        $info['user_count'] = Db::name('fastim_group_user')->where('group_id', $id)->count('user_id');

        $info['avatar'] = self::avatarSrc([
            $info['avatar'],
        ], $info['nickname']);

        $info['bio'] = self::trueAttr([
            $info['bio'],
            '群主很懒，什么也没写~'
        ]);
        return $info;
    }

    /**
     * 获取会话详情
     * @param int $sessionId 会话ID
     * @param int $uid 传递此参数则同时获取会话的用户信息
     * @return array|bool
     */
    public static function sessionInfo($sessionId, $uid = null)
    {
        $imSession = Db::name('fastim_session')->where('id', $sessionId)->find();

        if (!$imSession) {
            return false;
        } elseif (!$uid) {
            return $imSession;
        }

        if ($imSession['type'] == 'single') {
            $imSession['sessionUser'] = self::getImUserInfo(($imSession['user_one'] == $uid) ? $imSession['user_two'] : $imSession['user_one'], false, $uid);
        } elseif ($imSession['type'] == 'group') {
            $imSession['sessionUser'] = self::getImGroupChat($imSession['chat_id']);
        } elseif ($imSession['type'] == 'service') {
            $imSession['sessionUser'] = Db::name('fastim_service')->where('id', $imSession['chat_id'])->find();
        }
        $imSession['user'] = self::getImUserInfo($uid);
        if (!$imSession['sessionUser'] || !$imSession['user']) {
            return false;
        }
        $imSession['sessionUser']['avatar'] = self::avatarSrc($imSession['sessionUser']['avatar'], $imSession['sessionUser']['nickname']);

        return $imSession;
    }

    /**
     * 获得用户配置
     * @param int $uid 用户
     * @param string $name 配置项
     * @return array
     */
    public static function imUserConfig($uid, $name = null)
    {
        if ($name) {
            $imDefaultConfig = Db::name('fastim_config')
                ->where('user_settings', 1)
                ->where('name', $name)
                ->value('value');
            $userConfig      = Db::name('fastim_user_config')
                ->where('user_id', $uid)
                ->where('name', $name)
                ->value('value');
            return ($userConfig !== null) ? $userConfig : $imDefaultConfig;
        } else {
            $imDefaultConfig = Db::name('fastim_config')->where('user_settings', 1)->column('name,value');
            $userConfig      = Db::name('fastim_user_config')->where('user_id', $uid)->column('name,value');
            return array_merge($imDefaultConfig, $userConfig);
        }
    }

    /**
     * 绑定IM用户与FA管理员或FA用户的关系
     * @return array|bool imUser完整资料
     */
    public static function bindFaUserToIMUser($imUser, $userInfo)
    {
        if (!$imUser) {
            return false;
        }

        if ($userInfo) {
            if ($userInfo['identity'] == 'admin' && $imUser['admin_id'] == 0) {
                Db::name('fastim_user')->where('id', $imUser['id'])->update([
                    'type'     => 'user',
                    'admin_id' => $userInfo['id']
                ]);
                $imUser['type']     = 'user';
                $imUser['admin_id'] = $userInfo['id'];
            } elseif ($userInfo['identity'] == 'fauser' && $imUser['user_id'] == 0) {
                Db::name('fastim_user')->where('id', $imUser['id'])->update([
                    'type'    => 'user',
                    'user_id' => $userInfo['id']
                ]);
                $imUser['type']    = 'user';
                $imUser['user_id'] = $userInfo['id'];
            }
        }

        $imUser['nickname'] = self::trueAttr(self::nicknameSort([
            $imUser['nickname'],
            $userInfo['nickname'] ?? ''
        ], $imUser['id']));

        $imUser['avatar'] = self::avatarSrc([
            $imUser['avatar'],
            $userInfo['avatar'] ?? ''
        ], $imUser['nickname']);

        $imUser['identity'] = $userInfo['identity'] ?? $imUser['identity'];
        return $imUser;
    }

    /**
     * 创建一个Im用户
     * @return array|bool 用户资料
     */
    public static function createIMUser($referrer = '', $loginip = '')
    {
        $imUserId = Db::name('fastim_user')->max('id') + 1;
        $token    = \fast\Random::uuid();

        $imUser = [
            'avatar'     => 'zpupload/20230911/bfc12ff4011731d7b4.jpeg',
            'nickname'   => CommonCode::buildTouristNickname($imUserId),
            'referrer'   => $referrer,
            'token'      => $token,
            'loginip'    => $loginip,
            'createtime' => time(),
        ];
        if (Db::name('fastim_user')->insert($imUser)) {

            $newImUserId = Db::name('fastim_user')->getLastInsID();
            $keeptime    = 864000;
            $expiretime  = time() + $keeptime;
            $key         = md5(md5($newImUserId) . md5($keeptime) . md5($expiretime) . $token);
            $userToken   = [$newImUserId, $keeptime, $expiretime, $key];

            $imUser                = Db::name('fastim_user')
                ->field('id,type,user_id,admin_id,avatar,nickname,email,status')
                ->where('id', $newImUserId)
                ->find();
            $imUser['cache_token'] = implode('|', $userToken);// 缓存到客户端的token
            $imUser['identity']    = 'imuser';

            // 防止数据库id不对应时的昵称错误
            if ((CommonCode::$imTouristNicknamePrefix . $imUserId != CommonCode::$imTouristNicknamePrefix . $newImUserId) && ($imUser['nickname'] == CommonCode::$imTouristNicknamePrefix . $imUserId)) {
                Db::name('fastim_user')->where('id', $newImUserId)->update([
                    'nickname' => CommonCode::$imTouristNicknamePrefix . $newImUserId
                ]);
                $imUser['nickname'] = CommonCode::$imTouristNicknamePrefix . $newImUserId;
            }
            return $imUser;
        } else {
            return false;
        }
    }

    /**
     * 检查为一个Im用户
     * @param string token
     * @param string userInfo 已登录的用户或管理员资料
     * @return array|bool 用户资料
     */
    public static function checkIMUser($token = false, $userInfo = false)
    {
        // 通过绑定的用户获得imUser
        if ($userInfo) {
            if ($userInfo['identity'] == 'admin') {
                $imUser = Db::name('fastim_user')
                    ->alias('u')
                    ->field('u.id,u.type,u.user_id,u.admin_id,u.avatar,u.nickname,u.email,u.status,a.avatar as a_avatar,a.nickname as a_nickname,a.email as a_email')
                    ->join('admin a', 'u.admin_id=a.id', 'LEFT')
                    ->where('u.admin_id', $userInfo['id'])
                    ->find();

                if ($imUser) {

                    $imUser['nickname'] = self::trueAttr(self::nicknameSort([
                        $imUser['nickname'],
                        $imUser['a_nickname']
                    ], $imUser['id']));

                    $imUser['avatar'] = self::avatarSrc([
                        $imUser['avatar'],
                        $imUser['a_avatar']
                    ], $imUser['nickname']);

                    $imUser['email'] = self::trueAttr([
                        $imUser['email'],
                        $imUser['a_email']
                    ]);

                    unset($imUser['a_avatar'], $imUser['a_nickname'], $imUser['a_email']);
                }
            } elseif ($userInfo['identity'] == 'fauser') {
                $imUser = Db::name('fastim_user')
                    ->alias('u')
                    ->field('u.id,u.type,u.user_id,u.admin_id,u.avatar,u.nickname,u.email,u.status,fu.avatar as fu_avatar,fu.nickname as fu_nickname,fu.email as fu_email')
                    ->join('user fu', 'u.user_id=fu.id', 'LEFT')
                    ->where('u.user_id', $userInfo['id'])
                    ->find();

                if ($imUser) {
                    $imUser['nickname'] = self::trueAttr(self::nicknameSort([
                        $imUser['nickname'],
                        $imUser['fu_nickname']
                    ], $imUser['id']));
                    $imUser['avatar']   = self::avatarSrc([
                        $imUser['avatar'],
                        $imUser['fu_avatar']
                    ], $imUser['nickname']);
                    $imUser['email']    = self::trueAttr([
                        $imUser['email'],
                        $imUser['fu_email']
                    ]);

                    unset($imUser['fu_avatar'], $imUser['fu_nickname'], $imUser['fu_email']);
                }
            }
        }

        // 通过token获得imUser
        if ($token && (!isset($imUser) || !$imUser)) {
            // ios 网络传输特殊符号兼容
            if (strstr($token, '~') !== false) {
                $token = str_replace('~', '|', $token);
            }

            if (strstr($token, '|') === false) {
                return false;
            }

            [$id, $keeptime, $expiretime, $key] = explode('|', $token);
            if ($id && $keeptime && $expiretime && $key && $expiretime > time()) {
                $imUser = Db::name('fastim_user')
                    ->field('id,type,user_id,admin_id,avatar,nickname,email,token,status')
                    ->where('id', $id)
                    ->find();
                if (!$imUser || !$imUser['token']) {
                    return false;
                }

                // token有变更
                if ($key != md5(md5($id) . md5($keeptime) . md5($expiretime) . $imUser['token'])) {
                    return false;
                }

                $relationUser = false;
                if ($imUser['user_id'] > 0) {
                    $relationUser = Db::name('user')
                        ->field('avatar,nickname,email')
                        ->where('id', $imUser['user_id'])
                        ->where('status', 'normal')
                        ->find();
                } elseif ($imUser['admin_id'] > 0) {
                    $relationUser = Db::name('admin')
                        ->field('avatar,nickname,email')
                        ->where('id', $imUser['admin_id'])
                        ->where('status', 'normal')
                        ->find();
                }

                if ($relationUser) {
                    $imUser['nickname'] = self::trueAttr(self::nicknameSort([
                        $imUser['nickname'],
                        $relationUser['nickname']
                    ], $imUser['id']));
                    $imUser['avatar']   = self::avatarSrc([
                        $imUser['avatar'],
                        $relationUser['avatar']
                    ], $imUser['nickname']);

                    $imUser['email'] = self::trueAttr([
                        $imUser['email'],
                        $relationUser['email']
                    ]);
                }
            }
        }

        if (!isset($imUser)) {
            return false;
        }

        $imUser['identity'] = 'imuser';
        unset($imUser['token']);
        return $imUser;
    }

    /**
     * 微信授权登录检查为一个Im用户
     * @param string token
     * @param string userInfo 已登录的用户或管理员资料
     * @return array|bool 用户资料
     */
    public static function checkWxIMUser($token = false, $userInfo = false)
    {
        // 通过绑定的用户获得imUser
        if ($userInfo) {
            if ($userInfo['identity'] == 'admin') {
                $imUser = Db::name('fastim_user')
                    ->alias('u')
                    ->field('u.id,u.type,u.user_id,u.admin_id,u.avatar,u.nickname,u.email,u.status,a.avatar as a_avatar,a.nickname as a_nickname')
                    ->join('admin a', 'u.admin_id=a.id', 'LEFT')
                    ->where('u.admin_id', $userInfo['id'])
                    ->find();

                if ($imUser) {

                    $imUser['nickname'] = self::trueAttr(self::nicknameSort([
                        $imUser['nickname'],
                        $imUser['a_nickname']
                    ], $imUser['id']));

                    $imUser['avatar'] = self::avatarSrc([
                        $imUser['avatar'],
                        $imUser['a_avatar']
                    ], $imUser['nickname']);

                    $imUser['email'] = self::trueAttr([
                        $imUser['email'],
                        $imUser['a_email']
                    ]);

                    unset($imUser['a_avatar'], $imUser['a_nickname'], $imUser['a_email']);
                }
            } elseif ($userInfo['identity'] == 'fauser') {
                $imUser = Db::name('fastim_user')
                    ->alias('u')
                    ->field('u.id,u.type,u.user_id,u.admin_id,u.avatar,u.nickname,u.email,u.status,fu.avatar as fu_avatar,fu.nickname as fu_nickname')
                    ->join('users fu', 'u.user_id=fu.id', 'LEFT')
                    ->where('u.user_id', $userInfo['id'])
                    ->find();

                if ($imUser) {
                    $imUser['nickname'] = self::trueAttr(self::nicknameSort([
                        $imUser['nickname'],
                        $imUser['fu_nickname']
                    ], $imUser['id']));
                    $imUser['avatar']   = self::avatarSrc([
                        $imUser['avatar'],
                        $imUser['fu_avatar']
                    ], $imUser['nickname']);
                   /* $imUser['email']    = self::trueAttr([
                        $imUser['email'],
                        $imUser['fu_email']
                    ]);*/

                    unset($imUser['fu_avatar'], $imUser['fu_nickname']);
                }
            }
        }

        // 通过token获得imUser
        if ($token && (!isset($imUser) || !$imUser)) {
            // ios 网络传输特殊符号兼容
            if (strstr($token, '~') !== false) {
                $token = str_replace('~', '|', $token);
            }

            if (strstr($token, '|') === false) {
                return false;
            }

            [$id, $keeptime, $expiretime, $key] = explode('|', $token);
            if ($id && $keeptime && $expiretime && $key && $expiretime > time()) {
                $imUser = Db::name('fastim_user')
                    ->field('id,type,user_id,admin_id,avatar,nickname,email,token,status')
                    ->where('id', $id)
                    ->find();
                if (!$imUser || !$imUser['token']) {
                    return false;
                }

                // token有变更
                if ($key != md5(md5($id) . md5($keeptime) . md5($expiretime) . $imUser['token'])) {
                    return false;
                }

                $relationUser = false;
                if ($imUser['user_id'] > 0) {
                    $relationUser = Db::name('users')
                        ->field('avatar,nickname')
                        ->where('id', $imUser['user_id'])
                        ->where('status', 'normal')
                        ->find();
                } elseif ($imUser['admin_id'] > 0) {
                    $relationUser = Db::name('admin')
                        ->field('avatar,nickname')
                        ->where('id', $imUser['admin_id'])
                        ->where('status', 'normal')
                        ->find();
                }

                if ($relationUser) {
                    $imUser['nickname'] = self::trueAttr(self::nicknameSort([
                        $imUser['nickname'],
                        $relationUser['nickname']
                    ], $imUser['id']));
                    $imUser['avatar']   = self::avatarSrc([
                        $imUser['avatar'],
                        $relationUser['avatar']
                    ], $imUser['nickname']);

                   /* $imUser['email'] = self::trueAttr([
                        $imUser['email'],
                        $relationUser['email']
                    ]);*/
                }
            }
        }
        if (!isset($imUser)) {
            return false;
        }

        $imUser['identity'] = 'imuser';
        unset($imUser['token']);
        return $imUser;
    }

    /**
     * 创建一个微信Im用户
     * @return array|bool 用户资料
     */
    public static function createWxIMUser($referrer = '', $loginip = '',$token)
    {
        $imUserId = Db::name('fastim_user')->max('id') + 1;
//        $token    = \fast\Random::uuid();

        $imUser = [
            'avatar'     => '',
            'nickname'   => CommonCode::buildTouristNickname($imUserId),
            'referrer'   => $referrer,
            'token'      => $token,
            'loginip'    => $loginip,
            'createtime' => time(),
        ];
        if (Db::name('fastim_user')->insert($imUser)) {

            $newImUserId = Db::name('fastim_user')->getLastInsID();
            $keeptime    = 864000;
            $expiretime  = time() + $keeptime;
            $key         = md5(md5($newImUserId) . md5($keeptime) . md5($expiretime) . $token);
            $userToken   = [$newImUserId, $keeptime, $expiretime, $key];

            $imUser = Db::name('fastim_user')
                ->field('id,type,user_id,admin_id,avatar,nickname,email,status')
                ->where('id', $newImUserId)
                ->find();
            $imUser['cache_token'] = implode('|', $userToken);// 缓存到客户端的token
            $imUser['identity']    = 'imuser';

            // 防止数据库id不对应时的昵称错误
            if ((CommonCode::$imTouristNicknamePrefix . $imUserId != CommonCode::$imTouristNicknamePrefix . $newImUserId) && ($imUser['nickname'] == CommonCode::$imTouristNicknamePrefix . $imUserId)) {
                Db::name('fastim_user')->where('id', $newImUserId)->update([
                    'nickname' => CommonCode::$imTouristNicknamePrefix . $newImUserId
                ]);
                $imUser['nickname'] = CommonCode::$imTouristNicknamePrefix . $newImUserId;
            }
            return $imUser;
        } else {
            return false;
        }
    }

    /**
     * 生成鉴权token
     * 一个额外于游客身份token的授权token
     * uniapp编译的各端可以使用此 token 被鉴别为非游客用户
     * @param string $token
     */
    public static function createAuthToken($uid)
    {
        $authToken = Db::name('fastim_user')->where('id', $uid)->value('auth_token');
        if (!$authToken) {
            $authToken = md5(\fast\Random::uuid()); // md5后短一点
            Db::name('fastim_user')->where('id', $uid)->update([
                'auth_token' => $authToken
            ]);
        }

        $keeptime   = 864000;// token 10天后过期
        $expiretime = time() + $keeptime;
        $authKey    = md5(md5($uid) . md5($keeptime) . md5($expiretime) . $authToken);
        $authToken  = [$uid, $keeptime, $expiretime, $authKey];
        return implode('|', $authToken);
    }

    /**
     * 检查鉴权token
     * @param string $authToken 鉴权token
     * @return bool
     */
    public static function checkAuthToken($authToken)
    {
        [$id, $keeptime, $expiretime, $key] = explode('|', $authToken);
        if ($id && $keeptime && $expiretime && $key && $expiretime > time()) {
            $userAuthToken = Db::name('fastim_user')->where('id', $id)->value('auth_token');
            if (!$userAuthToken) {
                return false;
            }

            // token有变更
            if ($key == md5(md5($id) . md5($keeptime) . md5($expiretime) . $userAuthToken)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 检查管理员身份
     * @param string adminToken 管理员cookie
     * @param string adminId    管理员ID直接通过检查
     * @return array|bool
     */
    public static function checkAdmin($adminToken, $adminId = 0)
    {
        $readField = 'id,nickname,avatar,email,token'; // 要读取的字段

        if ($adminId) {
            $admin = Db::name('admin')->field($readField)->where('id', $adminId)->where('status', 'normal')->find();
        } else {
            [$id, $keeptime, $expiretime, $key] = explode('|', $adminToken);

            if ($id && $keeptime && $expiretime && $key && $expiretime > time()) {

                $admin = Db::name('admin')->field($readField)->where('id', $id)->where('status', 'normal')->find();

                if (!$admin || !$admin['token']) {
                    return false;
                }

                // 检查token是否有变更
                $sign = $id . CommonCode::$imAuthSign;
                if ($key != md5(md5($sign) . md5($keeptime) . md5($expiretime) . $admin['token'])) {
                    return false;
                }

            } else {
                return false;
            }
        }

        if (!$admin) {
            return false;
        }

        $admin['identity'] = 'admin';
        return $admin;
    }

    /**
     * 检查FastAdmin用户token
     * @param string token 用户的token
     * @param int id 用户ID直接登录
     * @return array|bool Fa用户信息
     */
    public static function checkFaUser($token, $id = 0)
    {
        $user = false;

        $readField = 'id,nickname,avatar,email,mobile';

        if ($id) {
            $user = Db::name('user')->field($readField)->where('id', $id)->where('status', 'normal')->find();
        } else {
            $userId = false;
            if (!config('cookie.httponly')) {
                $userId = Db::name('user_token')->where('token', self::getEncryptedToken($token))->value('user_id');
            } else {
                [$id, $key] = explode('|', $token);
                $userTokenList = Db::name('user_token')
                    ->where('user_id', $id)
                    ->where('expiretime', '>', time())
                    ->select();
                foreach ($userTokenList as $userToken) {
                    $sign    = $userToken['token'] . CommonCode::$imAuthSign;
                    $userKey = md5(md5($id) . md5($sign));
                    if ($userKey == $key) {
                        $userId = $id;
                        break;
                    }
                }
            }

            if ($userId) {
                $user = Db::name('user')->field($readField)->where('id', $userId)->where('status', 'normal')->find();
            }
        }

        if (!$user) {
            return false;
        }

        $user['identity'] = 'fauser';
        return $user;
    }

    /**
     * 检查FastAdmin用户token
     * @param string token 用户的token
     * @param int id 用户ID直接登录
     * @return array|bool Fa用户信息
     */
    public static function checkWxUser($token, $id = 0)
    {
        $user = false;

        $readField = 'id,nickname,avatar,mobile';

        if ($id) {
            $user = Db::name('users')->field($readField)->where('id', $id)->where('status', 1)->find();
        } else {
            $userId = false;
            if (!config('cookie.httponly')) {
                $userId = Db::name('user_token')->where('token', self::getEncryptedToken($token))->value('user_id');
            } else {
                [$id, $key] = explode('|', $token);
                $userTokenList = Db::name('user_token')
                    ->where('user_id', $id)
                    ->where('expiretime', '>', time())
                    ->select();
                foreach ($userTokenList as $userToken) {
                    $sign    = $userToken['token'] . CommonCode::$imAuthSign;
                    $userKey = md5(md5($id) . md5($sign));
                    if ($userKey == $key) {
                        $userId = $id;
                        break;
                    }
                }
            }

            if ($userId) {
                $user = Db::name('users')->field($readField)->where('id', $userId)->where('status', 'normal')->find();
            }
        }

        if (!$user) {
            return false;
        }

        $user['identity'] = 'fauser';
        return $user;
    }
    /**
     * 属性多选一
     * @param array $attr 数组-按顺序多选一
     */
    public static function trueAttr($attr)
    {
        if ($attr) {
            if (is_array($attr)) {
                foreach ($attr as $key => $value) {
                    if ($value) {
                        return $value;
                    }
                }
                return $attr[count($attr) - 1];// 返回数组中的最后一个值
            } else {
                return $attr;
            }
        } else {
            return $attr;
        }
    }

    /**
     * 对昵称数组进行排序
     * 默认昵称排到最后
     * @return array
     */
    public static function nicknameSort($nickname, $uid)
    {
        if (is_array($nickname)) {
            $defaultNickname    = CommonCode::buildTouristNickname($uid);
            $defaultNicknameKey = array_search($defaultNickname, $nickname);
            if ($defaultNicknameKey !== false) {
                unset($nickname[$defaultNicknameKey]);// 删除
                $nickname   = array_values($nickname);// 数组重排
                $nickname[] = $defaultNickname;// 添加到最后
            }
            return $nickname;
        } else {
            return $nickname;
        }
    }

    /**
     * 用户头像url
     * @param array|bool $avatar 原头像url数组
     * @param string $nickname 用户昵称
     */
    public static function avatarSrc($avatar, $nickname)
    {
        $avatar = self::trueAttr($avatar);
        if ($avatar) {
            return $avatar;
        }

        $nickname = mb_strtoupper(mb_substr($nickname, 0, 1));
        switch ($nickname) {
            case '游':
                $bg = "rgb(99,136,251)";
                break;
            case '新':
                $bg = "rgb(243,89,168)";
                break;
            case '群':
                $bg = "rgb(255,175,81)";
                break;

            default:
                $bg = false;// 随机颜色
                break;
        }
        return self::letterAvatar($nickname, $bg);
    }

    /*
     * 检查和过滤变量
     */
    public static function checkVariable(&$variable)
    {
        $variable = self::removeXss($variable);
        $variable = htmlspecialchars($variable);
        $variable = stripslashes($variable); // 删除反斜杠
        $variable = addslashes($variable); // 转义特殊符号
        $variable = trim($variable); // 去除字符两边的空格
    }

    public static function removeXss($val)
    {
        if (function_exists('xss_clean')) {
            return xss_clean($val);
        }
        $val = preg_replace('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/', '', $val);

        $search = 'abcdefghijklmnopqrstuvwxyz';
        $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $search .= '1234567890!@#$%^&*()';
        $search .= '~`";:?+/={}[]-_|\'\\';
        for ($i = 0; $i < strlen($search); $i++) {
            $val = preg_replace('/(&#[xX]0{0,8}' . dechex(ord($search[$i])) . ';?)/i', $search[$i], $val); // with a ;
            $val = preg_replace('/(&#0{0,8}' . ord($search[$i]) . ';?)/', $search[$i], $val); // with a ;
        }

        $ra1   = [
            'javascript',
            'vbscript',
            'expression',
            'applet',
            'meta',
            'xml',
            'blink',
            'link',
            'style',
            'script',
            'embed',
            'object',
            'iframe',
            'frame',
            'frameset',
            'ilayer',
            'layer',
            'bgsound',
            'title',
            'base'
        ];
        $ra2   = [
            'onabort',
            'onactivate',
            'onafterprint',
            'onafterupdate',
            'onbeforeactivate',
            'onbeforecopy',
            'onbeforecut',
            'onbeforedeactivate',
            'onbeforeeditfocus',
            'onbeforepaste',
            'onbeforeprint',
            'onbeforeunload',
            'onbeforeupdate',
            'onblur',
            'onbounce',
            'oncellchange',
            'onchange',
            'onclick',
            'oncontextmenu',
            'oncontrolselect',
            'oncopy',
            'oncut',
            'ondataavailable',
            'ondatasetchanged',
            'ondatasetcomplete',
            'ondblclick',
            'ondeactivate',
            'ondrag',
            'ondragend',
            'ondragenter',
            'ondragleave',
            'ondragover',
            'ondragstart',
            'ondrop',
            'onerror',
            'onerrorupdate',
            'onfilterchange',
            'onfinish',
            'onfocus',
            'onfocusin',
            'onfocusout',
            'onhelp',
            'onkeydown',
            'onkeypress',
            'onkeyup',
            'onlayoutcomplete',
            'onload',
            'onlosecapture',
            'onmousedown',
            'onmouseenter',
            'onmouseleave',
            'onmousemove',
            'onmouseout',
            'onmouseover',
            'onmouseup',
            'onmousewheel',
            'onmove',
            'onmoveend',
            'onmovestart',
            'onpaste',
            'onpropertychange',
            'onreadystatechange',
            'onreset',
            'onresize',
            'onresizeend',
            'onresizestart',
            'onrowenter',
            'onrowexit',
            'onrowsdelete',
            'onrowsinserted',
            'onscroll',
            'onselect',
            'onselectionchange',
            'onselectstart',
            'onstart',
            'onstop',
            'onsubmit',
            'onunload'
        ];
        $ra    = array_merge($ra1, $ra2);
        $found = true;
        while ($found == true) {

            $val_before = $val;
            for ($i = 0; $i < sizeof($ra); $i++) {
                $pattern = '/';
                for ($j = 0; $j < strlen($ra[$i]); $j++) {
                    if ($j > 0) {
                        $pattern .= '(';
                        $pattern .= '(&#[xX]0{0,8}([9ab]);)';
                        $pattern .= '|';
                        $pattern .= '|(&#0{0,8}([9|10|13]);)';
                        $pattern .= ')*';
                    }
                    $pattern .= $ra[$i][$j];
                }
                $pattern     .= '/i';
                $replacement = substr($ra[$i], 0, 2) . '<k>' . substr($ra[$i], 2);
                $val         = preg_replace($pattern, $replacement, $val);
                if ($val_before == $val) {
                    $found = false;
                }
            }
        }
        return $val;
    }

    /**
     * 用户token加密
     * @param string $token 待加密的token
     */
    public static function getEncryptedToken($token)
    {
        $tokenConfig = \think\Config::get('token');

        $config = [
            // 缓存前缀
            'key'      => $tokenConfig['key'],
            // 加密方式
            'hashalgo' => $tokenConfig['hashalgo'],
        ];

        return hash_hmac($config['hashalgo'], $token, $config['key']);
    }

    /**
     * 消息数据入库
     * @param array $data 消息数据
     * @return array|bool
     */
    public static function insertMessage($data)
    {
        $originalMessage = $data['message'];
        if (is_array($data['message'])) {
            $data['message'] = json_encode($data['message']);
        }

        $data['createtime'] = time();

        if (Db::name('fastim_message')->insert($data)) {
            $data['id'] = Db::name('fastim_message')->getLastInsID();

            // 发送给前台的消息数据
            if (is_array($originalMessage)) {
                $data['message'] = $originalMessage;
            } else {
                $data['message'] = htmlspecialchars_decode($originalMessage);
            }
            return $data;
        } else {
            return false;
        }
    }

    /**
     * 富文本图片Url处理
     * 此方法请勿在ws代码中使用
     * @param string $html
     * @return string 处理好图片Url的html
     */
    public static function htmlImgUrlHandle($html)
    {
        if ($html) {
            // 匹配出img标签
            $preg = '/<img.*?src="(.+?)".*?>/is';
            preg_match_all($preg, $html, $result, PREG_PATTERN_ORDER);

            if ($result[1]) {
                $fullImgUrl = [];
                foreach ($result[1] as $key => $value) {
                    $fullImgUrl[$key] = cdnurl($value, true);
                }
                $html = str_replace($result[1], $fullImgUrl, $html);
            }
        }
        return $html;
    }

    /**
     * 生成首字头像
     * @param string $text 文字
     * @return string svg
     */
    public static function letterAvatar($text, $bg = false)
    {
        if (!$bg) {
            $total = unpack('L', hash('adler32', $text, true))[1];
            $hue   = $total % 360;
            [$r, $g, $b] = hsv2rgb($hue / 360, 0.3, 0.9);
            $bg = "rgb({$r},{$g},{$b})";
        }

        $color = "#ffffff";
        $first = mb_strtoupper(mb_substr($text, 0, 1));
        $src   = base64_encode('<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="100" width="100"><rect fill="' . $bg . '" x="0" y="0" width="100" height="100"></rect><text x="50" y="50" font-size="50" text-copy="fast" fill="' . $color . '" text-anchor="middle" text-rights="admin" dominant-baseline="central">' . $first . '</text></svg>');
        $value = 'data:image/svg+xml;base64,' . $src;
        return $value;
    }

    /**
     * 根据文件后缀生成图片
     * 来自FastAdmin的开源代码
     * @param string $suffix 图片后缀
     * @param string $background 背景颜色
     * @return string             svg
     */
    public static function buildSuffixImage($suffix, $background = null)
    {
        $suffix = mb_substr(strtoupper($suffix), 0, 4);
        $total  = unpack('L', hash('adler32', $suffix, true))[1];
        $hue    = $total % 360;
        [$r, $g, $b] = hsv2rgb($hue / 360, 0.3, 0.9);

        $background = $background ? $background : "rgb({$r},{$g},{$b})";

        $icon = '
        <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
            <path style="fill:#E2E5E7;" d="M128,0c-17.6,0-32,14.4-32,32v448c0,17.6,14.4,32,32,32h320c17.6,0,32-14.4,32-32V128L352,0H128z"/>
            <path style="fill:#B0B7BD;" d="M384,128h96L352,0v96C352,113.6,366.4,128,384,128z"/>
            <polygon style="fill:#CAD1D8;" points="480,224 384,128 480,128 "/>
            <path style="fill:' . $background . ';" d="M416,416c0,8.8-7.2,16-16,16H48c-8.8,0-16-7.2-16-16V256c0-8.8,7.2-16,16-16h352c8.8,0,16,7.2,16,16 V416z"/>
            <path style="fill:#CAD1D8;" d="M400,432H96v16h304c8.8,0,16-7.2,16-16v-16C416,424.8,408.8,432,400,432z"/>
            <g><text><tspan x="220" y="380" font-size="124" font-family="Verdana, Helvetica, Arial, sans-serif" fill="white" text-anchor="middle">' . $suffix . '</tspan></text></g>
        </svg>';
        return $icon;
    }
}