<?php
/**
 * AbstractService.class.php
 *
 * Service 层基类
 * @author: daijun
 */

namespace Common\Service;

use Common\Common\Helper;
use Common\Common\Msg;
use Common\Common\Tag;
use Common\Common\User;
use Common\Model\PacketModel;
use Common\Model\PacketrecordModel;
use Common\Model\RightModel;

abstract class AbstractService extends \Com\Service
{

    // 默认页码
    const DEFAULT_PAGE = 1;
    // 默认每页条数
    const DEFAULT_LIMIT = 10;

    // 活动状态:草稿
    const ACTIVITY_DRAFT = 0;
    // 活动状态:发布
    const ACTIVITY_PUBLISH = 1;
    // 活动状态:终止
    const ACTIVITY_STOP = 2;

    // 积分规则:默认策略
    const INTEGRAL_ACTION_TYPE_DEFAULT = 1;

    // 积分规则:不使用策略
    const INTEGRAL_ACTION_TYPE_NO = 2;
    // 积分规则:使用自定义策略
    const INTEGRAL_ACTION_TYPE_MY = 3;


    // 开启红包
    const RED_OPEN = 1;
    // 未开启红包
    const RED_CLOSE = 0;

    // 活动隐藏
    const OPEN_ACTIVITY_HIDE = 1;
    // 关闭隐藏
    const CLOSE_ACTIVITY_HIDE = 0;

    /**
     *  推消息类型
     */

    // 管理员成功发布活动
    const MSG_ACTIVITY_PUBLISH = 1;
    // 帖子发布提醒审核人
    const MSG_CHECK_COMMENT_REMAIND_FOR_ADMIN = 2;
    // 帖子发布提醒发帖人
    const MSG_CHECK_COMMENT_REMAIND_FOR_USER = 3;
    // 帖子被回复
    const MSG_COMMENT_REPLY = 4;
    // 帖子审核通过
    const MSG_COMMENT_CHECK_SUCCESS = 5;
    // 帖子审核驳回
    const MSG_COMMENT_CHECK_FAIL = 6;
    // 获得随机红包提醒
    const MSG_GET_RANDOM_REDPACKET = 7;
    // 提醒领取随机红包
    const MSG_REMAIND_RECEIVE_RANDOM_REDPACKET = 8;
    // 获得补发随机红包提醒
    const MSG_GET_REPEAT_RANDOM_REDPACKET = 9;
    // 获得定向红包提醒
    const MSG_GET_VIP_REDPACKET = 10;
    // 提醒领取定向红包
    const MSG_REMAIND_RECEIVE_VIP_REDPACKET = 11;
    // 获得补发定向红包提醒
    const MSG_GET_REPEAT_VIP_REDPACKET = 12;
    // 未参与提醒消息
    const MSG_REMAIND_NO_JOIN_USERS = 13;
    // 自动审核通过消息
    const MSG_COMMENT_AUTOCHECK_SUCCESS = 14;
    // 自动审核被驳回消息
    const MSG_COMMENT_AUTOCHECK_FAIL = 15;
    // 发送消息给有权限的审核人员
    const MSG_ACTIVITY_PUBLISH_CHECK_USER = 16;
    // 发消息给有权限查看的人员
    const MSG_ACTIVITY_LOOK_COMMENT = 17;

    // 消息ID类型为活动
    const MSG_TYPE_ACTIVITY = 1;
    // 消息ID类型为评论
    const MSG_TYPE_COMMENT = 2;
    // 消息ID类型为红包
    const MSG_TYPE_REDPACKET = 3;
    // 审核消息类型
    const MSG_CHECK_TYPE_ACTIVITY = 4;

    // 可以进行删除操作
    const IS_DEL_OK = 1;
    // 不可以进行删除
    const IS_DEL_NO = 0;

    // 已点赞
    const IS_LIKE_OK = 1;
    // 未点赞
    const IS_LIKE_NO = 0;

    // 删除评论类型
    const DEL_COMMENT_TYPE = 1;
    // 删除回复类型
    const DEL_REPLY_TYPE = 2;

    // 活动点赞类型
    const LIKE_ACTIVITY_TYPE = 1;
    // 评论点赞类型
    const LIKE_COMMENT_TYPE = 2;
    // 回复点赞类型
    const LIKE_REPLY_TYPE = 3;

    // 分享权限
    const RIGHT_TYPE_ACTIVITY = 0;
    // 审核权限
    const RIGHT_TYPE_CHECK = 1;

