<?php
/**
  * @method 统计
  * @author soul
  * @copyright 2018/10/31
  */
class AdsTj extends AdsBase
{
	/**
	  * @method 广告统计日报表生成
	  * @author soul
	  * @copyright 2020/4/30
	  * @return 
	  */
	public function mainTongji()
	{
		//根据IP更新省市
		/*$OthPct = new OthPct();
		$ipres = $this->get('pj_ads_show', ["distinct as_ip"],  " and JSON_UNQUOTE(as_pcts->'$.pid') = 0");
		foreach($ipres['data'] as $vs)
		{
			$ires = $OthPct->getIpPCT($vs['as_ip']);
			if(!empty($ires['data']['pctids']))
			{
				$pctIdArr = $ires['data']['pctids'];
				$set = [
					'as_pcts'=>json_encode([
							"pid"=>(int) $pctIdArr[0],
							"cid"=>(int) $pctIdArr[1]
					])
				];
				$this->set('pj_ads_show', $set, sprintf(" and as_ip='%s' and JSON_UNQUOTE(as_pcts->'$.pid') = 0", $vs['as_ip']));
			}
		}*/

		//查找上一次执行的最后日期
		$firstDate = '20200420';
		$res = $this->get('pj_ads_report_date', ['max(ard_date) as ard_date'], '', true);
		if(empty($res['data']['ard_date']))
			$res = $this->get('pj_ads_show', ["min(as_date) as ard_date"], '', true);
		if(!empty($res['data']['ard_date'])) 
			$firstDate = $res['data']['ard_date'];

		$dateArr = [];
		$dateArr = array_map( function ($n) { return date('Ymd', $n);}, range(strtotime($firstDate), strtotime(date('Ymd')), 24 * 3600));
		asort($dateArr);

		foreach($dateArr as $date)
		{
			$this->AdsDb->Begin();

			//清空最后日期的数据统计
			$this->del('pj_ads_report_date', ' and ard_date = '.$date);

			//点击统计
			$clickArr = [];
			$where = sprintf(" and ac_date=%d GROUP BY ac_pa_id, ac_ads_id, pct_id, word, modtype", $date);
			$res = $this->get('pj_ads_click', ["ac_pa_id as pa_id, ac_ads_id as ads_id, JSON_UNQUOTE(ac_pcts->'$.pid') AS pct_id, ac_word as word, ac_modtype modtype, COUNT(*) AS ard_click"], $where);
			foreach($res['data'] as $vs)
			{
				$clickArr[$vs['pa_id'].'@'.$vs['ads_id'].'@'.$vs['pct_id'].'@'.$vs['word']] += $vs['ard_click'];
			}
			
			//广告
			$anyAddArr = [];
			$where = sprintf(" and as_date=%d AND as_ads_id > 0 GROUP BY as_pa_id, as_ads_id, pct_id, as_word, modtype", $date);
			$res = $this->get('pj_ads_show', ["as_pa_id as pa_id, as_ads_id as ads_id, json_unquote(as_pcts->'$.pid') as pct_id, as_word as word, as_modtype modtype, count(*) as ard_show, count(distinct as_view_uid) as ard_user, count(distinct as_ip) as ard_ip"], $where);
			foreach($res['data'] as $vs)
			{
				$anyAddArr[] = [
					'ard_date'=>$date,	
					'ard_ads_id'=>$vs['ads_id'],
					'ard_pa_id'=>$vs['pa_id'],
					'ard_pct_id'=>$vs['pct_id'],
					'ard_word'=>$vs['word'],
					'ard_modtype'=>$vs['modtype'],
					'ard_show'=>$vs['ard_show'],
					'ard_click'=>(int) $clickArr[$vs['pa_id'].'@'.$vs['ads_id'].'@'.$vs['pct_id'].'@'.$vs['word']],
					'ard_user'=>$vs['ard_user'],
					'ard_ip'=>$vs['ard_ip'],
				];
			}
			if(!empty($anyAddArr)) $this->addAny('pj_ads_report_date', array_keys($anyAddArr[0]), $anyAddArr);

			//只保留最近60天的历史记录
			$delDate = date('Ymd', strtotime($date)-3600*24*60);
			$this->del('pj_ads_show', sprintf(" and as_date <= %d", $delDate));
			$this->del('pj_ads_click', sprintf(" and ac_date <= %d", $delDate));
			$this->AdsDb->End();
		}

		//补安卓的开机广告统计数据
		//安卓苹果的开机启动比例
		$bdate = '20210623';
		$edate = '20230801';
		$PjltjBase = new PjltjBase();
		$res = $PjltjBase->get('tj_baidu_reportuser', ['br_date, br_sys, session_count kj_count'], sprintf(" and br_date >= %d and br_date <= %d", $bdate, $edate));
		$kjTjArr = [];
		foreach($res['data'] as $v)
		{
			$kjTjArr[$v['br_date']][$v['br_sys']] = $v['kj_count'];
			$kjTjArr[$v['br_date']]['total'] += $v['kj_count'];
		}

		$res = $this->get('pj_ads_report_date', ['ard_date, sum(ard_show) as org_count'], sprintf(" and ard_date >= %d and ard_date < %d and ard_pa_id = 83 and ara_and_ok = 0 and ard_temp_rate = 1.00 group by ard_date", $bdate, $edate));
		$orgTjArr = array_column($res['data'], 'org_count', 'ard_date');
		
		//开机广告
		$ares = $this->get('pj_ads_report_date', ['*'], sprintf(" and ard_date >= %d and ard_date < %d and ard_pa_id = 83 and ara_and_ok = 0 and ard_temp_rate = 1.00", $bdate, $edate));
		foreach($ares['data'] as $av)
		{
			if(empty($kjTjArr[$av['ard_date']]) || empty($orgTjArr[$av['ard_date']])) continue;
			$rate = sprintf("%.2f", $kjTjArr[$av['ard_date']]['total']/$orgTjArr[$av['ard_date']] - 0.2);
			if($rate < 1.05) continue;
			$setArr = [
				'ard_show'=>round($av['ard_show']*$rate),
				'ard_click'=>round($av['ard_click']*$rate),
				'ard_user'=>round($av['ard_user']*$rate),
				'ard_ip'=>round($av['ard_ip']*$rate),
				'ard_temp_rate'=>$rate,
				'ara_and_ok'=>1,
				'ara_orgdata'=>json_encode($av, JSON_UNESCAPED_UNICODE)
			];
			$swhere = sprintf(" and ard_date='%s' and ard_ads_id='%s' and ard_pa_id='%s' and ard_pct_id='%s' and ard_word='%s'", $av['ard_date'], $av['ard_ads_id'], $av['ard_pa_id'], $av['ard_pct_id'], $av['ard_word']);
			$this->set('pj_ads_report_date', $setArr, $swhere);
		}
		return LibFc::ReturnData(true);
    }

	
	/**
	 * @method 获取广告可售的情况
	 * @param $Params[
			  sby string  [选填] 展示模式 默认place  place:按广告位展示  group:按省|品牌展示
	          status string [选填] 状态 默认all  all:全部  wsw:未售完  zjks:最近可售  zjdq:最近到期  sw:售罄
			  ]
	 * @author soul 2021/4/6
	 * @return
	 	sby = place 按广告位展示
		{"status":true,"data":[{"pa_id":"83","pa_name":"开机广告","pa_is_area":"1","pa_is_keyword":"0","pa_keyword_type":"","pa_max":"3","adsuse":[{"id":"1","name":"北京","has":2,"can":1,"cantime":"2021-04-24","candate":0},{"id":"20","name":"广西省","has":3,"can":0,"cantime":"2021-06-06","candate":42},{"id":"21","name":"江西省","has":2,"can":1,"cantime":"2021-04-24","candate":0},{"id":"22","name":"四川省","has":2,"can":1,"cantime":"2021-04-24","candate":0},{"id":"23","name":"海南省","has":1,"can":2,"cantime":"2021-04-24","candate":0},{"id":"24","name":"贵州省","has":3,"can":0,"cantime":"2021-06-06","candate":42},{"id":"25","name":"云南省","has":2,"can":1,"cantime":"2021-04-24","candate":0},{"id":"26","name":"西藏","has":1,"can":2,"cantime":"2021-04-24","candate":0},{"id":"27","name":"陕西省","has":2,"can":1,"cantime":"2021-04-24","candate":0},{"id":"28","name":"甘肃省","has":3,"can":0,"cantime":"2021-06-06","candate":42},{"id":"29","name":"青海省","has":1,"can":2,"cantime":"2021-04-24","candate":0},{"id":"30","name":"宁夏省","has":1,"can":2,"cantime":"2021-04-24","candate":0},{"id":"31","name":"新疆","has":3,"can":0,"cantime":"2021-05-12","candate":17},{"id":"32","name":"台湾","has":1,"can":2,"cantime":"2021-04-24","candate":0},{"id":"42","name":"香港","has":1,"can":2,"cantime":"2021-04-24","candate":0},{"id":"43","name":"澳门","has":1,"can":2,"cantime":"2021-04-24","candate":0},{"id":"84","name":"钓鱼岛","has":1,"can":2,"cantime":"2021-04-24","candate":0}]},{"pa_id":"84","pa_name":"VIN解码","pa_is_area":"0","pa_is_keyword":"1","pa_keyword_type":"brand","pa_max":"1","adsuse":[{"id":"6","name":"奥迪","has":1,"can":0,"cantime":"2021-12-20","candate":239},{"id":"49","name":"哈弗","has":0,"can":"1","cantime":"2021-04-24","candate":0},{"id":"66","name":"哈飞","has":1,"can":0,"cantime":"2021-07-20","candate":86},{"id":"82","name":"海格","has":0,"can":"1","cantime":"2021-04-24","candate":0},{"id":"75","name":"黄海","has":0,"can":"1","cantime":"2021-04-24","candate":0},{"id":"48","name":"海马","has":1,"can":0,"cantime":"2021-05-20","candate":25},{"id":"139","name":"悍马","has":1,"can":0,"cantime":"2021-07-08","candate":74},{"id":"84","name":"众泰","has":0,"can":"1","cantime":"2021-04-24","candate":0},{"id":"83","name":"中兴","has":0,"can":"1","cantime":"2021-04-24","candate":0}]}],"code":"","encry":0,"runtime":64}

		sby=group 按省|品牌展示
		{"status":true,"data":{"province":[{"id":"1","name":"北京","plas":[{"id":83,"name":"开机广告","has":2,"can":1,"cantime":"2021-04-24"},{"id":85,"name":"首页轮播","has":1,"can":2,"cantime":"2021-04-24"}]},{"id":"84","name":"钓鱼岛","plas":[{"id":83,"name":"开机广告","has":1,"can":2,"cantime":"2021-04-24"},{"id":85,"name":"首页轮播","has":1,"can":2,"cantime":"2021-04-24"}]}],"brand":[{"id":"6","name":"奥迪","plas":[{"id":84,"name":"VIN解码","has":1,"can":0,"cantime":"2021-12-20"},{"id":86,"name":"车型查询","has":1,"can":0,"cantime":"2021-07-28"}]},{"id":"83","name":"中兴","plas":[{"id":84,"name":"VIN解码","has":0,"can":"1","cantime":"2021-04-24"},{"id":86,"name":"车型查询","has":0,"can":"1","cantime":"2021-04-24"}]}]},"code":"","encry":0,"runtime":54}

		"has":已售广告数,"can":可售广告数,"cantime":"可售广告日期","candate":离最近可售广告天数
	 */
	public function getAdsPlaces($Params)
	{
		$Params['status'] = empty($Params['status'])? 'all': $Params['status'];

		$res = $this->get('yp_province_city', ['pc_id as id, pc_name as name'], ' AND pc_pid = 0 ORDER BY pc_seq, pc_id');
		$provinceArr = array_column($res['data'], null, 'id');
		
		$Ypc2Base = new Ypc2Base();
		$res =$Ypc2Base->Get('bc_brand', ['brand_id as id, brand_name as name'], ' order by brand_initial');
		$brandArr = array_column($res['data'],  null, 'id');
		
		$worldArr = [['id'=>0, 'name'=>'独家']];

		//按省或者品牌合并规整
		$groupArr = ['province'=>$provinceArr, 'brand'=>$brandArr, 'world'=>$worldArr];
		
		$pres = $this->get('pj_place', ['pa_id, pa_name, pa_is_area, pa_is_keyword, pa_keyword_type, pa_max'], ' AND pa_status = 1 ORDER BY pa_seq');
		$placeArr = [];
		$now = time();
		//先计算每个广告位的适用情况
		foreach($pres['data'] as $v)
		{
			if($v['pa_is_area'] && $v['pa_is_keyword']) continue;
			$v['adsuse'] = [];
			$v['group'] = $v['pa_keyword_type'];
			if($v['pa_is_area'])
			{
				$v['group'] = 'province';
				foreach($provinceArr as $pk=>$pv)
				{
					$v['adsuse'][$pk] = array_merge($pv, ['has'=>0, 'can'=>$v['pa_max'], 'cantime'=>date('Y-m-d', $now), 'candate'=>0]);
				}
			}
			else if($v['pa_is_keyword'])
			{
				foreach($brandArr as $pk=>$pv)
				{
					$v['adsuse'][$pk] = array_merge($pv, ['has'=>0, 'can'=>$v['pa_max'], 'cantime'=>date('Y-m-d', $now), 'candate'=>0]);
				}
			}
			else
			{
				$v['group'] = 'world';
				$v['adsuse'][0] = array_merge($worldArr[0], ['has'=>0, 'can'=>$v['pa_max'], 'cantime'=>date('Y-m-d', $now), 'candate'=>0]);
			}

			//已有的
			$adsTime = [];
			$res = $this->get('pj_place_ads_time JOIN pj_place_ads ON pla_id = pat_pla_id', ['pat_keyword, pat_pc_id, pat_btime, pat_etime'], sprintf(" and pal_shoufei = 1 and pla_pa_id = %d and pat_etime >= %d order by pat_etime asc", $v['pa_id'], $now));
			foreach($res['data'] as $tvv)
			{
				if($tvv['pat_btime'] < $now) $tvv['pat_btime'] = $now;
				if($v['pa_is_area'])
				{
					$adsTime[$tvv['pat_pc_id']][] = ['btime'=>$tvv['pat_btime'], 'etime'=>$tvv['pat_etime']];
				}
				else if($v['pa_is_keyword'])
				{
					if($v['pa_keyword_type'] == 'brand')
						$adsTime[$tvv['pat_keyword']][] = ['btime'=>$tvv['pat_btime'], 'etime'=>$tvv['pat_etime']]; 
				}
				else
				{
					$adsTime[0][] = ['btime'=>$tvv['pat_btime'], 'etime'=>$tvv['pat_etime']];
				}
			}
			foreach($adsTime as $tkk=>$tvv)
			{
				$v['adsuse'][$tkk]['has'] = count($tvv);
				$v['adsuse'][$tkk]['can'] = $v['pa_max'] - count($tvv);
				//已全部售完
				if($v['adsuse'][$tkk]['has'] >= $v['pa_max'])
				{
					$v['adsuse'][$tkk]['can'] = 0;
					$endKey = $v['adsuse'][$tkk]['has'] - $v['pa_max'];
					$v['adsuse'][$tkk]['cantime'] = date('Y-m-d', $tvv[$endKey]['etime']+3600*24);
					$v['adsuse'][$tkk]['candate'] = (int) ceil(($tvv[$endKey]['etime'] - $now)/3600/24);
				}
			}
			$placeArr[$v['pa_id']] = $v;
		}

		foreach($groupArr as $style=>$gv)
		{
			foreach($gv as $gvid=>$gvval)
			{
				$gvval['plas'] = [];
				foreach($placeArr as $paid=>$pav)
				{
					if($pav['group'] == $style && !empty($pav['adsuse'][$gvid]))
						$gvval['plas'][] = [
							'id'=>$paid,
							'name'=>$pav['pa_name'],
							'has'=>$pav['adsuse'][$gvid]['has'],
							'can'=>$pav['adsuse'][$gvid]['can'],
							'cantime'=>$pav['adsuse'][$gvid]['cantime'],
							'candate'=> (int) ceil((strtotime($pav['adsuse'][$gvid]['cantime']) - $now)/3600/24)
						];
				}
				$gv[$gvid] = $gvval;
			}
			$groupArr[$style] = array_values($gv);
		}

		//根据状态过滤
		switch($Params['status'])
		{
			case 'wsw':
				if($Params['sby'] == 'group')
				{
					foreach($groupArr as $pk=>$pv)
					{
						foreach($pv as $pkk=>$pvv)
						{
							foreach($pvv['plas'] as $pkkk=>$pvvv)
							{
								if($pvvv['can'] > 0){}else{unset($pvv['plas'][$pkkk]);}
							}
							$pvv['plas'] = array_values($pvv['plas']);
							if(empty($pvv['plas'])) 
								unset($pv[$pkk]);
							else
								$pv[$pkk] = $pvv;
						}
						if(empty($pv)) 
							unset($groupArr[$pk]);
						else
							$groupArr[$pk] = array_values($pv);
					}
				}
				else
				{
					foreach($placeArr as $pk=>$pv)
					{
						foreach($pv['adsuse'] as $pkk=>$pvv)
						{
							if($pvv['can'] > 0){}else{unset($pv['adsuse'][$pkk]);}
						}
						$pv['adsuse'] = array_values($pv['adsuse']);
						if(empty($pv['adsuse'])) 
							unset($placeArr[$pk]);
						else
							$placeArr[$pk] = $pv;
					}
				}
			break;
			case 'zjks':
				if($Params['sby'] == 'group')
				{
					foreach($groupArr as $pk=>$pv)
					{
						foreach($pv as $pkk=>$pvv)
						{
							foreach($pvv['plas'] as $pkkk=>$pvvv)
							{
								if($pvvv['can'] > 0 || $pvvv['candate'] <= 30){}else{unset($pvv['plas'][$pkkk]);}
							}
							$pvv['plas'] = array_values($pvv['plas']);
							if(empty($pvv['plas'])) 
								unset($pv[$pkk]);
							else
								$pv[$pkk] = $pvv;
						}
						if(empty($pv)) 
							unset($groupArr[$pk]);
						else
							$groupArr[$pk] = array_values($pv);
					}
				}
				else
				{
					foreach($placeArr as $pk=>$pv)
					{
						foreach($pv['adsuse'] as $pkk=>$pvv)
						{
							if($pvv['can'] > 0 || $pvv['candate'] <= 30){}else{unset($pv['adsuse'][$pkk]);}
						}
						$pv['adsuse'] = array_values($pv['adsuse']);
						if(empty($pv['adsuse'])) 
							unset($placeArr[$pk]);
						else
							$placeArr[$pk] = $pv;
					}
				}
			break;
			case 'zjdq':
				if($Params['sby'] == 'group')
				{
					foreach($groupArr as $pk=>$pv)
					{
						foreach($pv as $pkk=>$pvv)
						{
							foreach($pvv['plas'] as $pkkk=>$pvvv)
							{
								if($pvvv['candate'] > 0 && $pvvv['candate'] <= 30){}else{unset($pvv['plas'][$pkkk]);}
							}
							$pvv['plas'] = array_values($pvv['plas']);
							if(empty($pvv['plas'])) 
								unset($pv[$pkk]);
							else
								$pv[$pkk] = $pvv;
						}
						if(empty($pv)) 
							unset($groupArr[$pk]);
						else
							$groupArr[$pk] = array_values($pv);
					}
				}
				else
				{
					foreach($placeArr as $pk=>$pv)
					{
						foreach($pv['adsuse'] as $pkk=>$pvv)
						{
							if($pvv['candate'] > 0 && $pvv['candate'] <= 30){}else{unset($pv['adsuse'][$pkk]);}
						}
						$pv['adsuse'] = array_values($pv['adsuse']);
						if(empty($pv['adsuse'])) 
							unset($placeArr[$pk]);
						else
							$placeArr[$pk] = $pv;
					}
				}
			break;
			case 'sw':
				if($Params['sby'] == 'group')
				{
					foreach($groupArr as $pk=>$pv)
					{
						foreach($pv as $pkk=>$pvv)
						{
							foreach($pvv['plas'] as $pkkk=>$pvvv)
							{
								if($pvvv['can'] < 1){}else{unset($pvv['plas'][$pkkk]);}
							}
							$pvv['plas'] = array_values($pvv['plas']);
							if(empty($pvv['plas'])) 
								unset($pv[$pkk]);
							else
								$pv[$pkk] = $pvv;
						}
						if(empty($pv)) 
							unset($groupArr[$pk]);
						else
							$groupArr[$pk] = array_values($pv);
					}
				}
				else
				{
					foreach($placeArr as $pk=>$pv)
					{
						foreach($pv['adsuse'] as $pkk=>$pvv)
						{
							if($pvv['can'] < 1){}else{unset($pv['adsuse'][$pkk]);}
						}
						$pv['adsuse'] = array_values($pv['adsuse']);
						if(empty($pv['adsuse'])) 
							unset($placeArr[$pk]);
						else
							$placeArr[$pk] = $pv;
					}
				}
			break;
		}
		

		//取消数字键
		$placeArr = array_values($placeArr);
		foreach($placeArr as $k=>$v)
		{
			$v['adsuse'] = array_values($v['adsuse']);
			$placeArr[$k] = $v;
		}
		if($Params['sby'] == 'group')
		{
			$arr = $groupArr;
		}
		else
		{
			$arr = $placeArr;
		}
		return LibFc::ReturnData(true, $arr);
	}

	
	/**
	  * @method IM推送广告位给特定用户任务 每天 10:00
	  * @author soul
	  * @copyright 2023/7/22
	  * @return	
	  */
	public function tsAdsPlaceToUser()
	{
		$reg = [
			'max'=>20,//同一个位置同省份或者品牌每天最多推送次数
			'mindate_place'=>30,//同一个位置同用户最近10天推送的过不再推送此位置
			'mindate_user'=>15,//同用户最近5天推送的过不再推送
		];

		$YpwBase = new YpwBase();
		$PjlBase = new PjlBase();
		$res = $PjlBase->get('ts_msg_tpl', ['*'], " and tm_type='pjlads' and tm_status = 1");
		$tsTplArr = array_column($res['data'], null, 'tm_dataid');
		
		//同用户最近3天推送的过不再推送
		$res = $PjlBase->get('ts_msg_history', ['distinct tmh_u_id'], sprintf(" and tmh_unikey like 'pjlads_%%' and tmh_status>=0 and tmh_atime>%d", strtotime('-'.$reg['mindate_user'].' day')));
		$ignorUid = array_column($res['data'], 'tmh_u_id');

		//同一个位置同用户最近7天推送的过不再推送此位置
		$res = $PjlBase->get('ts_msg_history', ['tmh_unikey, tmh_u_id'], sprintf(" and tmh_unikey like 'pjlads_%%' and tmh_status>=0 and tmh_atime>%d group by tmh_unikey, tmh_u_id", strtotime('-'.$reg['mindate_place'].' day')));
		$ignorUniKeyUid = [];
		foreach($res['data'] as $v)
		{
			$ignorUniKeyUid[$v['tmh_u_id']][] = $v['tmh_unikey'];
		}
		
		//需要推送的所有记录，单用户只推送一条
		$tsAllArr = [];
		
		//当天已推送的次数
		$res = $PjlBase->get('ts_msg_history', ["CONCAT(tmh_unikey, '_', JSON_UNQUOTE(tmh_para->'$.id')) maxkey, COUNT(*) total"], sprintf(" and tmh_unikey like 'pjlads_%%' and tmh_status>=0 and tmh_atime>=%d GROUP BY maxkey", strtotime(date('Y-m-d'))));
		$tsCountArr = array_column($res['data'], 'total', 'maxkey');

		$plres = $this->getAdsPlaces(['sby'=>'group', 'status'=>'all']);
		foreach($plres['data'] as $style=>$gvals)
		{
			//用属性ID作为键
			$gvals = array_column($gvals, null, 'id');
			//推送哪些品牌或者地区？
			$tsIdArr = [];
			if($style == 'province')
			{
				$res = $YpwBase->get('yp_user_act join yp_user on ua_uid = u_id and u_status = 1 JOIN yp_user_ext ON ui_uid =ua_uid', ['ui_province id', 'SUM(ua_pv) pv'], " and ui_province > 0 GROUP BY ui_province HAVING SUM(ua_pv) > 1000 ORDER BY SUM(ua_pv) DESC");
				$tsIdArr = array_column($res['data'], null, 'id');
			}
			else if($style == 'brand')
			{
				$res = $YpwBase->get('yp_user_act_brand join yp_user on uab_uid = u_id and u_status = 1', ['uab_brand_id id', 'SUM(uab_count) pv'], " and uab_brand_id > 0 GROUP BY uab_brand_id HAVING SUM(uab_count) > 100 ORDER BY SUM(uab_count) DESC");
				$tsIdArr = array_column($res['data'], null, 'id');
			}
			
			foreach($tsIdArr as $toid=>$toVal)
			{
				if(empty($gvals[$toid])) continue;
				$gval = $gvals[$toid];
				$remsgArr = [];

				$toUsers = [];
				if($style == 'province')
				{//此省份的活跃用户
					$remsgArr = ['#省份#'=>$gval['name']];
					$where = sprintf(" and ui_province = %d GROUP BY ui_uid HAVING SUM(ua_pv) > 10 ORDER BY SUM(ua_pv) DESC limit 1000", $toid);
					$res = $YpwBase->get('yp_user_act join yp_user on ua_uid = u_id and u_status = 1 JOIN yp_user_ext ON ui_uid =ua_uid', ['ui_uid u_id', 'SUM(ua_pv) pv'], $where);
					$toUsers = $res['data'];

				}
				else if($style == 'brand')
				{//此品牌的活跃用户
					$remsgArr = ['#品牌#'=>$gval['name']];
					$where = sprintf(" and uab_brand_id = %d GROUP BY uab_uid HAVING SUM(uab_count) > 10 ORDER BY SUM(uab_count) DESC limit 500", $toid);
					$res = $YpwBase->get('yp_user_act_brand join yp_user on uab_uid = u_id and u_status = 1', ['uab_uid u_id', 'SUM(uab_count) pv'], $where);
					$toUsers = $res['data'];
				}
				foreach($toUsers as $uval)
				{
					$uid = $uval['u_id'];
					//在忽略推送里或者已经在本次推送
					if(in_array($uid, $ignorUid) || $tsAllArr[$uid]) continue;
					shuffle($gval['plas']);//随机
					foreach($gval['plas'] as $pa)
					{
						//有推送模板，并且30天内有空位的
						$unikey = 'pjlads_'.$pa['id'];
						$maxkey = $unikey.'_'.$toid;
						if(empty($tsTplArr[$pa['id']]) || $pa['candate'] > 30 || $tsCountArr[$maxkey] >= $reg['max'])  continue;
						if(!empty($ignorUniKeyUid[$uid]) && in_array($unikey, $ignorUniKeyUid[$uid])) continue;
						$msg = strtr($tsTplArr[$pa['id']]['tm_tpl'], $remsgArr);
						$tsAllArr[$uid] = [
							'tmh_u_id'=>$uid,
							'tmh_tm_id'=>$tsTplArr[$pa['id']]['tm_id'],
							'tmh_unikey'=>$unikey,
							'tmh_para'=>json_encode(['id'=>$gval['id'],'name'=>$gval['name']], JSON_UNESCAPED_UNICODE),
							'tmh_msg'=>$msg,
							'tmh_atime'=>time(),
							'tmh_add_u_id'=>8
						];
						$tsCountArr[$maxkey]++;
					}
				}
			}
		}
		if(!empty($tsAllArr))
		{
			$tsAllArr = array_values($tsAllArr);
			$PjlBase->addAny('ts_msg_history', array_keys($tsAllArr[0]), $tsAllArr);
		}
		return LibFc::ReturnData(true);
	}

