<?php
/**
* Created by PhpStorm.
* User: buhuan
* Date: 14-4-18
* Time: 上午10:14
* 统计进程
* 广告留存
* 公式
* flag_id = channel_id*10000 + domain_id
*/
namespace Application\Process;
use Application\Core\ProcessBase;
use Application\Interfaces\IProcess;
use Application\Core\Pusher;
use \Ev;
use \EvTimer;
class Statistics extends ProcessBase implements IProcess{
/**
* 子进程数目序号 => 执行的函数
*/
private $procs = array(
0 => 'userOnline_daemon_handler',
1 => 'Retention_daemon_handler',
2 => 'AdRetention_daemon_handler'
);
/**
* 用户在线每5分钟执行一次 并且是在5的整数倍分钟时间点执行
*/
const USER_ONLINE_DIFF = 5;//分钟
/**
* 留存率每60分钟执行一次
*/
const RETENTION_DIFF = 60;//分钟
/**
* 广告留存率每60分钟执行一次
*/
const AD_RETENTION_DIFF = 60;//分钟
/**
* 在凌晨0点更新前N天的留存并写入前一天的留存
*/
const RETENTION_PUSH_HOUR = 0;//整点
/**
* 在凌晨0点更新前N天的广告留存并写入前一天的广告留存
*/
const AD_RETENTION_PUSH_HOUR = 1;//整点
private $pids = array();
private $excutable = NULL;
function onPreInit()
{
parent::onPreInit(); // TODO: Change the autogenerated stub
foreach($this->procs as $function_name){
$this->excutable[$function_name] = TRUE;
}
}
/**
* 开始执行命令。
*/
function execute()
{
// TODO: Implement execute() method.
//创建子进程
$num = count($this->procs);
$pid = pcntl_fork();
for($i =0 ; $i < $num ; $i++){
if($pid == -1){
print "不能创建子进程".chr(10);
}else if($pid){
//父进程
print '子进程号:'.$pid.chr(10);
$this->pids[$i] = $pid;//存入子进程序号和进程号
$pid = pcntl_fork();
}else{
if($this->excutable[$this->procs[$i]])
$this->daemon_dispatch($i);
}
}
}
/**
* @param $i 进程序号
*/
private function daemon_dispatch($i){
switch($i){
case 0: $timediff = self::USER_ONLINE_DIFF * 60;
//确认分钟数为5的整数倍的时候开始执行
$minutes = intval(date('i'));
$countdown = (5 - $minutes%5)*60;//倒计时开始毫秒数
break;
case 1: //Retention
$timediff = self::RETENTION_DIFF * 60;
$countdown = 1;
break;
case 2: //ADRETENTION
$timediff = self::AD_RETENTION_DIFF * 60;
$countdown = 10;
break;
}
print '启动进程['.$this->pids[$i].']'.$this->procs[$i].chr(10);
$timer = new EvTimer($countdown,$timediff,array($this,$this->procs[$i]));
$this->excutable[$this->procs[$i]] = FALSE;
Ev::run();
}
/**
* 释放资源。
*/
function dispose()
{
// TODO: Implement dispose() method.
}
function userOnline_daemon_handler(){
print "执行用户在线...".chr(10);
$statistics_date = strtotime(date('Y-m-d'));
$statistics_time = time();
//查找当前在线的玩家数量
$sql = "SELECT count(user_id) as num FROM user_online";
$d = $this->scope->db->fetch($sql);
$num = $d['num'];
$data = array(
'statistics_date' => $statistics_date,
'statistics_time' => $statistics_time,
'online' => $num
);
Pusher::instance($this->scope)->doPushStat(107,$data);
}
function Retention_daemon_handler(){
$hour = intval(date('H'));
print "执行留存率...".chr(10);
if($hour == SELF::RETENTION_PUSH_HOUR){//如果是$push_hour点 则开始发送数据
$time = time();
$current_time = $time-24*60*60;//计算的基准时间
$start_datetime = date('Y-m-d',$current_time).' 00:00:00';
$end_datetime = date('Y-m-d',$current_time).' 23:59:59';
//查询当前前一天的注册人数
$sql = "SELECT COUNT(user_id) as reg_num FROM user_info_ext WHERE user_active_time > '$start_datetime' AND user_active_time < '$end_datetime'";
$d = $this->scope->db->fetch($sql);
$reg_num = $d['reg_num'];
//查询当前前一天登录的用户
$sql = "SELECT user_id FROM user_info_ext WHERE user_last_login_time > '$start_datetime' AND user_last_login_time < '$end_datetime' ";
$d = $this->scope->db->fetchAll($sql);
$login_users = $d;
$reg_nums = array();
$reg_nums['day1'] = $this->nDayUsers(1,$current_time);
$reg_nums['day3'] = $this->nDayUsers(3,$current_time);
$reg_nums['day7'] = $this->nDayUsers(7,$current_time);
$reg_nums['day15'] = $this->nDayUsers(15,$current_time);
$reg_nums['day30'] = $this->nDayUsers(30,$current_time);
$data = array(
'currentTime' => $time,
'reg_num' => $reg_num,
'reg_nums' => $reg_nums,
'login_users' => $login_users
);
Pusher::instance($this->scope)->doPushStat(106,$data);
}
}
function AdRetention_daemon_handler(){
$hour = intval(date('H'));
print "执行广告留存率...".chr(10);
if($hour == SELF::AD_RETENTION_PUSH_HOUR){//如果是$push_hour点 则开始发送数据
$time = time();
$current_time = $time-24*60*60;//计算的基准时间
$start_datetime = date('Y-m-d',$current_time).' 00:00:00';
$end_datetime = date('Y-m-d',$current_time).' 23:59:59';
//必须先知道哪些渠道ID 和 域名ID
//查询当前前一天的注册人数集合
$sql = "SELECT COUNT(user_id) as reg_num,flag_id FROM user_info_ext
WHERE flag_id <> 0 AND user_active_time > '$start_datetime' AND user_active_time < '$end_datetime' GROUP BY flag_id";
$d = $this->scope->db->fetchAll($sql);
$reg_num = $d;
//查询当前前一天登录的用户的不重复的flag_id
$sql = "SELECT DISTINCT(flag_id) as flag_id FROM user_info_ext WHERE user_last_login_time > '$start_datetime' AND user_last_login_time < '$end_datetime' AND flag_id <> 0";
$flags = $this->scope->db->fetchAll($sql);
//查询当前前一天登录的用户
$sql = "SELECT user_id,flag_id FROM user_info_ext WHERE user_last_login_time > '$start_datetime' AND user_last_login_time < '$end_datetime' AND flag_id <> 0";
$d = $this->scope->db->fetchAll($sql);
$login_users = array();
//按channel_id,domain_id重组用户数据
if(count($d) > 0 && count($flags) > 0){
foreach($flags as $flag){
foreach($d as $user){
if($flag['flag_id'] == $user['flag_id']){
$login_users[$flag['flag_id']][] = $user['user_id'];
}
}
}
}
unset($d);
$reg_nums = array();
$reg_nums['day1'] = $this->nDayUsers_Ad(1,$current_time);
$reg_nums['day2'] = $this->nDayUsers_Ad(2,$current_time);
$reg_nums['day3'] = $this->nDayUsers_Ad(3,$current_time);
$reg_nums['day4'] = $this->nDayUsers_Ad(4,$current_time);
$reg_nums['day5'] = $this->nDayUsers_Ad(5,$current_time);
$reg_nums['day6'] = $this->nDayUsers_Ad(6,$current_time);
$reg_nums['day7'] = $this->nDayUsers_Ad(7,$current_time);
$reg_nums['day8'] = $this->nDayUsers_Ad(8,$current_time);
$data = array(
'currentTime' => $time,
'reg_num' => $reg_num,
'reg_nums' => $reg_nums,
'login_users' => $login_users
);
Pusher::instance($this->scope)->doPushStat(108,$data);
}
}
/**
* @param int $n 1=1日 3=3日。。。
* @param int $current_time 基准时间
* @return array
*/
protected function nDayDate($n,$current_time){
$diff = 24*60*60;
$start_datetime = date('Y-m-d',$current_time-$n*$diff).' 00:00:00';
$end_datetime = date('Y-m-d',$current_time-$n*$diff).' 23:59:59';
$date = array();
$date[0] = $start_datetime;
$date[1] = $end_datetime;
return $date;
}
/**
* 得到基准日期前第n日的注册用户集合
* @param $n
* @param $current_time
* @return array
*/
protected function nDayUsers($n,$current_time){
$dates = $this->nDayDate($n,$current_time);
//查询次日注册用户
$sql = "SELECT user_id FROM user_info_ext WHERE user_active_time > '{$dates[0]}' AND user_active_time < '{$dates[1]}'";
return $this->scope->db->fetchAll($sql);
}
/**
* 得到基准日期前第n日的注册用户集合
* @param $n
* @param $current_time
* @return array
*/
function nDayUsers_Ad($n,$current_time){
$dates = $this->nDayDate($n,$current_time);
//查询当前前一天注册的用户的不重复的flag_id
$sql = "SELECT DISTINCT(flag_id) as flag_id FROM user_info_ext WHERE user_active_time > '{$dates[0]}' AND user_active_time < '{$dates[1]}' AND flag_id <> 0";
$flags = $this->scope->db->fetchAll($sql);
//查询$n日注册用户
$sql = "SELECT user_id,flag_id FROM user_info_ext WHERE user_active_time > '{$dates[0]}' AND user_active_time < '{$dates[1]}' AND flag_id <> 0";
$d = $this->scope->db->fetchAll($sql);
$reg_users = array();
if(count($d) > 0 && count($flags) > 0){
foreach($flags as $flag){
foreach($d as $user){
if($flag['flag_id'] == $user['flag_id']){
$reg_users[$flag['flag_id']][] = $user['user_id'];
}
}
}
}
usleep(100000);
return $reg_users;
}
}