    // 上传图片个数最大值
    const IMAGES_MAX_COUNT = 9;

    // 权限:全公司
    const IS_ALL = 1;

    // 开启分享审核
    const OPEN_CHECK = 1;
    // 关闭分享审核
    const CLOSE_CHECK = 0;

    // 审核状态:待审核
    const CHECK_ING = 0;
    // 审核状态:已通过
    const CHECK_OK = 1;
    // 审核状态:已驳回
    const CHECK_NO = 2;

    // 开启红包
    const OPEN_RED = 1;
    // 关闭红包
    const CLOSE_RED = 0;

    // 随机比例红包
    const RED_PACKET_RAND = 1;
    // 定向红包
    const RED_PACKET_VIP = 4;

    // 红包状态:(已补发)
    const PACKET_STATUS_REPEAT = 3;
    // 红包状态:(领取失败)
    const PACKET_STATUS_FAIL = 2;
    // 红包状态:(领取成功)
    const PACKET_STATUS_SUCCESS = 1;
    // 红包状态:(待领取)
    const PACKET_STATUS_WAIT = 0;


    // 构造方法
    public function __construct()
    {

        parent::__construct();
    }

    /**
     * 即时消息通知方法 【发消息统一写这里,方便维护】
     *
     * @param $params -通知参数
     *          + uids      -用户uid数组
     *              + memID     -用户uid
     *          + dp_ids    -部门id数组
     *              + dpID      -部门id
     *          + tag_ids   -标签id数组
     *              + tagID     -标签id
     *          + comment_id -帖子id
     *          + ac_id  -活动id
     *          + rid  -红包记录id
     * @param $msg_type -通知文案类型
     *
     * @return bool
     */
    public function send_msg($params, $msg_type)
    {
        // 获取应用名称
        $application_name = cfg('APPLICATION_NAME');
        // 去除中文空格
        $search = [" ", " ", "\n", "\r", "\t", '&nbsp;'];
        $replace = ["", "", "", "", "", ""];

        $content = str_replace($search, $replace, strip_tags($params['content']));

        // 本方法根据类型区分拼接不同的文案
        switch ($msg_type) {
            case self::MSG_ACTIVITY_PUBLISH:
                // 活动发布时
                $data = [
                    'title' => '【' . $application_name . '】有新的活动,快来参与吧',
                    'description' => '标题:' . $params['subject'] . "\r\n活动时间:" .
                        rgmdate((string)$params['begin_time'], 'Y-m-d H:i') . '~' .
                        ($params['end_time'] ? rgmdate((string)$params['end_time'], 'Y-m-d H:i') : '无') . "\r\n活动描述:" .
                        $this->cutstr($content, 0, 20),
                    'picUrl' => '',
                    'type' => self::MSG_TYPE_ACTIVITY, // 活动类型
                ];
                break;
            case self::MSG_ACTIVITY_PUBLISH_CHECK_USER:
                // 推送消息告知用户被设置为审核人员
                $data = [
                    'title' => '【' . $application_name . '】您已被设置成活动审核负责人,请关注审核消息',
                    'description' => '标题:' . $params['subject'] . "\r\n活动时间:" .
                        rgmdate((string)$params['begin_time'], 'Y-m-d H:i') . '~' .
                        ($params['end_time'] ? rgmdate((string)$params['end_time'], 'Y-m-d H:i') : '无') . "\r\n活动描述:" .
                        $this->cutstr($content, 0, 20),
                    'picUrl' => '',
                    'type' => self::MSG_TYPE_ACTIVITY, // 审核人消息类型
                ];
                break;
            case self::MSG_ACTIVITY_LOOK_COMMENT:
                // 发消息给有权限查看的人员
                $data = [
                    'title' => '【' . $application_name . '】' . $this->cutstr($params['subject'], 0,
                            10) . '下有一条新的回帖,快来查看吧',
                    'description' => '活动标题:' . $params['subject'] . "\r\n回帖人:" .
                        $params['username'] . "\r\n回帖内容:" .
                        $this->cutstr($content, 0, 30),
                    'picUrl' => '',
                    'type' => self::MSG_TYPE_ACTIVITY, // 帖子类型
                ];
                break;
            case self::MSG_CHECK_COMMENT_REMAIND_FOR_ADMIN:
                // 审核消息—负责人
                $data = [
                    'title' => '【' . $application_name . '】您负责的活动有新的帖子需要审核',
                    'description' => '标题:' . $params['subject'] . "\r\n回帖人:" .
                        $params['username'] . "\r\n回帖内容:" .
                        $this->cutstr($content, 0, 30),
                    'picUrl' => '',
                    'type' => self::MSG_CHECK_TYPE_ACTIVITY, // 帖子类型
                ];
                break;
            case self::MSG_CHECK_COMMENT_REMAIND_FOR_USER:
                // 审核消息—发起者
                $data = [
                    'title' => '【' . $application_name . '】您已成功发帖,请耐心等待审核',
                    'description' => '标题:' . $params['subject'] . "\r\n回帖时间:" .
                        rgmdate((string)$params['created'], 'Y-m-d H:i') . "\r\n回帖内容:" .
                        $this->cutstr($content, 0, 30),
                    'picUrl' => '',
                    'type' => self::MSG_TYPE_COMMENT, // 帖子类型
                ];

                break;
            case self::MSG_COMMENT_REPLY:
                // 评论消息(帖子被回复)
                $data = [
                    'title' => '【' . $application_name . '】您的帖子有了新的评论,快来瞧瞧吧',
                    'description' => '标题:' . $params['subject'] . "\r\n评论人:" .
                        $params['username'] . "\r\n评论内容:" .
                        $this->cutstr($content, 0, 30),
                    'picUrl' => '',
                    'type' => self::MSG_TYPE_COMMENT, // 帖子类型
                ];

                break;
            case self::MSG_COMMENT_CHECK_SUCCESS:
                // 帖子审核通过
                $data = [
                    'title' => '【' . $application_name . '】恭喜您,您的帖子已通过审核,现已正式发布',
                    'description' => '标题:' . $params['subject'] . "\r\n回帖时间:" .
                        rgmdate((string)$params['created'], 'Y-m-d H:i') . "\r\n审核人:" .
                        $params['username'] . "\r\n审核时间:" .
                        rgmdate((string)$params['check_time'], 'Y-m-d H:i'),
                    'picUrl' => '',
                    'type' => self::MSG_TYPE_COMMENT, // 帖子类型
                ];

                break;
            case self::MSG_COMMENT_CHECK_FAIL:
                // 帖子审核驳回
                if (!empty($params['reason'])) {
                    $description = '标题:' . $params['subject'] . "\r\n回帖时间:" .
                        rgmdate((string)$params['created'], 'Y-m-d H:i') . "\r\n审核人:" .
                        $params['username'] . "\r\n审核时间:" .
                        rgmdate((string)$params['check_time'], 'Y-m-d H:i') . "\r\n驳回原因:" .
                        $this->cutstr($params['reason'], 0, 20);
                } else {
                    $description = '标题:' . $params['subject'] . "\r\n回帖时间:" .
                        rgmdate((string)$params['created'], 'Y-m-d H:i') . "\r\n审核人:" .
                        $params['username'] . "\r\n审核时间:" .
                        rgmdate((string)$params['check_time'], 'Y-m-d H:i');
                }

                $data = [
                    'title' => '【' . $application_name . '】抱歉,您的帖子未通过审核',
                    'description' => $description,
                    'picUrl' => '',
                    'type' => self::MSG_TYPE_COMMENT, // 帖子类型
                ];

                break;
            case self::MSG_GET_RANDOM_REDPACKET:
                // 获得随机红包
                $data = [
                    'title' => '【' . $application_name . '】恭喜您,获得一个随机红包,请点击领取',
                    'description' => '活动标题:' . $params['subject'] . "\r\n发放时间:" .
                        rgmdate((string)$params['give_time'], 'Y-m-d H:i'),
                    'picUrl' => '',
                    'type' => self::MSG_TYPE_REDPACKET, // 红包类型
                ];

                break;
            case self::MSG_REMAIND_RECEIVE_RANDOM_REDPACKET:
                // 提醒领取随机红包
                $data = [
                    'title' => '【' . $application_name . '】您有一个随机红包未领取,请点击领取',
                    'description' => '活动标题:' . $params['subject'] . "\r\n发放时间:" .
                        rgmdate((string)$params['give_time'], 'Y-m-d H:i'),
                    'picUrl' => '',
                    'type' => self::MSG_TYPE_REDPACKET, // 红包类型
                ];

                break;
            case self::MSG_GET_REPEAT_RANDOM_REDPACKET:
                // 获得补发随机红包
                $data = [
                    'title' => '【' . $application_name . '】恭喜您,获得一个补发随机红包,请点击领取',
                    'description' => '活动标题:' . $params['subject'] . "\r\n发放时间:" .
                        rgmdate((string)$params['give_time'], 'Y-m-d H:i'),
                    'picUrl' => '',
                    'type' => self::MSG_TYPE_REDPACKET, // 红包类型
                ];

                break;
            case self::MSG_GET_VIP_REDPACKET:
                // 获得定向红包提醒
                $data = [
                    'title' => '【' . $application_name . '】恭喜您,获得一个定向红包,请点击领取',
                    'description' => '活动标题:' . $params['subject'] . "\r\n发放时间:" .
                        rgmdate((string)$params['give_time'], 'Y-m-d H:i') . "\r\n红包金额:" .
                        sprintf('%.2f', $params['rand_money'] / 100) . '元',
                    'picUrl' => '',
                    'type' => self::MSG_TYPE_REDPACKET, // 红包类型
                ];

                break;
            case self::MSG_REMAIND_RECEIVE_VIP_REDPACKET:
                // 提醒领取定向红包
                $data = [
                    'title' => '【' . $application_name . '】您一个定向红包未领取,请点击领取',
                    'description' => '活动标题:' . $params['subject'] . "\r\n发放时间:" .
                        rgmdate((string)$params['give_time'], 'Y-m-d H:i') . "\r\n红包金额:" .
                        sprintf('%.2f', $params['rand_money'] / 100) . '元',
                    'picUrl' => '',
                    'type' => self::MSG_TYPE_REDPACKET, // 红包类型
                ];

                break;
            case self::MSG_GET_REPEAT_VIP_REDPACKET:
                // 获得补发定向红包
                $data = [
                    'title' => '【' . $application_name . '】恭喜您,获得一个补发定向红包,请点击领取',
                    'description' => '活动标题:' . $params['subject'] . "\r\n发放时间:" .
                        rgmdate((string)$params['give_time'], 'Y-m-d H:i') . "\r\n红包金额:" .
                        sprintf('%.2f', $params['rand_money'] / 100) . '元',
                    'picUrl' => '',
                    'type' => self::MSG_TYPE_REDPACKET, // 红包类型
                ];

                break;
            case self::MSG_REMAIND_NO_JOIN_USERS:
                // 活动发布时
                $data = [
                    'title' => '【' . $application_name . '】您有一个活动尚未参与',
                    'description' => '标题:' . $params['subject'] . "\r\n活动时间:" .
                        rgmdate((string)$params['begin_time'], 'Y-m-d H:i') . '~' .
                        ($params['end_time'] ? rgmdate((string)$params['end_time'], 'Y-m-d H:i') : '无') . "\r\n活动描述:" .
                        $this->cutstr($content, 0, 20),
                    'picUrl' => '',
                    'type' => self::MSG_TYPE_ACTIVITY, // 活动类型
                ];

                break;

            case self::MSG_COMMENT_AUTOCHECK_SUCCESS:
                // 帖子自动审核通过
                $data = [
                    'title' => '【' . $application_name . '】恭喜您,您的帖子已通过审核,现已正式发布',
                    'description' => '标题:' . $params['subject'] . "\r\n回帖时间:" .
                        rgmdate((string)$params['created'], 'Y-m-d H:i') . "\r\n审核人:系统自动审核" . "\r\n审核时间:" .
                        rgmdate((string)$params['check_time'], 'Y-m-d H:i'),
                    'picUrl' => '',
                    'type' => self::MSG_TYPE_COMMENT, // 帖子类型
                ];

                break;
            case self::MSG_COMMENT_AUTOCHECK_FAIL:
                // 帖子自动审核驳回
                $description = '标题:' . $params['subject'] . "\r\n回帖时间:" .
                    rgmdate((string)$params['created'], 'Y-m-d H:i') . "\r\n审核人:系统自动审核" .
                    "\r\n审核时间:" . rgmdate((string)$params['check_time'],
                        'Y-m-d H:i') . "\r\n驳回原因:" . $this->cutstr($params['reason'], 0, 20);

                $data = [
                    'title' => '【' . $application_name . '】抱歉,您的帖子未通过审核',
                    'description' => $description,
                    'picUrl' => '',
                    'type' => self::MSG_TYPE_COMMENT, // 帖子类型
                ];

                break;

            default:

                return true;
        }

        $this->send($params, $data);

        return true;
    }