	/**
	  * @method 机器人推送给横向件用户
	  * @author soul
	  * @copyright 2023/7/22
	  * @return	
	  */
	public function tsRobotToUser()
	{
		$reg = [
			'max'=>100,//每天最多推送次数
			'mindate_user'=>15,//同用户最近5天推送的过不再推送
		];

		$YpwBase = new YpwBase();
		$PjlBase = new PjlBase();
		$tmtype = 'robot';
		$res = $PjlBase->get('ts_msg_tpl', ['*'], " and tm_type='{$tmtype}' and tm_status = 1", true);
		$tsTplArr = $res['data'];
		if(empty($tsTplArr)) return LibFc::ReturnData(false, '机器人推送模板不存在！');
		
		//同用户最近3天推送的过不再推送
		$res = $PjlBase->get('ts_msg_history', ['distinct tmh_u_id'], sprintf(" and tmh_unikey='%s' and tmh_status>=0 and tmh_atime>%d", $tmtype, strtotime('-'.$reg['mindate_user'].' day')));
		$ignorUid = array_column($res['data'], 'tmh_u_id');

		//需要推送的所有记录，单用户只推送一条
		$tsAllArr = [];
		
		//当天已推送的次数
		$res = $PjlBase->get('ts_msg_history', ["COUNT(*) total"], sprintf(" and tmh_unikey='%s' and tmh_status>=0 and tmh_atime>=%d", $tmtype, strtotime(date('Y-m-d'))));
		$tsCountArr = $res['data']['total'];
		if($tsCountArr >= $reg['max']) return LibFc::ReturnData(true);
		
		$toUsers = [];
		$res = $YpwBase->get('yp_user_act_part join yp_user on uap_uid = u_id and u_status = 1', ['uap_uid u_id', 'SUM(uap_count) pv'], " and uap_count > 5 GROUP BY uap_uid HAVING COUNT(DISTINCT uap_part) <= 5 AND SUM(uap_count) > 100 ORDER BY SUM(uap_count) DESC");
		$toUsers = $res['data'];
		foreach($toUsers as $uval)
		{
			$uid = $uval['u_id'];
			//在忽略推送里或者已经在本次推送
			if(in_array($uid, $ignorUid) || $tsAllArr[$uid]) continue;
			$unikey = $tmtype;
			if($tsCountArr >= $reg['max'])  continue;
			$msg = $tsTplArr['tm_tpl'];
			$tsAllArr[$uid] = [
				'tmh_u_id'=>$uid,
				'tmh_tm_id'=>$tsTplArr['tm_id'],
				'tmh_unikey'=>$unikey,
				'tmh_para'=>'[]',
				'tmh_msg'=>$msg,
				'tmh_atime'=>time(),
				'tmh_add_u_id'=>8
			];
			$tsCountArr++;
		}
		if(!empty($tsAllArr))
		{
			$tsAllArr = array_values($tsAllArr);
			$PjlBase->addAny('ts_msg_history', array_keys($tsAllArr[0]), $tsAllArr);
		}
		return LibFc::ReturnData(true);
	}


