1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510 |
- <?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;
- }
- }
|