    /**
     * 发送消息
     *
     * @param $params -通知参数
     *          + is_all    -全公司
     *          + uids      -用户uid数组
     *              + memID     -用户uid
     *          + dp_ids    -部门id数组
     *              + dpID      -部门id
     * @param array $condition 消息参数
     *          + title  -标题
     *          + description -内容
     *          + picUrl -图片URL
     *          + type   -数据类型(1:活动  2:帖子,3:红包)
     *
     * @return bool
     */
    private function send($params = [], $condition = [])
    {
        // 发送消息接收人
        $msgUser = !empty($params['uids']) ? $params['uids'] : [];
        $toUser = isset($params['is_all']) && !empty($params['is_all']) ? '@all' : $msgUser;

        // 发送消息部门
        $toParty = !empty($params['dp_ids']) ? $params['dp_ids'] : [];

        $articles = [
            [
                'title' => $condition['title'],
                'description' => $condition['description'],
                'url' => oaUrl('Frontend/Index/Msg/Index',
                    [
                        'type' => $condition['type'],
                        'ac_id' => intval($params['ac_id']),
                        'comment_id' => intval($params['comment_id']),
                        'rid' => intval($params['rid'])
                    ]
                ),
                'picUrl' => '',
            ],
        ];

        $msgServ = &Msg::instance();

        // 根据部门ID查询其下的所有用户UID(兼容发送对象超过1000人的情况)
        if (!empty($toParty) && $toUser != '@all') {

            $params = [
                'dpIdList' => $toParty, // 跟UC确认可以传部门的数组
                'departmentChildrenFlag' => Helper::USER_DEPARTMENTCHILDRENFLAG_TRUE, // 按部门条件查询时,表示部门递归查询人员
            ];
            // 部门下的用户UID获取
            $toPartUser = User::instance()->listAllBasic($params);
            $toUser = array_merge($toUser, (array)array_column($toPartUser, 'memUid'));
        }

        // 发消息给指定人员
        if ($toUser != '@all') {
            // 去除用户UID数组 空值和重复值
            $toUser = is_array($toUser) ? array_unique(array_filter($toUser)) : [$toUser];
            $uid_groups = array_chunk($toUser, Msg::USER_MAX_COUNT);
            foreach ($uid_groups as $uid_group) {
                $msgServ->sendNews($uid_group, null, null, $articles);
            }
        } else {
            // 发送给全公司
            $msgServ->sendNews($toUser, null, null, $articles);
        }


        return true;
    }

