ComplexSummaryGameServer.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  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 ComplexSummaryGameServer 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和服务器ID汇总
  103. if ($isDateRange) {
  104. // 先获取所有汇总数据
  105. $allList = Db::table('cy_complex_summary_game_server')
  106. ->where($where)
  107. ->field('game_id, complex_id, server_id,
  108. max(server_name) as server_name,
  109. max(reg_total) as reg_total,
  110. sum(reg_num) as reg_num,
  111. sum(reg_role_num) as reg_role_num,
  112. sum(active_user_num) as active_user_num,
  113. sum(pay_money) as pay_money,
  114. sum(pay_amount) as pay_amount,
  115. sum(pay_user_num) as pay_user_num')
  116. ->group('game_id, complex_id, server_id')
  117. ->order('game_id desc, complex_id desc, server_id desc')
  118. ->select();
  119. // 转换为数组(统一处理,兼容数组和Collection对象)
  120. $allList = $allList ?: [];
  121. if (!is_array($allList) && method_exists($allList, 'toArray')) {
  122. $allList = $allList->toArray();
  123. }
  124. // 处理汇总数据,设置日期范围
  125. foreach ($allList as &$item) {
  126. $item['day'] = $startDate . ' ~ ' . $endDate;
  127. $activeUser = (int)($item['active_user_num'] ?? 0);
  128. $payUser = (int)($item['pay_user_num'] ?? 0);
  129. $payMoney = (float)($item['pay_money'] ?? 0);
  130. // 计算付费率、ARPU和ARPPU(注意:数据库中存储的值已经乘以了100,所以这里需要除以100)
  131. $item['pay_rate_num'] = $activeUser > 0 ? round(100 * $payUser / $activeUser, 2) : 0;
  132. $item['arpu'] = $activeUser > 0 ? round($payMoney / $activeUser, 2) : 0;
  133. $item['arppu'] = $payUser > 0 ? round($payMoney / $payUser, 2) : 0;
  134. }
  135. unset($item);
  136. // 手动分页处理
  137. $page = intval(input('get.page', 1));
  138. $page = $page > 0 ? $page : 1;
  139. $limit = 20;
  140. $totalCount = count($allList);
  141. $offset = ($page - 1) * $limit;
  142. $listData = array_slice($allList, $offset, $limit);
  143. // 创建分页对象,参考Complex.php的方式
  144. $collection = new \think\Collection($listData);
  145. $list = \think\paginator\driver\Bootstrap::make($collection, $limit, $page, $totalCount, false, [
  146. 'var_page' => 'page',
  147. 'path' => request()->url(),
  148. 'query' => $param,
  149. ]);
  150. } else {
  151. // 查询列表(原有逻辑)
  152. $list = Db::table('cy_complex_summary_game_server')
  153. ->where($where)
  154. ->order('day desc, game_id desc, server_id desc')
  155. ->paginate(20, false, ['query' => $param])->each(function($item){
  156. $item['pay_rate_num'] = round($item['pay_rate_num']/100, 2);
  157. $item['arpu'] = round($item['arpu']/100, 2);
  158. $item['arppu'] = round($item['arppu']/100, 2);
  159. return $item;
  160. });
  161. }
  162. // 计算汇总
  163. $total = Db::table('cy_complex_summary_game_server')
  164. ->where($where)
  165. ->field('count(*) as total, sum(reg_total) as reg_total, sum(reg_num) as reg_num, sum(reg_role_num) as reg_role_num, sum(pay_money) as pay_money, sum(pay_amount) as pay_amount, sum(pay_user_num) as pay_user_num')
  166. ->find();
  167. // 跨日期时,列表中的玩家总数按游戏、渠道、区服取累计最大值,
  168. // 底部汇总必须使用相同口径,不能把每天的累计值重复相加。
  169. if ($isDateRange) {
  170. $total['reg_total'] = array_sum(array_map(function ($item) {
  171. return (int)($item['reg_total'] ?? 0);
  172. }, $allList));
  173. }
  174. return [$list, $total ?: ['total' => 0, 'reg_total' => 0, 'reg_num' => 0, 'reg_role_num' => 0, 'pay_money' => 0, 'pay_amount' => 0, 'pay_user_num' => 0]];
  175. }
  176. /**
  177. * 导出数据
  178. */
  179. public function export()
  180. {
  181. if (request()->isPost()) {
  182. $data = input('post.');
  183. $where = [];
  184. $isDateRange = false;
  185. $startDate = '';
  186. $endDate = '';
  187. // 游戏筛选(多选)
  188. if (isset($data['game_id']) && $data['game_id']) {
  189. $gameIds = is_array($data['game_id']) ? $data['game_id'] : explode(',', $data['game_id']);
  190. $where['game_id'] = ['in', $gameIds];
  191. }
  192. // 渠道筛选
  193. if (isset($data['complex_id']) && $data['complex_id'] != '') {
  194. $where['complex_id'] = $data['complex_id'];
  195. }
  196. // 时间筛选
  197. if (isset($data['start']) && isset($data['end']) && $data['start'] != '' && $data['end'] != '') {
  198. $startDate = $data['start'];
  199. $endDate = $data['end'];
  200. // 计算日期差
  201. $startTimestamp = strtotime($startDate);
  202. $endTimestamp = strtotime($endDate);
  203. $daysDiff = ($endTimestamp - $startTimestamp) / 86400;
  204. // 如果日期范围大于1天,需要汇总
  205. if ($daysDiff >= 1) {
  206. $isDateRange = true;
  207. }
  208. $where['day'] = [
  209. ['>=', $data['start']],
  210. ['<=', $data['end']],
  211. ];
  212. } elseif (isset($data['start']) && $data['start'] != '') {
  213. $where['day'] = ['>=', $data['start']];
  214. } elseif (isset($data['end']) && $data['end'] != '') {
  215. $where['day'] = ['<=', $data['end']];
  216. } else {
  217. // 默认昨天
  218. $defaultDate = date('Y-m-d', strtotime('-1 day'));
  219. $startDate = $defaultDate;
  220. $endDate = $defaultDate;
  221. $where['day'] = [
  222. ['>=', $defaultDate],
  223. ['<=', $defaultDate],
  224. ];
  225. }
  226. // 构建查询SQL
  227. $query = Db::table('cy_complex_summary_game_server');
  228. // 如果日期范围大于1天,按游戏ID、渠道ID和服务器ID汇总
  229. if ($isDateRange) {
  230. $sql = $query
  231. ->where($where)
  232. ->field('game_id, complex_id, server_id,
  233. max(server_name) as server_name,
  234. max(reg_total) as reg_total,
  235. sum(reg_num) as reg_num,
  236. sum(reg_role_num) as reg_role_num,
  237. sum(active_user_num) as active_user_num,
  238. sum(pay_money) as pay_money,
  239. sum(pay_user_num) as pay_user_num,
  240. ROUND(CASE WHEN sum(active_user_num) = 0 THEN 0 ELSE 100 * sum(pay_user_num) / sum(active_user_num) END, 2) as pay_rate_num,
  241. ROUND(CASE WHEN sum(active_user_num) = 0 THEN 0 ELSE sum(pay_money) / sum(active_user_num) END, 2) as arpu,
  242. ROUND(CASE WHEN sum(pay_user_num) = 0 THEN 0 ELSE sum(pay_money) / sum(pay_user_num) END, 2) as arppu')
  243. ->group('game_id, complex_id, server_id')
  244. ->order('game_id desc, complex_id desc, server_id desc')
  245. ->fetchSql(true)
  246. ->select();
  247. } else {
  248. $sql = $query
  249. ->where($where)
  250. ->field('*')
  251. ->order('day desc, game_id desc, server_id desc')
  252. ->fetchSql(true)
  253. ->select();
  254. }
  255. // 扩展参数,用于Go脚本处理
  256. $extend = [
  257. 'is_date_range' => $isDateRange,
  258. 'start_date' => $startDate,
  259. 'end_date' => $endDate,
  260. 'task_type' => 'complexSummaryGameServerExport',
  261. 'file_name' => "渠道-游戏区服数据_%s.csv",
  262. 'headers' => [
  263. ['field' => 'day', 'title' => '日期'],
  264. ['field' => 'game_id', 'title' => '游戏名称'],
  265. ['field' => 'complex_id', 'title' => '渠道名称'],
  266. ['field' => 'server_id', 'title' => '区服名称'],
  267. ['field' => 'reg_total', 'title' => '累计注册'],
  268. ['field' => 'reg_num', 'title' => '新增玩家'],
  269. ['field' => 'reg_role_num', 'title' => '新增角色'],
  270. ['field' => 'active_user_num','title' => '活跃玩家'],
  271. ['field' => 'pay_money', 'title' => '充值金额'],
  272. ['field' => 'pay_user_num', 'title' => '付费人数'],
  273. ['field' => 'pay_rate_num', 'title' => '付费率(%)'],
  274. ['field' => 'arpu', 'title' => 'ARPU'],
  275. ['field' => 'arppu', 'title' => 'ARPPU'],
  276. ],
  277. // 字段处理
  278. 'handle_fields' => [
  279. [
  280. 'field' => 'game_id',
  281. 'handler' => 'mapGameName',
  282. 'target_field' => 'game_name',
  283. 'extra' => [
  284. 'table_name' => 'cy_game',
  285. 'field_key' => 'name',
  286. 'where_key' => 'id',
  287. ],
  288. ],
  289. [
  290. 'field' => 'complex_id',
  291. 'handler' => 'mapComplexName',
  292. 'target_field' => 'complex_name',
  293. 'extra' => [
  294. 'table_name' => 'nw_complex_channel',
  295. 'field_key' => 'name',
  296. 'where_key' => 'id',
  297. ],
  298. ],
  299. [
  300. 'field' => 'server_id',
  301. 'handler' => 'mapServerName',
  302. 'target_field' => 'server_name',
  303. 'extra' => [
  304. 'table_name' => 'nw_complex_server',
  305. 'field_key' => 'server_name',
  306. 'where_key' => 'id',
  307. ],
  308. ],
  309. ],
  310. ];
  311. if ((new MakeReportGo())->addTask('admin.complexSummary', $sql, session_id(), $extend)) {
  312. $this->jsonResult('', 1, '报表生成的任务已经提交, 报表生成完成后,会及时通知您,请耐心稍等');
  313. } else {
  314. $this->jsonResult('', 0, '报表生成任务不可重复提交,如遇到无法导出情况,建议修改查询条件解除当前状态,提交重新生成报表任务!');
  315. }
  316. }
  317. }
  318. }