<?php

namespace App\Services\Home;

use App\Events\CompleteInfoRemind;
use App\Exceptions\AlertException;
use App\Jobs\FeedRecommendJob;
use App\Models\Common\FeedAlgorithmModel;
use App\Models\Deed\FriendsModel;
use App\Models\Deed\InvitationCardModel;
use App\Models\FlowerLogModel;
use App\Models\PartnerModel;
use App\Models\PraiseModel;
use App\Models\User\FeedType4RemindModel;
use App\Models\User\InviteConfigModel;
use App\Models\User\SuperLikeModel;
use App\Models\User\UserModel;
use App\Models\User\UserStateModel;
use App\Services\Service;
use App\Services\Vendor\Geohash;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Redis;
use Ixudra\Curl\Facades\Curl;
use PocketBE\MsyPush\Jobs\RegistEventJob;

/**
 * Class FeedService
 * @package App\Services\Home
 */
class FeedService extends Service
{
    /**
     * 获取卡片信息
     * @param  PartnerModel|integer  $arg
     * @param  int|string  $uid
     * @return PartnerModel
     */
    public function getPartner($arg, $uid)
    {
        if (is_int($arg)) {
            /** @var PartnerModel $data */
            $data = PartnerModel::where('id', $arg)->firstOrFail([
                'id',
                'uid',
                'school',
                'address',
                'home',
                'height',
                'star',
                'introduce',
                'expect',
                'sex',
                'photo_src',
                'check_photo',
                'is_sell',
                'photo_1',
                'photo_1_check',
                'photo_2',
                'photo_2_check',
                'photo_3',
                'photo_3_check',
                'photo_4',
                'photo_4_check',
                'voice',
                'voice_check',
                'praises',
                'age',
                'star',
                'is_recommend',
                'is_push_feed',
                'feed_push_type',
                'black_at',
                'last_three_day_feed',
            ]);
            $data->black_at = $data->black_at < time() ? 0 : $data->black_at;
        } else {
            /** @var PartnerModel $arg */
            $data = $arg;
        }

        $data->photo_src = "https://oss.pocketuniversity.cn{$data->photo_src}";
        $data->photo_1 = "https://oss.pocketuniversity.cn{$data->photo_1}";
        $data->photo_2 = "https://oss.pocketuniversity.cn{$data->photo_2}";
        $data->photo_3 = "https://oss.pocketuniversity.cn{$data->photo_3}";
        $data->photo_4 = "https://oss.pocketuniversity.cn{$data->photo_4}";
        if (1 != $data->check_photo) {
            unset($data->photo_src);
        }
        if (1 == $data->photo_1_check) {
        } else {
            unset($data->photo_1);
        }
        if (1 == $data->photo_2_check) {
        } else {
            unset($data->photo_2);
        }
        if (1 == $data->photo_3_check) {
        } else {
            unset($data->photo_3);
        }
        if (1 != $data->photo_4_check) {
            unset($data->photo_4);
        }
        if (1 != $data->voice_check) {
            unset($data->voice);
        }
        unset($data->check_photo);
        unset($data->photo_1_check);
        unset($data->photo_2_check);
        unset($data->photo_3_check);
        unset($data->photo_4_check);
        unset($data->voice_check);

        /** @var UserModel $user */
        $user = UserModel::findOrFail($data->uid, [
            'uid',
            'headimgurl',
            'nickname',
            'be_vip_at',
            'supvip_endat',
            'weixin',
            'qq',
            'age',
            'star',
            'introduce',
            'expect',
            'height',
            'sex',
            'school',
            'address',
            'home',
            'tag_1',
            'tag_2',
            'tag_3',
            'tag_4',
            'login_at',
            'location',
            'identity_auth',
            'wx_auth',
            'claim_tag',
            'work_state',
            'logoff_at',
        ]);

        /** @var PartnerModel $data */
        $key = "{beta:paint}";
        $bool = Redis::Sismember($key, $uid);
        if (($inviteConf = InviteConfigModel::find($data->uid))) {
            $user->task_question = $inviteConf->task_question;
            $user->task_sing = $inviteConf->task_sing;
            $user->task_question_data = $inviteConf->task_question_data ? true : false;
            $user->setAttribute('task_paint', $bool ? true : $inviteConf->task_paint);
            $user->setAttribute('task_sing_data', $inviteConf->task_sing_data ? true : false);
        } else {
            $user->task_question = false;
            $user->task_sing = false;
            $user->task_question_data = false;
            $user->setAttribute('task_paint', $bool);
            $user->setAttribute('task_sing_data', false);
        }

        $user->setAttribute('invite_cnt', InvitationCardModel::where('invite_uid', $data->uid)->count());

        // 与self的关系处理
        $self = array(
            'friend' => false,
            'praise' => false,
            'invite' => [],
            'superlike' => false,
        );
        if (is_int($uid)) {
            $friend = FriendsModel::where([['uid', $uid], ['friend_uid', $data->uid]])->first();
            if ($uid != $data->uid && collect($friend)->isEmpty()) {
                !empty($user->weixin) && $user->weixin = mb_substr($user->weixin, 0, 1) . "****" . mb_substr(
                    $user->weixin,
                    -1,
                    1
                );
                !empty($user->qq) && $user->qq = mb_substr($user->qq, 0, 1) . "****" . mb_substr($user->qq, -1, 1);
            }
            /** @var array $self */
            $self['friend'] = $friend;
            if (PraiseModel::where([['uid', $uid], ['partner_id', $data->id], ['type', 1]])->exists()) {
                $self['praise'] = true;
            }
            $invite = InvitationCardModel::where([
                ['uid', $uid],
                ['invite_uid', $data->uid],
                ['expired_at', '>', time()],
            ])->groupBy('question_type')->get(['question_type'])->pluck('question_type');
            $self['invite'] = $invite->toArray();
            if (SuperLikeModel::where([['uid', $uid], ['partner_id', $data->id]])->exists()) {
                $self['superlike'] = true;
            }
        }
        $data->setAttribute('user', $user);
        $data->setAttribute('self', $self);

        return $data;
    }