    /**
     * 转换时间格式
     * 时间显示规则:1小时内显示XX分钟前,1天以内的显示XX个小时前,1~7天显示XX天前,超过7天显示具体年-月-日 时-分
     *
     * @param string $time 当前时间戳
     *
     * @return bool
     */
    public function get_time($time = '')
    {
        // 获取剩余秒数
        $way = NOW_TIME - ($time / 1000);

        // 一分钟之内
        if ($way < 60) {

            return '刚刚';
        }

        // 如果是一小时之内
        if ($way < 3600) {

            return intval($way / 60) . '分钟前';
        }

        // 如果是一天之内
        if ($way < 86400) {

            return intval($way / 3600) . '小时前';
        }

        // 如果是一周之内
        if ($way < 604800) {

            return intval($way / 86400) . '天前';
        }

        return rgmdate(strval($time), 'Y-m-d H:i');
    }


    /**
     * 评论和回复获取图片信息
     *
     * @param array $data
     *
     * @return array
     */
    public function get_image_list($data = [])
    {

        // 初始化图片数组
        $images = [];

        $image_ids = explode(',', $data);

        foreach ($image_ids as $_atId) {

            $images[] = [
                'atId' => $_atId,
                'imgUrl' => imgUrlReal($_atId),
            ];
        }

        return $images;
    }

