Retain.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. <?php
  2. declare (strict_types=1);
  3. namespace app\store\controller\analysis;
  4. use app\common\model\analysis\AnalysisDailyRetainTag;
  5. use app\store\controller\Controller;
  6. use app\store\model\analysis\AnalysisDailyRetain as AnalysisDailyRetainModel;
  7. use app\store\model\analysis\AnalysisWeeklyRetain as AnalysisWeeklyRetainModel;
  8. use app\store\model\analysis\AnalysisMonthlyRetain as AnalysisMonthlyRetainModel;
  9. use app\common\service\Export as ExportService;
  10. /**
  11. * 留存分析
  12. *
  13. * Class Retain
  14. * @package app\store\controller\analysis
  15. */
  16. class Retain extends Controller
  17. {
  18. public function exportRetainData(){
  19. $params = $this->request->param();
  20. $type = $params['type'] ?? 2; // 时间筛选类型 1-最近7天 2-最近30天 3-自定义
  21. $start = (int)($params['start'] ?? '');
  22. $end = (int)($params['end'] ?? '');
  23. $retain_type = $params['retain_type'] ?? 1; // 类型 1-新增用户留存 2-活跃用户留存
  24. $grain_size = $params['grain_size'] ?? 1; // 粒度 1-日粒度 2-周粒度 3-月粒度
  25. if ($type == 3 && (!$start || !$end)) {
  26. return $this->renderError('自定义区间时间不能为空');
  27. }
  28. // 根据时间筛选类型计算筛选开始结束时间
  29. if ($type == 1) { //最近7天
  30. $start = strtotime('-7 days');
  31. $end = strtotime('-1 days');
  32. } elseif ($type == 2) {//最近30天
  33. $start = strtotime('-30 days');
  34. $end = strtotime('-1 days');
  35. }
  36. $table = $this->getChartTables($start, $end, $retain_type, $grain_size);
  37. $data = [];
  38. $data['header'] = ['序号', '时间', '新增用户数','1天后','2天后','3天后','4天后','5天后','6天后','7天后','14天后','30天后'];
  39. $data['filename'] = '用户留存分析';
  40. $data['data'] = [];
  41. foreach ($table as $k=>$arr){
  42. $new_list['key'] = $k+1;
  43. $new_list['ref_date'] = $arr['ref_date'];
  44. $new_list['retain_0'] = $arr['retain_0'];
  45. $new_list['retain_1'] = $arr['retain_1'];
  46. $new_list['retain_2'] = $arr['retain_2'];
  47. $new_list['retain_3'] = $arr['retain_3'];
  48. $new_list['retain_4'] = $arr['retain_4'];
  49. $new_list['retain_5'] = $arr['retain_5'];
  50. $new_list['retain_6'] = $arr['retain_6'];
  51. $new_list['retain_7'] = $arr['retain_7'];
  52. $new_list['retain_14'] = $arr['retain_14'];
  53. $new_list['retain_30'] = $arr['retain_30'];
  54. $data['data'][] = $new_list;
  55. }
  56. $res = ExportService::export($data['data'],$data['header'],$data['filename'],'列表','Xls');
  57. return $this->renderSuccess($res,'导出成功');
  58. }
  59. /**
  60. * 留存数据
  61. *
  62. * @return array
  63. */
  64. public function retainData()
  65. {
  66. $params = $this->request->param();
  67. $type = $params['type'] ?? 2; // 时间筛选类型 1-最近7天 2-最近30天 3-自定义
  68. $start = (int)($params['start'] ?? '');
  69. $end = (int)($params['end'] ?? '');
  70. $retain_type = $params['retain_type'] ?? 1; // 类型 1-新增用户留存 2-活跃用户留存
  71. $grain_size = $params['grain_size'] ?? 1; // 粒度 1-日粒度 2-周粒度 3-月粒度
  72. if ($type == 3 && (!$start || !$end)) {
  73. return $this->renderError('自定义区间时间不能为空');
  74. }
  75. // 根据时间筛选类型计算筛选开始结束时间
  76. if ($type == 1) { //最近7天
  77. $start = strtotime('-7 days');
  78. $end = strtotime('-1 days');
  79. } elseif ($type == 2) {//最近30天
  80. $start = strtotime('-30 days');
  81. $end = strtotime('-1 days');
  82. }
  83. $table = $this->getChartTables($start, $end, $retain_type, $grain_size);
  84. $data = [
  85. 'start' => date('Y-m-d', $start),
  86. 'end' => date('Y-m-d', $end),
  87. 'table' => $table
  88. ];
  89. return $this->renderSuccess($data);
  90. }
  91. private function getChartTables($start, $end, $retain_type, $grain_size)
  92. {
  93. $start = strtotime(date('Y-m-d', $start));
  94. $end = strtotime(date('Y-m-d', $end));
  95. if ($retain_type == AnalysisDailyRetainTag::TYPE_NEW) {
  96. $with = 'retainTagNew';
  97. } elseif($retain_type == AnalysisDailyRetainTag::TYPE_ACTIVE) {
  98. $with = 'retainTagActive';
  99. } else {
  100. return $this->renderError('留存类型参数无效');
  101. }
  102. $retainDays = self::getRetainDays($grain_size);
  103. if ($grain_size == 1) { // 日粒度
  104. $retainModel = new AnalysisDailyRetainModel();
  105. } elseif ($grain_size == 2) { // 周粒度
  106. $retainModel = new AnalysisWeeklyRetainModel();
  107. // 判断开始时间是否不满一周
  108. if (date("w",$start) != 1) {
  109. $start = strtotime('-1 monday', $start);
  110. }
  111. } elseif ($grain_size == 3) { // 月粒度
  112. $retainModel = new AnalysisMonthlyRetainModel();
  113. // 判断开始时间是否不满一月
  114. if (date("Y-m",$start) == date("Y-m")) {
  115. $start = strtotime(last_month('Y-m', 2, $start));
  116. } else {
  117. $start = strtotime(last_month('Y-m', 1, $start));
  118. }
  119. }
  120. $list = $retainModel::where('start_time', '>=', $start)
  121. ->where('end_time', '<', $end+ 86400)
  122. ->field('id,ref_date')
  123. ->with($with)
  124. ->order('ref_date', 'desc')
  125. ->paginate()
  126. ->each(function ($v) use ($with, $retainDays) {
  127. $tags = [];
  128. foreach ($v[$with] as $vv) {
  129. $tags[$vv['key']] = $vv['value'];
  130. }
  131. foreach ($retainDays as $kk=>$rd) {
  132. $retain_first = $tags[0]; // 当日
  133. if (isset($tags[$rd])) {
  134. if ($rd > 0) {
  135. if ($tags[$rd] > 0 && $retain_first > 0) {
  136. $v["retain_{$rd}"] = round(($tags[$rd] / $retain_first)*100, 2);
  137. } else {
  138. $v["retain_{$rd}"] = 0;
  139. }
  140. } else {
  141. $v["retain_{$rd}"] = $tags[$rd];
  142. }
  143. } else {
  144. $v["retain_{$rd}"] = '';
  145. }
  146. }
  147. unset($v['id'],$v[$with]);
  148. });
  149. return $list;
  150. }
  151. private static function getRetainDays($grain_size)
  152. {
  153. if ($grain_size == 1) {
  154. return [0,1,2,3,4,5,6,7,14,30];
  155. } elseif ($grain_size == 2) {
  156. return [0,1,2,3,4];
  157. } elseif ($grain_size == 3) {
  158. return [0,1];
  159. } else {
  160. return [];
  161. }
  162. }
  163. }