    /**
     * 获取用户算法方案和算法推荐结果
     * @param  int  $uid
     * @return FeedAlgorithmModel|Builder|mixed|null
     * @throws AlertException
     */
    public function randAlgorithm(int $uid)
    {
        $algorithms = FeedAlgorithmModel::where([
            ['open_at', '<', date('Y-m-d H:i:s')],
            ['end_at', '>', date('Y-m-d H:i:s')],
        ])->get();
        $total = $algorithms->sum('rate');
        $rand = rand(1, $total);
        $value = 0;
        foreach ($algorithms as $algorithm) {
            /** @var FeedAlgorithmModel $algorithm */
            $value = $value + $algorithm->rate;
            if ($rand <= $value) {
                UserStateModel::set($uid, 'algorithm', $algorithm->name);

                return $algorithm;
            }
        }

        return null;
    }

    /**
     * 获取算法推荐的集合
     * @param  int  $uid
     * @param  int  $sex  性别1男2女
     * @return array|null
     * @throws AlertException
     */
    public function algorithmRecommend(int $uid, int $sex = 1)
    {
        $current = UserStateModel::get($uid, 'algorithm');
        $selectAlgo = null;

        if (is_null($current)) {
            $selectAlgo = $this->randAlgorithm($uid);
        } else {
            $algorithm = FeedAlgorithmModel::where('name', $current)->first();
            if (collect($algorithm)->isEmpty() || !Carbon::now()->isBetween($algorithm->open_at, $algorithm->end_at)) {
                $selectAlgo = $this->randAlgorithm($uid);
            } else {
                $selectAlgo = $algorithm;
            }
        }
        if (is_null($selectAlgo)) {
            // 用户方案, 算法结果
            return ['N', null];
        } else {
            if ('N' == $selectAlgo->name) {
                // 用户方案, 算法结果
                return ['N', null];
            } else {
                if (1 == $sex) {
                    $api = trim($selectAlgo->man_url . $uid . "?number=464656");
                } else {
                    $api = trim($selectAlgo->woman_url . $uid . "?number=464656");
                }
                $resp = Curl::to($api)->withTimeout(1)->asJsonResponse(true)->get();

                return [$selectAlgo->name, $resp];
            }
        }
    }