    /**
     * 获取已删除用户信息
     *
     * @param array &$user_list 用户列表
     * @param array $uids 全部用户UID
     *
     * @return bool
     */
    public function user_list(&$user_list = [], $uids = [])
    {
        // 获取未删除的用户UID
        $_uids = array_column($user_list, 'memUid');

        // 获取被删除的用户UID
        $un_uid = array_diff($uids, $_uids);

        // 实例化
        $user = User::instance();

        // 遍历数据
        foreach ($un_uid as $v) {

            // 如果UID不为空
            if (!empty($v)) {

                $user_list[] = $user->getByUid($v);
            }
        }

        return true;
    }

    /**
     * 字符串截取,支持中文和其他编码
     * static
     * access public
     *
     * @param string $str 需要转换的字符串
     * @param int $start 开始位置
     * @param string $length 截取长度
     * @param string $charset 编码格式
     * @param bool|string $suffix 截断显示字符
     *
     * @return string
     */
    public function cutstr($str, $start = 0, $length, $charset = "utf-8", $suffix = true)
    {
        if (function_exists("mb_substr")) {
            $slice = mb_substr($str, $start, $length, $charset);
        } elseif (function_exists('iconv_substr')) {
            $slice = iconv_substr($str, $start, $length, $charset);
            if (false === $slice) {
                $slice = '';
            }
        } else {
            $re['utf-8'] = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
            $re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
            $re['gbk'] = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
            $re['big5'] = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
            preg_match_all($re[$charset], $str, $match);
            $slice = join("", array_slice($match[0], $start, $length));
        }

        if (get_str_len($str) > $length && $slice) {

            $slice = $slice . '...';
        }

        return $suffix ? $slice : $slice;
    }


