ComplexSummaryGameRetention.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. <?php
  2. namespace app\admin\controller;
  3. use think\Db;
  4. use app\common\model\ComplexChannelModel;
  5. use app\common\library\MakeReportGo;
  6. /**
  7. * 聚合-游戏留存报表控制器
  8. */
  9. class ComplexSummaryGameRetention extends Admin
  10. {
  11. /**
  12. * 游戏留存报表列表
  13. */
  14. public function index()
  15. {
  16. $tmpGameList = model('Common/Game')->getAllByCondition('name,id', ['cooperation_status' => 1], "id desc", "all");
  17. $gameList = [];
  18. foreach ($tmpGameList as $game) {
  19. $gameList[$game['id']] = $game;
  20. }
  21. $channelList = (new ComplexChannelModel())->getAllByCondition('id,name', ['flag' => 4], 'name asc');
  22. $channelByIdList = [];
  23. foreach ($channelList as $channel) {
  24. $channelByIdList[$channel['id']] = $channel;
  25. }
  26. // 设置默认时间范围(昨天)
  27. $defaultDate = date('Y-m-d', strtotime('-1 day'));
  28. $defaultStart = $defaultDate;
  29. $defaultEnd = $defaultDate;
  30. $this->assign('gameArr', $tmpGameList);
  31. $this->assign('game_list', $gameList);
  32. $this->assign('channel_list', $channelByIdList);
  33. $this->assign('default_start', $defaultStart);
  34. $this->assign('default_end', $defaultEnd);
  35. list($list, $total) = $this->getSummaryList(request()->param());
  36. // 参考Complex.php的方式处理分页数据
  37. // 确保 $list 是分页对象,如果不是则获取总数
  38. $listTotal = is_object($list) && method_exists($list, 'total') ? $list->total() : (is_array($list) ? count($list) : 0);
  39. $listData = is_object($list) && method_exists($list, 'toArray') ? $list->toArray()['data'] : (is_array($list) ? $list : []);
  40. $pageHtml = is_object($list) && method_exists($list, 'render') ? $list->render() : '';
  41. $this->assign('total', $total);
  42. $this->assign('list', $listData);
  43. $this->assign('listTotal', $listTotal); // 传递总数,供视图使用
  44. $this->assign('page', $pageHtml);
  45. return $this->fetch();
  46. }
  47. /**
  48. * 获取汇总列表数据
  49. */
  50. private function getSummaryList($data)
  51. {
  52. $where = [];
  53. $isDateRange = false;
  54. $startDate = '';
  55. $endDate = '';
  56. // 游戏筛选(多选)
  57. if (isset($data['game_id']) && $data['game_id']) {
  58. $gameIds = is_array($data['game_id']) ? $data['game_id'] : explode(',', $data['game_id']);
  59. $where['game_id'] = ['in', $gameIds];
  60. }
  61. // 渠道筛选
  62. if (isset($data['complex_id']) && $data['complex_id'] != '') {
  63. $where['complex_id'] = $data['complex_id'];
  64. }
  65. // 时间筛选
  66. if (isset($data['start']) && isset($data['end']) && $data['start'] != '' && $data['end'] != '') {
  67. $startDate = $data['start'];
  68. $endDate = $data['end'];
  69. // 计算日期差
  70. $startTimestamp = strtotime($startDate);
  71. $endTimestamp = strtotime($endDate);
  72. $daysDiff = ($endTimestamp - $startTimestamp) / 86400;
  73. // 如果日期范围大于1天,需要汇总
  74. if ($daysDiff >= 1) {
  75. $isDateRange = true;
  76. }
  77. $where['day'] = [
  78. ['>=', $data['start']],
  79. ['<=', $data['end']],
  80. ];
  81. } elseif (isset($data['start']) && $data['start'] != '') {
  82. $where['day'] = ['>=', $data['start']];
  83. } elseif (isset($data['end']) && $data['end'] != '') {
  84. $where['day'] = ['<=', $data['end']];
  85. } else {
  86. // 默认昨天
  87. $defaultDate = date('Y-m-d', strtotime('-1 day'));
  88. $startDate = $defaultDate;
  89. $endDate = $defaultDate;
  90. $where['day'] = [
  91. ['>=', $defaultDate],
  92. ['<=', $defaultDate],
  93. ];
  94. if (!isset($data['start'])) {
  95. $data['start'] = $defaultDate;
  96. }
  97. if (!isset($data['end'])) {
  98. $data['end'] = $defaultDate;
  99. }
  100. }
  101. $param = input('get.');
  102. // 如果日期范围大于1天,按游戏ID和渠道ID汇总
  103. if ($isDateRange) {
  104. // 先获取所有汇总数据
  105. $allList = Db::table('cy_complex_summary_game_retention')
  106. ->where($where)
  107. ->fieldRaw("
  108. game_id,
  109. complex_id,
  110. sum(reg_num) as reg_num,
  111. ROUND(CASE WHEN sum(reg_num) = 0 THEN 0 ELSE sum(one_stay * reg_num) / sum(reg_num) END, 2) as one_stay,
  112. ROUND(CASE WHEN sum(reg_num) = 0 THEN 0 ELSE sum(three_stay * reg_num) / sum(reg_num) END, 2) as three_stay,
  113. ROUND(CASE WHEN sum(reg_num) = 0 THEN 0 ELSE sum(four_stay * reg_num) / sum(reg_num) END, 2) as four_stay,
  114. ROUND(CASE WHEN sum(reg_num) = 0 THEN 0 ELSE sum(five_stay * reg_num) / sum(reg_num) END, 2) as five_stay,
  115. ROUND(CASE WHEN sum(reg_num) = 0 THEN 0 ELSE sum(six_stay * reg_num) / sum(reg_num) END, 2) as six_stay,
  116. ROUND(CASE WHEN sum(reg_num) = 0 THEN 0 ELSE sum(seven_stay * reg_num) / sum(reg_num) END, 2) as seven_stay,
  117. ROUND(CASE WHEN sum(reg_num) = 0 THEN 0 ELSE sum(fifteen_stay * reg_num) / sum(reg_num) END, 2) as fifteen_stay,
  118. ROUND(CASE WHEN sum(reg_num) = 0 THEN 0 ELSE sum(thirty_stay * reg_num) / sum(reg_num) END, 2) as thirty_stay
  119. ")
  120. ->group('game_id')
  121. ->order('game_id desc desc')
  122. ->select();
  123. // 转换为数组(统一处理,兼容数组和Collection对象)
  124. $allList = $allList ?: [];
  125. if (!is_array($allList) && method_exists($allList, 'toArray')) {
  126. $allList = $allList->toArray();
  127. }
  128. // 处理汇总数据,设置日期范围
  129. foreach ($allList as &$item) {
  130. $item['day'] = $startDate . ' ~ ' . $endDate;
  131. }
  132. unset($item);
  133. // 手动分页处理
  134. $page = intval(input('get.page', 1));
  135. $page = $page > 0 ? $page : 1;
  136. $limit = 20;
  137. $totalCount = count($allList);
  138. $offset = ($page - 1) * $limit;
  139. $listData = array_slice($allList, $offset, $limit);
  140. // 创建分页对象,参考Complex.php的方式
  141. $collection = new \think\Collection($listData);
  142. $list = \think\paginator\driver\Bootstrap::make($collection, $limit, $page, $totalCount, false, [
  143. 'var_page' => 'page',
  144. 'path' => request()->url(),
  145. 'query' => $param,
  146. ]);
  147. } else {
  148. // 查询列表(原有逻辑)
  149. $list = Db::table('cy_complex_summary_game_retention')
  150. ->where($where)
  151. ->group('game_id')
  152. ->order('day desc, game_id desc')
  153. ->paginate(20, false, ['query' => $param]);
  154. }
  155. // 计算汇总
  156. $total = Db::table('cy_complex_summary_game_retention')
  157. ->where($where)
  158. ->field('count(*) as total, sum(reg_num) as reg_total')
  159. ->find();
  160. return [$list, $total ?: ['total' => 0, 'reg_total' => 0]];
  161. }
  162. /**
  163. * 导出数据
  164. */
  165. public function export()
  166. {
  167. if (request()->isPost()) {
  168. $data = input('post.');
  169. $where = [];
  170. $isDateRange = false;
  171. $startDate = '';
  172. $endDate = '';
  173. // 游戏筛选(多选)
  174. if (isset($data['game_id']) && $data['game_id']) {
  175. $gameIds = is_array($data['game_id']) ? $data['game_id'] : explode(',', $data['game_id']);
  176. $where['game_id'] = ['in', $gameIds];
  177. }
  178. // 渠道筛选
  179. if (isset($data['complex_id']) && $data['complex_id'] != '') {
  180. $where['complex_id'] = $data['complex_id'];
  181. }
  182. // 时间筛选
  183. if (isset($data['start']) && isset($data['end']) && $data['start'] != '' && $data['end'] != '') {
  184. $startDate = $data['start'];
  185. $endDate = $data['end'];
  186. // 计算日期差
  187. $startTimestamp = strtotime($startDate);
  188. $endTimestamp = strtotime($endDate);
  189. $daysDiff = ($endTimestamp - $startTimestamp) / 86400;
  190. // 如果日期范围大于1天,需要汇总
  191. if ($daysDiff >= 1) {
  192. $isDateRange = true;
  193. }
  194. $where['day'] = [
  195. ['>=', $data['start']],
  196. ['<=', $data['end']],
  197. ];
  198. } elseif (isset($data['start']) && $data['start'] != '') {
  199. $where['day'] = ['>=', $data['start']];
  200. } elseif (isset($data['end']) && $data['end'] != '') {
  201. $where['day'] = ['<=', $data['end']];
  202. } else {
  203. // 默认昨天
  204. $defaultDate = date('Y-m-d', strtotime('-1 day'));
  205. $startDate = $defaultDate;
  206. $endDate = $defaultDate;
  207. $where['day'] = [
  208. ['>=', $defaultDate],
  209. ['<=', $defaultDate],
  210. ];
  211. }
  212. // 构建查询SQL
  213. $query = Db::table('cy_complex_summary_game_retention');
  214. // 如果日期范围大于1天,按游戏ID和渠道ID汇总
  215. if ($isDateRange) {
  216. $sql = $query
  217. ->where($where)
  218. ->fieldRaw("
  219. game_id,
  220. complex_id,
  221. sum(reg_num) as reg_num,
  222. ROUND(CASE WHEN sum(reg_num) = 0 THEN 0 ELSE sum(one_stay * reg_num) / sum(reg_num) END, 2) as one_stay,
  223. ROUND(CASE WHEN sum(reg_num) = 0 THEN 0 ELSE sum(three_stay * reg_num) / sum(reg_num) END, 2) as three_stay,
  224. ROUND(CASE WHEN sum(reg_num) = 0 THEN 0 ELSE sum(four_stay * reg_num) / sum(reg_num) END, 2) as four_stay,
  225. ROUND(CASE WHEN sum(reg_num) = 0 THEN 0 ELSE sum(five_stay * reg_num) / sum(reg_num) END, 2) as five_stay,
  226. ROUND(CASE WHEN sum(reg_num) = 0 THEN 0 ELSE sum(six_stay * reg_num) / sum(reg_num) END, 2) as six_stay,
  227. ROUND(CASE WHEN sum(reg_num) = 0 THEN 0 ELSE sum(seven_stay * reg_num) / sum(reg_num) END, 2) as seven_stay,
  228. ROUND(CASE WHEN sum(reg_num) = 0 THEN 0 ELSE sum(fifteen_stay * reg_num) / sum(reg_num) END, 2) as fifteen_stay,
  229. ROUND(CASE WHEN sum(reg_num) = 0 THEN 0 ELSE sum(thirty_stay * reg_num) / sum(reg_num) END, 2) as thirty_stay
  230. ")
  231. ->group('game_id, complex_id')
  232. ->order('game_id desc, complex_id desc')
  233. ->fetchSql(true)
  234. ->select();
  235. } else {
  236. $sql = $query
  237. ->where($where)
  238. ->field('*')
  239. ->order('day desc, game_id desc')
  240. ->fetchSql(true)
  241. ->select();
  242. }
  243. // 扩展参数,用于Go脚本处理
  244. $extend = [
  245. 'is_date_range' => $isDateRange,
  246. 'start_date' => $startDate,
  247. 'end_date' => $endDate,
  248. 'task_type' => 'complexSummaryGameRetentionExport',
  249. 'file_name' => "渠道-游戏留存报表_%s.csv",
  250. 'headers' => [
  251. ['field' => 'day', 'title' => '日期'],
  252. ['field' => 'game_id', 'title' => '游戏名称'],
  253. ['field' => 'complex_id', 'title' => '渠道名称'],
  254. ['field' => 'reg_num', 'title' => '新增玩家'],
  255. ['field' => 'one_stay', 'title' => '次日留存(%)'],
  256. ['field' => 'three_stay', 'title' => '3日留存(%)'],
  257. ['field' => 'four_stay', 'title' => '4日留存(%)'],
  258. ['field' => 'five_stay', 'title' => '5日留存(%)'],
  259. ['field' => 'six_stay', 'title' => '6日留存(%)'],
  260. ['field' => 'seven_stay', 'title' => '7日留存(%)'],
  261. ['field' => 'fifteen_stay', 'title' => '15日留存(%)'],
  262. ['field' => 'thirty_stay', 'title' => '30日留存(%)'],
  263. ],
  264. // 字段处理
  265. 'handle_fields' => [
  266. [
  267. 'field' => 'game_id',
  268. 'handler' => 'mapGameName',
  269. 'target_field' => 'game_name',
  270. 'extra' => [
  271. 'table_name' => 'cy_game',
  272. 'field_key' => 'name',
  273. 'where_key' => 'id',
  274. ],
  275. ],
  276. [
  277. 'field' => 'complex_id',
  278. 'handler' => 'mapComplexName',
  279. 'target_field' => 'complex_name',
  280. 'extra' => [
  281. 'table_name' => 'nw_complex_channel',
  282. 'field_key' => 'name',
  283. 'where_key' => 'id',
  284. ],
  285. ],
  286. ],
  287. ];
  288. if ((new MakeReportGo())->addTask('admin.complexSummary', $sql, session_id(), $extend)) {
  289. $this->jsonResult('', 1, '报表生成的任务已经提交, 报表生成完成后,会及时通知您,请耐心稍等');
  290. } else {
  291. $this->jsonResult('', 0, '报表生成任务不可重复提交,如遇到无法导出情况,建议修改查询条件解除当前状态,提交重新生成报表任务!');
  292. }
  293. }
  294. }
  295. }