	/**
	  * @method 入驻商家机器人
	  * @author soul
	  * @copyright 2023/7/22
	  * @return	
	  */
	public function tsPjlRobotToUser()
	{
		$reg = [
			'max'=>1000,//每天最多推送次数
			'mindate_user'=>15,//同用户最近N天推送的过不再推送
		];

		$YpwBase = new YpwBase();
		$PjlBase = new PjlBase();
		$tmtype = 'pjlrobot';
		$res = $PjlBase->get('ts_msg_tpl', ['*'], " and tm_type='{$tmtype}' and tm_status = 1", true);
		$tsTplArr = $res['data'];
		if(empty($tsTplArr)) return LibFc::ReturnData(false, '推送模板不存在！');
		
		//同用户最近3天推送的过不再推送
		$res = $PjlBase->get('ts_msg_history', ['distinct tmh_u_id'], sprintf(" and tmh_unikey='%s' and tmh_status>=0 and tmh_atime>%d", $tmtype, strtotime('-'.$reg['mindate_user'].' day')));
		$ignorUid = array_column($res['data'], 'tmh_u_id');

		//需要推送的所有记录，单用户只推送一条
		$tsAllArr = [];
		
		//当天已推送的次数
		$res = $PjlBase->get('ts_msg_history', ["COUNT(*) total"], sprintf(" and tmh_unikey='%s' and tmh_status>=0 and tmh_atime>=%d", $tmtype, strtotime(date('Y-m-d'))));
		$tsCountArr = $res['data']['total'];
		if($tsCountArr >= $reg['max']) return LibFc::ReturnData(true);
		
		$toUsers = [];
		$res = $PjlBase->get('pl_shop', ['shop_pu_id u_id'], " ORDER BY shop_apply_time DESC");
		$toUsers = $res['data'];
		foreach($toUsers as $uval)
		{
			$uid = $uval['u_id'];
			//在忽略推送里或者已经在本次推送
			if(in_array($uid, $ignorUid) || $tsAllArr[$uid]) continue;
			$unikey = $tmtype;
			if($tsCountArr >= $reg['max'])  continue;
			$msg = $tsTplArr['tm_tpl'];
			$tsAllArr[$uid] = [
				'tmh_u_id'=>$uid,
				'tmh_tm_id'=>$tsTplArr['tm_id'],
				'tmh_unikey'=>$unikey,
				'tmh_para'=>'[]',
				'tmh_msg'=>$msg,
				'tmh_atime'=>time(),
				'tmh_add_u_id'=>8
			];
			$tsCountArr++;
		}
		if(!empty($tsAllArr))
		{
			$tsAllArr = array_values($tsAllArr);
			$PjlBase->addAny('ts_msg_history', array_keys($tsAllArr[0]), $tsAllArr);
		}
		return LibFc::ReturnData(true);
	}