    /**
     * 替换字符串中的/r/n为回车
     *
     * @param string $str 字符串
     *
     * @return string
     */
    public function enter($str = '')
    {

        $str = trim($str); //清除字符串两边的空格

        $replace = ["\r\n", "\n", "\r", "&crarr;", "&#8629;"];

        return trim(str_replace($replace, "<br/>", $str)); //返回字符串

    }


    /**
     * 根据规则生成随机比例红包记录
     *
     * @param int $ac_id 活动id
     * @param int $pid 红包id
     * @param array $rule_list 红包规则
     *              + red_proportion 比例
     *              + red_proportion_money 金额(元)
     * @param int $red_base_num 红包基数(默认100)
     *
     * @return bool
     */
    public function create_packet_record($ac_id = 0, $pid = 0, $rule_list = [], $red_base_num = 100)
    {
        // 实例化红包记录表Model
        $records_model = new PacketrecordModel();
        // 红包记录初始化
        $red_list = [];

        // 判断红包基数是否被100整除
        if (($red_base_num % 100) != 0) {

            E('_ERR_RED_BASE_NUM');
        }

        // 管理后台设置红包规则,红包基数代码中没有做上限设置验证,故需在此判断
        if ($red_base_num > 5000) {
            // 产品需求:如果红包基数大于5000,则每次生成5000
            $red_base_num = 5000;
        }

        foreach ($rule_list as $v) {
            // 计算当前规则比例的红包个数
            $red_total = intval($red_base_num) * (intval($v['red_proportion']) / 100);

            for ($i = 0; $i < $red_total; $i++) {
                // 组装红包记录数据
                $red_list[] = [
                    'pid' => $pid, // 红包id
                    'p_type' => self::RED_PACKET_RAND, // 随机比例红包
                    'ac_id' => $ac_id, // 活动id
                    'cid' => 0, // 回帖id
                    'rand_money' => $v['red_proportion_money'] * 100 // 金额(单位:分)
                ];
            }
        }

        // 打乱数组
        shuffle($red_list);
        // 将红包记录入库
        $total = $records_model->insert_all($red_list);

        // 删除大数组
        unset($red_list);

        return empty($total) ? false : true;
    }

    /**
     * 获取红包订单号
     *
     * @param int $type 红包类型(1:随机比例红包,2:定向红包,3.补发红包)
     * @param int $rid 红包记录id
     *
     * @return string
     */
    public function get_order_sn($type = 1, $rid = 0)
    {

        if (1 == $type) {
            $str = 'N';  // 随机比例红包
        } elseif (2 == $type) {
            $str = 'V';  // 定向红包
        } else {
            $str = 'R';  // 补发红包
        }

        if (empty($rid)) {
            $rid = rand(100, 999);
        }

        return $str . rgmdate(strval(MILLI_TIME), 'ymd') . sprintf("%08d", $rid);
    }