    /**
     * 信息流-对检索的结果进行组合输出
     * @param $feedArr
     * @param  int  $sxo
     * @param  int  $take
     * @return array
     * @throws AlertException
     */
    public function feed($feedArr, int $sxo, int $take = 2)
    {
        // 不同用户层级分配的卡片层级比例
        $levelRates = array(
            'A' => [
                1 => [
                    [
                        'pool' => 'likeme',
                        'weight' => 5,
                    ],
                    [
                        'pool' => 'pool',
                        'weight' => 95,
                        'attribute' => [
                            [
                                'pool' => 'low',
                                'weight' => 10,
                            ],
                            [
                                'pool' => 'normal',
                                'weight' => 90,
                                'attribute' => [
                                    [
                                        'pool' => 'select',
                                        'weight' => 80,
                                    ],
                                    [
                                        'pool' => 'recommend',
                                        'weight' => 20,
                                    ],
                                    [
                                        'pool' => 'new',
                                        'weight' => 0,
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
                2 => [
                    [
                        'pool' => 'likeme',
                        'weight' => 5,
                    ],
                    [
                        'pool' => 'pool',
                        'weight' => 95,
                        'attribute' => [
                            [
                                'pool' => 'low',
                                'weight' => 5,
                            ],
                            [
                                'pool' => 'normal',
                                'weight' => 95,
                                'attribute' => [
                                    [
                                        'pool' => 'select',
                                        'weight' => 80,
                                    ],
                                    [
                                        'pool' => 'recommend',
                                        'weight' => 20,
                                    ],
                                    [
                                        'pool' => 'new',
                                        'weight' => 0,
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
            'B' => [
                1 => [
                    [
                        'pool' => 'likeme',
                        'weight' => 5,
                    ],
                    [
                        'pool' => 'pool',
                        'weight' => 95,
                        'attribute' => [
                            [
                                'pool' => 'low',
                                'weight' => 25,
                            ],
                            [
                                'pool' => 'normal',
                                'weight' => 75,
                                'attribute' => [
                                    [
                                        'pool' => 'select',
                                        'weight' => 40,
                                    ],
                                    [
                                        'pool' => 'recommend',
                                        'weight' => 60,
                                    ],
                                    [
                                        'pool' => 'new',
                                        'weight' => 0,
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
                2 => [
                    [
                        'pool' => 'likeme',
                        'weight' => 5,
                    ],
                    [
                        'pool' => 'pool',
                        'weight' => 95,
                        'attribute' => [
                            [
                                'pool' => 'low',
                                'weight' => 20,
                            ],
                            [
                                'pool' => 'normal',
                                'weight' => 80,
                                'attribute' => [
                                    [
                                        'pool' => 'select',
                                        'weight' => 40,
                                    ],
                                    [
                                        'pool' => 'recommend',
                                        'weight' => 60,
                                    ],
                                    [
                                        'pool' => 'new',
                                        'weight' => 0,
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
            'C' => [
                1 => [
                    [
                        'pool' => 'likeme',
                        'weight' => 5,
                    ],
                    [
                        'pool' => 'pool',
                        'weight' => 95,
                        'attribute' => [
                            [
                                'pool' => 'low',
                                'weight' => 25,
                            ],
                            [
                                'pool' => 'normal',
                                'weight' => 75,
                                'attribute' => [
                                    [
                                        'pool' => 'select',
                                        'weight' => 20,
                                    ],
                                    [
                                        'pool' => 'recommend',
                                        'weight' => 80,
                                    ],
                                    [
                                        'pool' => 'new',
                                        'weight' => 0,
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
                2 => [
                    [
                        'pool' => 'likeme',
                        'weight' => 5,
                    ],
                    [
                        'pool' => 'pool',
                        'weight' => 95,
                        'attribute' => [
                            [
                                'pool' => 'low',
                                'weight' => 20,
                            ],
                            [
                                'pool' => 'normal',
                                'weight' => 80,
                                'attribute' => [
                                    [
                                        'pool' => 'select',
                                        'weight' => 20,
                                    ],
                                    [
                                        'pool' => 'recommend',
                                        'weight' => 80,
                                    ],
                                    [
                                        'pool' => 'new',
                                        'weight' => 0,
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
            'D' => [
                1 => [
                    [
                        'pool' => 'likeme',
                        'weight' => 5,
                    ],
                    [
                        'pool' => 'pool',
                        'weight' => 95,
                        'attribute' => [
                            [
                                'pool' => 'low',
                                'weight' => 25,
                            ],
                            [
                                'pool' => 'normal',
                                'weight' => 75,
                                'attribute' => [
                                    [
                                        'pool' => 'select',
                                        'weight' => 10,
                                    ],
                                    [
                                        'pool' => 'recommend',
                                        'weight' => 90,
                                    ],
                                    [
                                        'pool' => 'new',
                                        'weight' => 0,
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
                2 => [
                    [
                        'pool' => 'likeme',
                        'weight' => 5,
                    ],
                    [
                        'pool' => 'pool',
                        'weight' => 95,
                        'attribute' => [
                            [
                                'pool' => 'low',
                                'weight' => 20,
                            ],
                            [
                                'pool' => 'normal',
                                'weight' => 80,
                                'attribute' => [
                                    [
                                        'pool' => 'select',
                                        'weight' => 10,
                                    ],
                                    [
                                        'pool' => 'recommend',
                                        'weight' => 90,
                                    ],
                                    [
                                        'pool' => 'new',
                                        'weight' => 0,
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
            'E' => [
                1 => [
                    [
                        'pool' => 'likeme',
                        'weight' => 5,
                    ],
                    [
                        'pool' => 'pool',
                        'weight' => 95,
                        'attribute' => [
                            [
                                'pool' => 'low',
                                'weight' => 25,
                            ],
                            [
                                'pool' => 'normal',
                                'weight' => 75,
                                'attribute' => [
                                    [
                                        'pool' => 'select',
                                        'weight' => 20,
                                    ],
                                    [
                                        'pool' => 'recommend',
                                        'weight' => 50,
                                    ],
                                    [
                                        'pool' => 'new',
                                        'weight' => 30,
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
                2 => [
                    [
                        'pool' => 'likeme',
                        'weight' => 5,
                    ],
                    [
                        'pool' => 'pool',
                        'weight' => 95,
                        'attribute' => [
                            [
                                'pool' => 'low',
                                'weight' => 20,
                            ],
                            [
                                'pool' => 'normal',
                                'weight' => 80,
                                'attribute' => [
                                    [
                                        'pool' => 'select',
                                        'weight' => 20,
                                    ],
                                    [
                                        'pool' => 'recommend',
                                        'weight' => 50,
                                    ],
                                    [
                                        'pool' => 'new',
                                        'weight' => 30,
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
            'F' => [
                1 => [
                    [
                        'pool' => 'likeme',
                        'weight' => 5,
                    ],
                    [
                        'pool' => 'pool',
                        'weight' => 95,
                        'attribute' => [
                            [
                                'pool' => 'low',
                                'weight' => 25,
                            ],
                            [
                                'pool' => 'normal',
                                'weight' => 75,
                                'attribute' => [
                                    [
                                        'pool' => 'select',
                                        'weight' => 10,
                                    ],
                                    [
                                        'pool' => 'recommend',
                                        'weight' => 60,
                                    ],
                                    [
                                        'pool' => 'new',
                                        'weight' => 30,
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
                2 => [
                    [
                        'pool' => 'likeme',
                        'weight' => 5,
                    ],
                    [
                        'pool' => 'pool',
                        'weight' => 95,
                        'attribute' => [
                            [
                                'pool' => 'low',
                                'weight' => 20,
                            ],
                            [
                                'pool' => 'normal',
                                'weight' => 80,
                                'attribute' => [
                                    [
                                        'pool' => 'select',
                                        'weight' => 10,
                                    ],
                                    [
                                        'pool' => 'recommend',
                                        'weight' => 60,
                                    ],
                                    [
                                        'pool' => 'new',
                                        'weight' => 30,
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
            'G' => [
                1 => [
                    [
                        'pool' => 'likeme',
                        'weight' => 5,
                    ],
                    [
                        'pool' => 'pool',
                        'weight' => 95,
                        'attribute' => [
                            [
                                'pool' => 'low',
                                'weight' => 25,
                            ],
                            [
                                'pool' => 'normal',
                                'weight' => 75,
                                'attribute' => [
                                    [
                                        'pool' => 'select',
                                        'weight' => 5,
                                    ],
                                    [
                                        'pool' => 'recommend',
                                        'weight' => 65,
                                    ],
                                    [
                                        'pool' => 'new',
                                        'weight' => 30,
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
                2 => [
                    [
                        'pool' => 'likeme',
                        'weight' => 5,
                    ],
                    [
                        'pool' => 'pool',
                        'weight' => 95,
                        'attribute' => [
                            [
                                'pool' => 'low',
                                'weight' => 20,
                            ],
                            [
                                'pool' => 'normal',
                                'weight' => 80,
                                'attribute' => [
                                    [
                                        'pool' => 'select',
                                        'weight' => 5,
                                    ],
                                    [
                                        'pool' => 'recommend',
                                        'weight' => 65,
                                    ],
                                    [
                                        'pool' => 'new',
                                        'weight' => 30,
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
        );
        $disRates = array(
            'likeme' => array(
                [
                    'min' => 0,
                    'max' => 20,
                    'weight' => 30,
                ],
                [
                    'min' => 20,
                    'max' => 50,
                    'weight' => 30,
                ],
                [
                    'min' => 50,
                    'max' => 100,
                    'weight' => 15,
                ],
                [
                    'min' => 100,
                    'max' => 200,
                    'weight' => 10,
                ],
                [
                    'min' => 200,
                    'max' => 300,
                    'weight' => 7,
                ],
                [
                    'min' => 300,
                    'max' => 400,
                    'weight' => 5,
                ],
                [
                    'min' => 400,
                    'max' => INF,
                    'weight' => 3,
                ],
            ),
            'low' => array(
                [
                    'min' => 0,
                    'max' => 20,
                    'weight' => 30,
                ],
                [
                    'min' => 20,
                    'max' => 50,
                    'weight' => 30,
                ],
                [
                    'min' => 50,
                    'max' => 100,
                    'weight' => 15,
                ],
                [
                    'min' => 100,
                    'max' => 200,
                    'weight' => 10,
                ],
                [
                    'min' => 200,
                    'max' => 300,
                    'weight' => 7,
                ],
                [
                    'min' => 300,
                    'max' => 400,
                    'weight' => 5,
                ],
                [
                    'min' => 400,
                    'max' => INF,
                    'weight' => 3,
                ],
            ),
            'new' => array(
                [
                    'min' => 0,
                    'max' => 20,
                    'weight' => 30,
                ],
                [
                    'min' => 20,
                    'max' => 50,
                    'weight' => 30,
                ],
                [
                    'min' => 50,
                    'max' => 100,
                    'weight' => 15,
                ],
                [
                    'min' => 100,
                    'max' => 200,
                    'weight' => 10,
                ],
                [
                    'min' => 200,
                    'max' => 300,
                    'weight' => 7,
                ],
                [
                    'min' => 300,
                    'max' => 400,
                    'weight' => 5,
                ],
                [
                    'min' => 400,
                    'max' => INF,
                    'weight' => 3,
                ],
            ),
            'recommend' => array(
                [
                    'min' => 0,
                    'max' => 20,
                    'weight' => 30,
                ],
                [
                    'min' => 20,
                    'max' => 50,
                    'weight' => 30,
                ],
                [
                    'min' => 50,
                    'max' => 100,
                    'weight' => 15,
                ],
                [
                    'min' => 100,
                    'max' => 200,
                    'weight' => 10,
                ],
                [
                    'min' => 200,
                    'max' => 300,
                    'weight' => 7,
                ],
                [
                    'min' => 300,
                    'max' => 400,
                    'weight' => 5,
                ],
                [
                    'min' => 400,
                    'max' => INF,
                    'weight' => 3,
                ],
            ),
            'select' => array(
                [
                    'min' => 0,
                    'max' => 20,
                    'weight' => 30,
                ],
                [
                    'min' => 20,
                    'max' => 50,
                    'weight' => 30,
                ],
                [
                    'min' => 50,
                    'max' => 100,
                    'weight' => 15,
                ],
                [
                    'min' => 100,
                    'max' => 200,
                    'weight' => 10,
                ],
                [
                    'min' => 200,
                    'max' => 300,
                    'weight' => 7,
                ],
                [
                    'min' => 300,
                    'max' => 400,
                    'weight' => 5,
                ],
                [
                    'min' => 400,
                    'max' => INF,
                    'weight' => 3,
                ],
            ),
        );
//        dump("开始:".microtime(true));
        $res = array();
        for ($i = 0; $i < $take; $i++) {
            // 滑卡奖励
            $flowerLog = new FlowerLogModel();
            if (is_int($feedArr['uid']) && $redpack = $flowerLog->redpack($feedArr['uid'])) {
                Redis::hincrby("session_msy_{$feedArr['uid']}", "feed_limit", 1);
                $res[] = array(
                    'type' => 1,
                    'data' => $redpack,
                );
                continue;
            }
//            dump("  输出单元{$i}:" . microtime(true));
            $levelCnt = 0;
            do {
//                dump("      输出单元{$i}定义检索条件:" . microtime(true));
                $poolList = $levelRates[$feedArr['feed_level']][$sxo];
                ++$levelCnt;
                if (0 == $feedArr['partner_id']) {
                    $pool = "select";   // 新用户只看精选的 - 正常卡片-标记精选池分配比例
                } else {
                    $poolArray = $this->getRandomList($poolList, 'weight');
                    if ('pool' == $poolArray['pool']) {
                        $poolArray = $this->getRandomList($poolArray['attribute'], 'weight');
                        if ('normal' == $poolArray['pool']) {
                            $poolArray = $this->getRandomList($poolArray['attribute'], 'weight');
                            $pool = $poolArray['pool'];
                        } else {
                            $pool = $poolArray['pool'];
                        }
                    } else {
                        $pool = $poolArray['pool'];
                    }
                }

                $disList = $disRates[$pool];
                $disArray = $this->getRandomList($disList, 'weight');
                $reduceFeed = Redis::get("reduce_feed");
                if ($reduceFeed && $reduceFeed > 0) {
                    $disArray = array(
                        'min' => 0,
                        'max' => INF,
                    );
                }
//                dump("      得到检索条件:性别:{$sxo};池:{$pool};min:{$disArray['min']};max:{$disArray['max']};" .microtime(true));
                $feed = $this->feedpartner($feedArr, $sxo, $pool, $disArray['min'], $disArray['max']);
                // Ntodo 移除没有的$poolList和$disList
            } while (collect($feed)->isEmpty() && $levelCnt < 3);

            if (collect($feed)->isEmpty()) {
                continue;
            } else {
                Redis::hincrby("session_msy_{$feedArr['uid']}", "feed_limit", 1);
                $res[] = array(
                    'type' => $feed->feed_push_type,
                    'data' => $feed,
                );
                continue;
            }
        }

        return $res;
    }

    /**
     * 检索feed流卡片
     * @param  array  $feedUser  检索者信息
     * @param  int  $sxo  输出性别
     * @param  string  $pool  卡片等级
     * @param  int  $minDis  最近距离
     * @param  float  $maxDis  最远距离
     * @return PartnerModel
     * @throws AlertException
     */
    public function feedpartner(array $feedUser, int $sxo, string $pool, int $minDis = 0, float $maxDis = INF)
    {
        // LBS缓存key
        $cacheLbskey = ($sxo == 1) ? "fpdx:user:locations:sell:boy" : "fpdx:user:locations:sell:girl";
        // 看过的卡片id缓存key
        $cacheIsSeedPartnerIds = "charge_feed_{$feedUser['uid']}";
        // 信息流所以卡片分数缓存key
        $cachePartnerScore = "feed:push:partner:score";
        // 卡片等级缓存key
        $searchUidskey = "feed:pool:{$pool}:sex:{$sxo}";
        // 某人某条件搜索的缓存key
        $cacheKey = "feed:user:{$feedUser['uid']}:sex:{$sxo}:pool:{$pool}:mindis:{$minDis}:maxdis:{$maxDis}";
        // 某人某条件搜索的结果缓存key
        $cacheResult = "feed:user:{$feedUser['uid']}:sex:{$sxo}:pool:{$pool}:mindis:{$minDis}:maxdis:{$maxDis}:result";

        $feed = new PartnerModel();
        $whereUid = Redis::smembers($cacheKey);
        if (empty($whereUid)) {
//            dump("未命中缓存:".microtime(true));
            // 可查询的用户uid集合A
            $where = array(['sex', $sxo], ['is_sell', 1], ['score', '>=', 0]);
            switch ($pool) {
                case 'likeme':                  // 喜欢我的
                    $useAlgorithm = false;
                    $uids = PraiseModel::where([
                        ['partner_id', $feedUser['partner_id']],
                        ['type', 1],
                    ])->get(['uid'])->pluck('uid')->toArray();
                    $searchUids = PartnerModel::where(array(['sex', $sxo], ['is_sell', 1]))->whereIn(
                        'uid',
                        $uids
                    )->get(['uid'])->pluck('uid')->toArray();
                    break;
                case 'select':                  // 正常卡片-标记精选池分配比例
                    $useAlgorithm = false;      // 是否运用算法
                    $where[] = array('is_select', 1);
                    $searchUids = PartnerModel::where($where)->get(['uid'])->pluck('uid')->toArray();
                    break;
                case 'low':                     // 低保池
                    $useAlgorithm = false;      // 是否运用算法
                    $searchUids = Redis::Smembers($searchUidskey);
                    break;
                case 'recommend':  // 正常卡片-标记推荐池分配比例
                    $useAlgorithm = false;       // 是否运用算法 true
                    $searchUids = Redis::Smembers($searchUidskey);
                    break;
                case 'new': // 正常卡片-新卡片(未标记)池分配比例
                    $useAlgorithm = false;       // 是否运用算法 true
                    $searchUids = Redis::Smembers($searchUidskey);
                    break;
                default:
                    return $feed;
            }
            $whereUid = $searchUids;

            // 看过的用户集合B
            $hideIds = Redis::zrange($cacheIsSeedPartnerIds, 0, -1);
            if (empty($hideIds)) {
                $hideIds = array();
            }
            $isSeeUids = PartnerModel::whereIn('id', $hideIds)->get(['uid'])->pluck('uid')->toArray();
            $whereUid = array_diff($whereUid, $isSeeUids);
            if (empty($whereUid)) {
                Redis::sadd($cacheKey, ...[-1]);
                Redis::setex($cacheResult, 3600 * 2, 0);
                Redis::expire($cacheKey, 3600 * 2);

                return collect([]);
            }

            // LBS用户集合D

            if (INF == $maxDis) {
                $maxDisUids = $whereUid;
            } else {
                $maxDisUids = Redis::geoRadius(
                    $cacheLbskey,
                    $feedUser['lng'],
                    $feedUser['lat'],
                    $maxDis,
                    "km",
                    array('ASC')
                );
            }
            if (0 == $minDis) {
                $minDisUids = array();
            } else {
                $minDisUids = Redis::geoRadius(
                    $cacheLbskey,
                    $feedUser['lng'],
                    $feedUser['lat'],
                    $minDis,
                    "km",
                    array('ASC')
                );
            }
            $disUids = array_diff($maxDisUids, $minDisUids);
            $whereUid = array_intersect($whereUid, $disUids);
            // 算法卡片用户集合C
            $isAlgorithm = false;
            if (is_integer($feedUser['uid'])) {
                list($selectPlan, $algorithm) = $this->algorithmRecommend($feedUser['uid'], $feedUser['sex']);
            } else {
                list($selectPlan, $algorithm) = array('N', null);
            }
            if ($useAlgorithm && !is_null($algorithm)) {
                $algorithmIds = array_column($algorithm, 'ItemId');
                $algoHash = array_combine($algorithmIds, $algorithm);
                $algorithmUids = PartnerModel::whereIn('id', $algorithmIds)->get(['id', 'uid'])->toArray();
                $algorithmUids = array_column($algorithmUids, 'uid');
                $byFeed = $selectPlan;
            } else {
                $algorithmUids = [];
                $algoHash = [];
                $byFeed = 'N';
            }
            // 结果集合E
            $whereAUid = array_intersect($whereUid, $algorithmUids);
            if (empty($whereAUid)) {
                $algoHash = [];
                $byFeed = 'N';
            } else {
                $whereUid = $whereAUid;
            }
        } else {
            if (1 == abs($whereUid[0])) {
//                dump("未命中缓存".microtime(true));
                return collect([]);
            }
//            dump("命中缓存".microtime(true));
            // 看过的用户集合B
            $hideIds = Redis::zrange("charge_feed_{$feedUser['uid']}", 0, -1);
            if (empty($hideIds)) {
                $hideIds = array();
            }
            $isSeeUids = PartnerModel::whereIn('id', $hideIds)->get(['uid'])->pluck('uid')->toArray();
            $whereUid = array_diff($whereUid, $isSeeUids);

            if (is_integer($feedUser['uid'])) {
                list($selectPlan, $algorithm) = $this->algorithmRecommend($feedUser['uid'], $feedUser['sex']);
            } else {
                list($selectPlan, $algorithm) = array('N', null);
            }
            if (!is_null($algorithm)) {
                $algorithmIds = array_column($algorithm, 'ItemId');
                $algoHash = array_combine($algorithmIds, $algorithm);
            } else {
                $algoHash = [];
            }

            $isAlgorithm = Redis::get($cacheResult) ?? 0;
            if ($isAlgorithm) {
                $byFeed = $selectPlan;
            } else {
                $byFeed = "N";
            }
        }

        if (empty($whereUid)) {
            Redis::sadd($cacheKey, ...[-1]);
            Redis::setex($cacheResult, 3600 * 2, 0);
            Redis::expire($cacheKey, 3600 * 2);

            return collect([]);
        }
        $scoreRandomUids = array();
        foreach ($whereUid as $wu) {
            $scoreRandomUids[] = array(
                'uid' => $wu,
                'score' => rand(1, 100),
            );
        }
//        dump("          得到可用集合" . microtime(true));
//        $whereUidScores = array_combine($whereUid, array_pad([], count($whereUid), 0));
//        Redis::zadd($cachePartnerScoreWhereUid, $whereUidScores);
//        Redis::zinterstore($cachePartnerScoreResult, [$cachePartnerScore, $cachePartnerScoreWhereUid]);
//        $scoreUids = Redis::zrange($cachePartnerScoreResult, 0, -1, ['withscores' => true]);
//        Redis::del([$cachePartnerScoreWhereUid, $cachePartnerScoreResult]);
//        if (empty($scoreUids)) {
//            Redis::sadd($cacheKey, ...[-1]);
//            Redis::setex($cacheResult, 3600 * 2, 0);
//            Redis::expire($cacheKey, 3600 * 2);
//            return collect([]);
//        } else {
//            $scoreRandomUids = array();
//            foreach ($scoreUids as $sk => $sv) {
//                $scoreRandomUids[] = array(
//                    'score' => $sv,
//                    'uid' => $sk
//                );
//            }
//        }

        $resultUid = $this->getRandomList($scoreRandomUids, 'score');
//        dump("          加权随机" . microtime(true));
        $cacheWhereUid = array_diff($whereUid, [$resultUid['uid']]);
        if (!empty($cacheWhereUid)) {
            Redis::sadd($cacheKey, ...$cacheWhereUid);
            Redis::setex($cacheResult, 3600 * 2, $isAlgorithm ? 1 : 0);
            Redis::expire($cacheKey, 3600 * 2);
        }

        $feed = PartnerModel::whereIn('uid', $resultUid)->where('is_sell', 1)->first();
        if (!$feed) {
            return collect([]);
        }
        $feed = $this->getPartner(/** @var PartnerModel $feed */ $feed, $feedUser['uid']);
        if ($pool == 'low') {
            $feed->setAttribute('feed_push_type', 5);
        }
        $attach = array(
            'belong_feed' => $selectPlan,                                           // 用户所属方案
            'by_feed' => $byFeed,                                                       // 卡片的输出方案
            'algorithm_score' => 0,                     // 算法分数
            'user_level' => $feedUser['feed_level'],                                // 用户分级
            'partner_level' => $pool,                                               // 卡片分级
        );
        if ($isAlgorithm) {
            $attach['algorithm_score'] = $algoHash[$feed->id]['Score'];
        }
        $feed->setAttribute('attach', json_encode($attach));
        Redis::zadd("charge_feed_{$feedUser['uid']}", [$feed->id => time()]);
        Redis::expire("charge_feed_{$feedUser['uid']}", 86400 * 60);

//        dump("          输出:".microtime(true));
        return $feed;
    }

    /**
     * 加权随机数
     * @param  array  $list  随机项
     * @param  string  $weightKey  权重字段
     * @return int|string|null
     */
    public function getRandomList(array $list, string $weightKey = "score")
    {
        $sum = 0;
        $listPoint = array(0);
        foreach ($list as $key => $value) {
            if (0 > $value[$weightKey]) {
                $value[$weightKey] = 1;
            }
            $sum += $value[$weightKey];
            array_push($listPoint, $sum);
        }
        $num = rand(0, $sum);
        for ($i = 0; $i < count($listPoint) - 1; $i++) {
            if ($num >= $listPoint[$i] && $num <= $listPoint[$i + 1]) {
                $elem = array_slice($list, $i, 1);

                return array_pop($elem);
            }
        }

        return array_pop(array_slice($list, 0, 1));
    }

    /**
     * 获取用户和卡片之间的分数
     * @param  int  $uid
     * @param  int  $partner_id
     * @return array
     */
    public function getScoreUid2Partner(int $uid, int $partner_id): array
    {
        $info = array();
        $user = UserModel::find($uid);
        /** @var UserModel $puser */
        $puser = UserModel::where('partner_id', $partner_id)->firstOrFail();

        $score = array(
            'score' => 0,
            'show' => "not_show",
        );

        // 预需求
        $user_pre = explode(',', $user->claim_tag);
        $puser_pre = explode(',', $puser->claim_tag);
        $pre_request = count(array_intersect($user_pre, $puser_pre)) * 20;
        if ($pre_request > $score['score']) {
            $score['score'] = $pre_request;
            $score['show'] = "pre_request";
        }

        // 工作状态
        $work_state = 0;
        if (!empty($puser->school)) {
            switch ($user->work_state) {
                case "工作党":
                    $work_state = 40;
                    break;
                case "高中党":
                case "大学党":
                    $work_state = 80;
                    break;
                default:
                    $work_state = 60;
                    break;
            }
        }
        if ($work_state > $score['score']) {
            $score['score'] = $work_state;
            $score['show'] = "work_state";
        }

        // 兴趣
        $puser_tag2 = explode(',', $puser->tag_2);
        $user_tag2 = explode(',', $user->tag_2);
        $puser_tag3 = explode(',', $puser->tag_3);
        $user_tag3 = explode(',', $user->tag_3);
        $puser_tag4 = explode(',', $puser->tag_4);
        $user_tag4 = explode(',', $user->tag_4);
        $intersect_tag2 = array_intersect($puser_tag2, $user_tag2);
        $intersect_tag3 = array_intersect($puser_tag3, $user_tag3);
        $intersect_tag4 = array_intersect($puser_tag4, $user_tag4);
        $info['tags'] = compact('intersect_tag2', 'intersect_tag3', 'intersect_tag4');
        $cnt = count($intersect_tag2) + count($intersect_tag3) + count($intersect_tag4);
        $tag = $cnt * 10;
        if ($tag > $score['score']) {
            $score['score'] = $tag;
            $score['show'] = "tag";
        }

        // 身高
        if ($user->height != 0 && $puser->height != 0) {
            if ($user->sex == 1) {
                $height = ceil(100 / abs($user->height / 1.09 - $puser->height));
            } else {
                $height = ceil(100 / abs($puser->height / 1.09 - $user->height));
            }
            if ($height > $score['score']) {
                $score['score'] = $height;
                $score['show'] = "height";
            }
        }

        // 家乡
        $home = 0;
        if (!empty($puser->home)) {
            if ($puser->home == $user->home) {
                $home = 100;
            }
            if ($home > $score['score']) {
                $score['score'] = $home;
                $score['show'] = "home";
            }
        }

        // 距离
        $geo = new Geohash();
        $dis = $geo->getDistance($user->lat, $user->lng, $puser->lat, $puser->lng) + rand(1000, 5000);

        return array($puser->location, $dis, $score, $info);
    }

    /**
     * feed流每日滑卡片限制
     * @param  int  $uid
     * @return bool
     */
    public function limit(int $uid)
    {
        $last_time = Redis::hget("session_msy_{$uid}", "last_feed_time") ?? time();
        if ($last_time < mktime(0, 0, 0)) {
            Redis::hset("session_msy_{$uid}", "feed_limit", 0);
            Redis::hset("session_msy_{$uid}", "flower_limit", 0);
            Redis::hset("session_msy_{$uid}", "hd_cnt", 0);
            Redis::hset("session_msy_{$uid}", "init_recommend_queues", 0);
            $count = Redis::zcount("fpdx:feed:thumb:{$uid}", 0, "+inf");
            if ($count <= 10) {
                Redis::hset("session_msy_{$uid}", "thumbme_limit", 2);
            } elseif ($count > 10 && $count < 50) {
                Redis::hset("session_msy_{$uid}", "thumbme_limit", 5);
            } else {
                Redis::hset("session_msy_{$uid}", "thumbme_limit", 10);
            }
        }
        $limit = Redis::hget("session_msy_{$uid}", "hd_cnt");
        $low_limit = Redis::hget("session_msy_{$uid}", "feed_limit");
        Redis::hset("session_msy_{$uid}", "last_feed_time", time());
        Redis::expire("session_msy_{$uid}", 86400);

        if (($limit > 200 || $low_limit > 400) && !in_array($uid, [94302, 98047, 10771042])) {
            return false;
        } else {
            if (0 == Redis::hget("session_msy_{$uid}", "init_recommend_queues")) {
                FeedRecommendJob::dispatch($uid);
                Redis::hset("session_msy_{$uid}", "init_recommend_queues", 1);
            }
            Redis::hincrby("session_msy_{$uid}", "feed_count", 1);

            return true;
        }
    }

    /**
     * 获取点赞队列存量
     * @param  int  $uid
     * @return int
     */
    public function getHasThumb(int $uid)
    {
        $count = Redis::zcount("fpdx:feed:thumb:{$uid}", 0, "+inf");

        return $count;
    }

    /**
     * 提醒TA补全信息
     * @param  int  $uid
     * @param  int  $remind_uid
     * @param $type
     * @return bool
     */
    public function remindType4(int $uid, int $remind_uid, $type): bool
    {
        // 我提醒过
        if (
            FeedType4RemindModel::where([
            ['uid', $uid],
            ['is_remind_uid', $remind_uid],
            ['is_update_feedback_at', 0],
            ['type', $type],
            ])->exists()
        ) {
            return false;
        }

        FeedType4RemindModel::create([
            'uid' => $uid,
            'is_remind_uid' => $remind_uid,
            'type' => $type,
        ]);

        // 被提醒过两次以上则不发送
        if (FeedType4RemindModel::where(['is_remind_uid' => $remind_uid, 'type' => $type])->count() > 2) {
            return false;
        }

        switch ($type) {
            case '1':
                // 发起提醒事件
                $nickname = UserModel::find($uid)->value('nickname');
                event(new CompleteInfoRemind(UserModel::find($remind_uid), $nickname));
                break;
            case '2':
                // 通知队列
                $to_user = UserModel::find($remind_uid);
                $from_user = UserModel::find($uid);
                $payload = [
                    'to_user' => $to_user->getAuth(),
                    'user' => $to_user->toArray(),
                    'from_user' => $from_user->toArray(),
                ];
                dispatch(new RegistEventJob(100009, $payload))->onQueue("{push}");
                break;
            case '3':
                // 通知队列
                $to_user = UserModel::find($remind_uid);
                $from_user = UserModel::find($uid);
                $payload = [
                    'to_user' => $to_user->getAuth(),
                    'user' => $to_user->toArray(),
                    'from_user' => $from_user->toArray(),
                ];
                dispatch(new RegistEventJob(100008, $payload))->onQueue("{push}");
                break;
        }

        return true;
    }
}