	/**
	  * @method 发送推送任务
	  * @param  $tmh_id 任务ID，为空则推送所有待推送的
	  * @return ["status"=> bool(true), "data"=> true, "code"=>""]
	  */
	public function runTsTask($tmh_id = [])
	{
		$PjlBase = new PjlBase();
		$OthIm = new OthIm();
		$where = " and u_status = 1 and tmh_status = 0 and u_im_init='ok'";
		if(!empty($tmh_id)) $where .= sprintf(" and tmh_id in (%s)", implode(',', $tmh_id));
		$taskres = $PjlBase->get('ts_msg_history a join ypdb_write.yp_user b on a.tmh_u_id=b.u_id', ['a.*', 'u_register_time'], $where.' order by rand()');
		$count = 0;
		foreach($taskres['data'] as $v)
		{
			$msgArr = json_decode($v['tmh_msg'], true);
			switch($msgArr['msg_type'])
			{
				case 'text':
					$OthIm->sendImMsg($msgArr['fromuser'], $v['tmh_u_id'], $msgArr['des']);
					$PjlBase->set('ts_msg_history', ['tmh_status'=>1, 'tmh_send'=>time()], ' and tmh_id='.$v['tmh_id']);
					$count++;
				break;
				case 'normal':
					$msgArr['clickval'] = 'tmh_id:'.$v['tmh_id'];
					$OthIm->sendImCustomMsg(100, $v['tmh_u_id'], $msgArr);
					$PjlBase->set('ts_msg_history', ['tmh_status'=>1, 'tmh_send'=>time()], ' and tmh_id='.$v['tmh_id']);
					$count++;
				break;
			}
		}
		return LibFc::ReturnData(true, $count);
	}
}