    /**
     * 推消息时给用户分配红包【只适用正常发放,不包含补发】
     *
     * @param array $data
     *          + int $ac_id 活动id
     *          + int $p_type 红包类型(1.随机比例红包,2.拼手气红包,3:普通红包,4:定向红包)
     *          + string $uid 用户id
     *          + string $comment_id 分享id
     *          + int $price 红包金额(定向红包此项为必填,单位:元)
     *          + string $send_uname 红包发放人姓名
     *          + string $send_phone 红包发放人电话
     *
     * @return array 红包记录 id 以及发放时间
     *          + int rid 红包记录 ID
     *          + int give_time 发放红包时间(毫秒时间戳)
     */
    public function get_red_record($data = [])
    {
        // 实例化红包记录表
        $records_model = new PacketrecordModel();
        // 实例化红包配置表
        $packet_model = new PacketModel();

        // 红包记录表自增 id 初始化
        $rid = 0;
        // 发放时间
        $give_time = MILLI_TIME;

        // 定向红包
        if (self::RED_PACKET_VIP == $data['p_type']) {
            // 红包金额
            $price = $data['price'];

            if (intval($price) < 1 || intval($price) > 2000) {
                // 红包金额设置不正确
                E('_ERR_RED_PRICE');
            }

            $user = User::instance()->getByUid($data['uid']);

            $record_arr = [
                'pid' => 0, // 红包id
                'p_type' => $data['p_type'], // 红包类型
                'ac_id' => $data['ac_id'], // 活动id
                'rand_money' => $data['price'] * 100, // 红包金额(单位:分)
                'packet_status' => self::PACKET_STATUS_WAIT, // 待领取
                'give_time' => $give_time // 发放时间
            ];
            $rid_new = (int)$records_model->insert($record_arr);

            // 组装更新数据,主要是为了更新订单id以及与用户的关联关系
            $update_arr = [
                'cid' => $data['comment_id'], // 回帖id
                'order_sn' => $this->get_order_sn(2, $rid_new), // 生成订单号
                'send_uname' => $data['send_uname'], // 发放人姓名
                'send_phone' => $data['send_phone'], // 发放人电话
                'uid' => $data['uid'], // 领取人uid
                'username' => $user['memUsername'], // 领取人姓名
            ];

            // 更新订单记录数据
            if ($records_model->update($rid_new, $update_arr)) {

                $rid = $rid_new;
            }
        } elseif (self::RED_PACKET_RAND == $data['p_type']) { // 随机红包
            // 查询红包规则:每人最大领取数量
            $packet_conds = [
                'ac_id' => $data['ac_id'],
            ];
            $packet_info = $packet_model->get_by_conds($packet_conds);

            // 如果设置了最多领取次数
            if (!empty($packet_info['max_total'])) {

                $record_conds = [
                    'uid' => $data['uid'],
                    'parent_rid' => 0, // parent_rid=0的是主红包
                    'ac_id' => $data['ac_id'], // 当前活动
                    'p_type' => self::RED_PACKET_RAND // 随机比例类型
                ];
                // 查询该用户此次活动领取次数
                $receive_count = $records_model->count_by_conds($record_conds);

                // 超过红包领取限制,结束程序
                if ($receive_count >= $packet_info['max_total']) {

                    return [];
                }
            }

            // 给用户分配红包记录
            $rid = $this->match_packet_record($data, $packet_info, $give_time);

        }

        return ['rid' => $rid, 'give_time' => $give_time];
    }


    /**
     * 递归给用户分配红包
     *
     * @param array $data 回帖数据
     * @param array $packet_info 红包配置数据
     * @param int $give_time 发放时间
     * @param int $step 递归次数
     *
     * @return int 红包记录ID
     */
    public function match_packet_record($data = [], $packet_info = [], $give_time = 0, $step = 0)
    {
        // 当前递归次数
        $step++;

        $rid = 0;  // 初始化红包记录ID

        if ($step > 5) {
            // 最多递归5次还没分配到,直接放弃
            return 0;
        }

        // 实例化红包记录表
        $records_model = new PacketrecordModel();

        // 组织查询未分配红包记录条件
        $record_conds = [
            'ac_id' => $data['ac_id'], // 活动id
            'packet_status' => self::PACKET_STATUS_WAIT, // 待领取
            'cid' => 0  // 回帖ID为0表示未分配
        ];

        // 获取【未分配】的红包数据
        $record = $records_model->get_by_conds($record_conds);

        if (empty($record)) {
            // 没有【未分配】的红包记录了,需要按照红包配置再次生成
            if (empty($packet_info)) {
                // 活动的红包配置未找到,抛错
                E('_ERR_REPEAT_DATA');
            }

            // 按规则生成红包
            $this->create_packet_record($data['ac_id'], $packet_info['id'], unserialize($packet_info['red_content']),
                $packet_info['red_base_num']);

            // 获取【未分配】的红包数据
            $record = $records_model->get_by_conds($record_conds);
        }

        if (!empty($record)) {
            // 有【未分配】的红包记录
            if (!isset($data['username'])) {
                // 从UC查询发帖人姓名 username(兼容传入参数中不含发帖人 username 的情况)
                $user = User::instance()->getByUid($data['uid']);
                $data['username'] = $user['memUsername'];
            }

            $update_data = [
                'order_sn' => $this->get_order_sn(1, $record['rid']),
                'uid' => $data['uid'],
                'username' => $data['username'], // 发帖人
                'cid' => $data['comment_id'], // 回帖 ID
                'packet_status' => self::PACKET_STATUS_WAIT, // 待领取
                'give_time' => $give_time // 发放时间
            ];
            // 更新红包记录
            if ($records_model->update_by_conds(['rid' => $record['rid'], 'cid' => 0], $update_data)) {
                // 如果执行成功,则该记录匹配给当前用户
                $rid = $record['rid'];
            }
        }

        if (empty($rid)) {
            // 如果没匹配到红包记录或者分配到红包记录,但是已被人抢占,则重新分配
            $this->match_packet_record($data, $packet_info, $give_time, $step);
        } else {

            return $rid;
        }

    }

    /**
     * 数组分页
     *
     * @param $data array 需分页的数组
     * @param $page int 页码
     * @param $limit int 每页数量
     *
     * @return array
     */
    public function get_page_data($data, $page, $limit)
    {
        $list = [];
        //计算每次分页的开始位置
        $start_limt = ($page - 1) * $limit;
        $list = array_slice($data, $start_limt, $limit);

        return $list;
    }


    /**
     * 获取活动未参与人数
     *
     * @param array $params 过滤条件
     * @param array $join_list 已参与列表
     *
     * @return array
     */
    public function count_unjoin($params, $join_list)
    {

        // 获取权限内总人数
        $right_m = new RightModel();
        $right_conds = [
            'ac_id' => $params['ac_id'],
        ];
        $right_list = $right_m->list_by_conds($right_conds);

        // 实例化User
        $user_s = new User();

        if (!empty($right_list)) {
            // 获取权限数组
            $uid = array_values(array_filter(array_column($right_list, 'uid')));
            $dp_id = array_values(array_filter(array_column($right_list, 'dp_id')));
            $job_id = array_values(array_filter(array_column($right_list, 'job_id')));
            $role_id = array_values(array_filter(array_column($right_list, 'role_id')));
            $tag_id = array_values(array_filter(array_column($right_list, 'tag_id')));

            // 查询用户信息,多次查询UC,合并去重

            $tag_list = [];
            if (!empty($tag_id)) {
                $tag_s = new Tag();
                $tag_list = $tag_s->listUserByTagId($tag_id, 1, 1000);
                $tag_dp_id = array_filter(array_column($tag_list['list'], 'dpId'));
                $tag_uid = array_filter(array_column($tag_list['list'], 'memUid'));
            }

            $uid_list = [];
            $uid_list = $tag_uid ? array_merge($tag_uid, $uid) : $uid;

            $dp_id = !empty($tag_dp_id) ? array_merge($tag_dp_id, $dp_id) : $dp_id;
            if (!empty($dp_id)) {
                $dp_list = $user_s->listAll([
                    'dpIdList' => $dp_id,
                    'departmentChildrenFlag' => 1,
                    'resultFields' => ['memUid'],
                ]);
            }

            $job_list = [];
            if (!empty($job_id)) {
                $job_list = $user_s->listAll([
                    'jobIdList' => $job_id,
                    'resultFields' => ['memUid'],
                ]);
            }

            $role_list = [];
            if (!empty($role_id)) {
                $role_list = $user_s->listAll([
                    'roleIdList' => $role_id,
                    'resultFields' => ['memUid'],
                ]);
            }

            $user_list = array_merge((array)$dp_list, (array)$job_list, (array)$role_list);

        } else {
            // 权限表不存在权限信息,为全公司,直接listAll
            $result_user = $user_s->listBasicByConds([], 1, 1);

            $res_list = $user_s->listBasicByConds([], 1, $result_user['total']);

            $user_list = $res_list['list'];
        }

        $user_uids = array_column($user_list, 'memUid');

        $user_uids = array_unique(array_merge((array)$user_uids, (array)$uid_list));

        // 剔除已参与的人员
        $join_uids = array_column($join_list, 'memId'); // 接口wiki原因,memId,不是memID,不是memUid,不是uid
        $unjoin_uids = array_diff($user_uids, $join_uids);

        return [
            'unjoin_uids' => $unjoin_uids, // 未参与的uid数组
            'unjoin_num' => count($unjoin_uids), // 未参与人数
        ];
    }

}