企微短剧业务系统

DataStatisticsService.php 123KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: shensong
  5. * Date: 2022/4/20
  6. * Time: 11:55
  7. */
  8. namespace App\Service;
  9. use App\Console\Commands\PlayletDataStatistics;
  10. use App\Console\Commands\PlayletTrendDataStatistics;
  11. use App\Console\Commands\NewReport\AdqPlayletAccountDataTrendStatistics;
  12. use App\Console\Commands\Report\AdqPlayletDataTrendStatistics;
  13. use App\Console\Commands\NewReport\MpPlayletAccountDataTrendStatistics;
  14. use App\Console\Commands\Report\MpPlayletDataTrendStatistics;
  15. use App\Log;
  16. use App\Models\AccountConfigNoUserRelation;
  17. use App\Models\ActiveFansData;
  18. use App\Models\AdqAccountTrendData;
  19. use App\Models\AdqPlayletTrendData;
  20. use App\Models\AdqUser;
  21. use App\Models\DjOrder;
  22. use App\Models\DramaSeries;
  23. use App\Models\DramaUserRela;
  24. use App\Models\Es\AccountDataTrend;
  25. use App\Models\Es\OperateDataTrend;
  26. use App\Models\Es\PlayletAccountDataTrend;
  27. use App\Models\MpAccountTrendData;
  28. use App\Models\MpPlayletTrendData;
  29. use App\Models\OfficialAccount;
  30. use App\Models\OfficialAccountData;
  31. use App\Models\OfficialAccountRelation;
  32. use App\Models\OfficialWebUserActionSetId;
  33. use App\Models\PlayletTrendStatistics;
  34. use App\Models\Report\DjCumulativeRecoveryData;
  35. use App\Models\Report\DjDataCycleAdq;
  36. use App\Models\Report\DjDataCycleMp;
  37. use App\Models\Report\DjDataCycleNur;
  38. use App\Models\Report\DjRegUserRangeReport;
  39. use App\Models\Report\PlayletDataTrend;
  40. use App\Models\System\Users;
  41. use App\Models\TencentAdAuth;
  42. use App\Models\TencentAdDailyReport;
  43. use App\Models\VpOrder;
  44. use App\RedisModel;
  45. use App\Models\PlayletStatisticsData as Statistics;
  46. use App\Support\EmailQueue;
  47. class DataStatisticsService
  48. {
  49. protected static $days = 2;
  50. // 公众号数据
  51. public static function wechatAccountData($startDate, $endDate, $accountId, $corpid, $sortField, $page, $pageSize, $sysGroupId)
  52. {
  53. // 查询公众号列表
  54. $select = 'mp_app_id as app_id, mp_name as account_name';
  55. list($accountList, $total) = OfficialAccount::getSysGroupMpAppList($accountId, $sysGroupId, $select,
  56. 'DataStatisticsService.wechatAccountData');
  57. if(empty($accountList)) return [['list' => [], 'total' => []], 0];
  58. $appIds = array_unique(array_column($accountList->toArray(), 'app_id'));
  59. // 获取最早消耗日期,最晚消耗日期和累计消耗金额
  60. $paidCondition = TencentAdDailyReport::paidCondition($appIds, $startDate, $endDate);
  61. // 获取累计充值金额,累计充值人数
  62. $chargeOrders = DjOrder::chargeOrders($appIds, $startDate, $endDate);
  63. // 获取最近三天的充值数据
  64. $days = self::$days;
  65. $recentPaid = DjOrder::getRecentPaidNew($appIds, $days, $startDate, $endDate);
  66. // 获取各个日期对应的账号对应投手数据
  67. $promoteList = DramaUserRela::getPromoteByDate($appIds);
  68. // 获取所有投手基础信息
  69. $promoterList = DataStatisticsService::getPromoterList();
  70. # 获取最近三天数据
  71. $today = date('Y-m-d');
  72. $yesterday = date('Y-m-d', strtotime('-1 days'));
  73. $beforeYesterday = date('Y-m-d', strtotime('-2 days'));
  74. # 处理数据
  75. foreach($accountList as $list) {
  76. $promoter_name = [];
  77. # 最早消耗日期,最晚消耗日期和累计消耗,总关注人数处理
  78. if(isset($paidCondition[$list->app_id])) {
  79. $list->start_paid = $paidCondition[$list->app_id]['start_paid'];
  80. $list->end_paid = $paidCondition[$list->app_id]['end_paid'];
  81. $list->paid_total = $paidCondition[$list->app_id]['paid_total'] / 100;
  82. $list->follow_total = $paidCondition[$list->app_id]['scan_follow_count'];// 企微关注数
  83. // 投手数据
  84. $promoteInfo = $promoteList->where('app_id', $list->app_id)
  85. ->where('start_date', '<=', $list->end_paid)
  86. ->where('end_date', '>=', $list->start_paid)
  87. ->pluck('user_id')
  88. ->toArray();
  89. foreach ($promoteInfo as $promoterId) {
  90. $promoterInfo = isset($promoterList[$promoterId]) ? $promoterList[$promoterId] : null;
  91. $promoterName = isset($promoterInfo['name']) ? $promoterInfo['name'] : null;
  92. if(!empty($promoterName)) {
  93. $promoter_name[] = $promoterName;
  94. }
  95. }
  96. $promoter_name = array_unique($promoter_name);
  97. } else {
  98. $list->start_paid = 0;
  99. $list->end_paid = 0;
  100. $list->paid_total = 0;
  101. $list->follow_total = 0;
  102. }
  103. $list->promoter_name = implode(',', $promoter_name);
  104. # 平均关注人数成本(累计消耗 / 总的企微关注人数)
  105. if(is_numeric($list->paid_total) && is_numeric($list->follow_total)) {
  106. $list->average_follow_paid = $list->follow_total != 0 ?
  107. round($list->paid_total / $list->follow_total, 2) : 0;
  108. } else {
  109. $list->average_follow_paid = 0;
  110. }
  111. # 累计充值、总充值人数(在时间范围内新注册用户的)
  112. if(isset($chargeOrders[$list->app_id])) {
  113. $list->charge_total = $chargeOrders[$list->app_id]['charge_total'] / 10000;
  114. $list->charge_user_total = $chargeOrders[$list->app_id]['charge_user_total'];
  115. } else {
  116. $list->charge_total = 0;
  117. $list->charge_user_total = 0;
  118. }
  119. # 充值转化比率(总充值人数 / 总的企微关注数)
  120. if(is_numeric($list->charge_user_total) && is_numeric($list->follow_total)) {
  121. $list->charge_transform = $list->follow_total != 0 ? round(($list->charge_user_total / $list->follow_total) * 100, 2) : 0;
  122. } else {
  123. $list->charge_transform = 0;
  124. }
  125. # 总毛利额(累计充值金额 - 累计消耗)、回本率(累计充值金额 / 累计消耗)
  126. if(is_numeric($list->paid_total) && is_numeric($list->charge_total)) {
  127. $list->margin_rate = round($list->charge_total - $list->paid_total, 2);
  128. $list->cost_recovery_rate = $list->paid_total != 0 ?
  129. round(($list->charge_total / $list->paid_total) * 100,2) : 0;
  130. } else {
  131. $list->margin_rate = 0;
  132. $list->cost_recovery_rate = 0;
  133. }
  134. # 充值转化成本(累计消耗金额 / 总充值人数)
  135. if(is_numeric($list->paid_total) && is_numeric($list->charge_user_total)) {
  136. $list->charge_transform_cost = $list->charge_user_total != 0 ?
  137. round($list->paid_total / $list->charge_user_total, 2) : 0;
  138. } else {
  139. $list->charge_transform_cost = 0;
  140. }
  141. # 获取近三天的充值金额
  142. $todayData = $recentPaid->where('paid_date', $today)
  143. ->where('app_id', $list->app_id)->first();
  144. $list->today = isset($todayData->day_paid) ? ($todayData->day_paid / 10000) : 0;
  145. $yesterdayData = $recentPaid->where('paid_date', $yesterday)
  146. ->where('app_id', $list->app_id)->first();
  147. $list->yesterday = isset($yesterdayData->day_paid) ? ($yesterdayData->day_paid / 10000) : 0;
  148. $beforeYesterdayData = $recentPaid->where('paid_date', $beforeYesterday)
  149. ->where('app_id', $list->app_id)->first();
  150. $list->before_yesterday = isset($beforeYesterdayData->day_paid) ? ($beforeYesterdayData->day_paid / 10000)
  151. : 0;
  152. }
  153. // 计算总概数据
  154. $condition = DataStatisticsService::accountCondition($startDate, $endDate, $accountId, $sysGroupId);
  155. // 数据切割,排序
  156. $accountList = json_decode(json_encode($accountList), 1);
  157. $newAccountList = DataStatisticsService::my_array_multisort($accountList, $sortField);
  158. $accountList = array_splice($newAccountList, ($page - 1)*$pageSize, $pageSize);
  159. $data = ['list' => $accountList, 'total' => $condition];
  160. return [$data, $total];
  161. }
  162. // 获取企业关联的公众号列表
  163. public static function getAccountIdListByCorpid($corpid)
  164. {
  165. $data = OfficialAccount::query()
  166. ->where('enable', 1)
  167. ->where('corp_id', $corpid)
  168. ->pluck('mp_app_id')
  169. ->toArray();
  170. return $data;
  171. }
  172. // 获取投手列表
  173. public static function getPromoterList()
  174. {
  175. $list = Users::query()->get()->toArray();
  176. $list = array_column($list, null, 'id');
  177. return $list;
  178. }
  179. // 公众号数据总概
  180. public static function accountCondition($startDate, $endDate, $accountId, $sysGroupId)
  181. {
  182. $select = 'mp_app_id as app_id, mp_name as account_name';
  183. list($accountList, $total) = OfficialAccount::getSysGroupMpAppList($accountId, $sysGroupId, $select,
  184. 'DataStatisticsService.accountCondition');
  185. if(!$total) return [];
  186. $appIds = array_column($accountList->toArray(), 'app_id');
  187. # 获取MP端数据汇总
  188. $mpData = TencentAdDailyReport::getPaidTotalCondition($startDate, $endDate, $appIds);
  189. # 获取小说平台订单数据汇总
  190. $orderData = DjOrder::getOrderTotalCondition($startDate, $endDate, $appIds);
  191. $paidTotal = isset($mpData->paid_total) ? ($mpData->paid_total / 100) : 0;
  192. $chargeTotal = isset($orderData->charge_total) ? ($orderData->charge_total / 10000) : 0;
  193. $followTotal = isset($mpData->scan_follow_count) ? $mpData->scan_follow_count : 0;
  194. # 获取最近三天充值数据
  195. $days = self::$days;
  196. $recentPaid = DjOrder::getTotalRecentPaidNew($appIds, $days, $startDate, $endDate);
  197. # 获取最近三天数据
  198. $today = date('Y-m-d');
  199. $yesterday = date('Y-m-d', strtotime('-1 days'));
  200. $beforeYesterday = date('Y-m-d', strtotime('-2 days'));
  201. # 获取近三天的充值金额
  202. $todayData = $recentPaid->where('paid_date', $today)->first();
  203. $yesterdayData = $recentPaid->where('paid_date', $yesterday)->first();
  204. $beforeYesterdayData = $recentPaid->where('paid_date', $beforeYesterday)->first();
  205. $data = [
  206. 'paid_total' => round($paidTotal, 2),
  207. 'charge_total' => round($chargeTotal, 2),
  208. 'margin_rate' => round($chargeTotal - $paidTotal, 2),
  209. 'cost_recovery_rate' => $paidTotal != 0 ? round(($chargeTotal / $paidTotal) * 100, 2) : 0,
  210. 'follow_total' => $followTotal,
  211. 'average_follow_paid' => $followTotal != 0 ? round($paidTotal / $followTotal, 2) : 0,
  212. 'charge_user_total' => isset($orderData->charge_uv_total) ? $orderData->charge_uv_total : 0,
  213. 'today' => isset($todayData->day_paid) ? ($todayData->day_paid / 10000) : 0,
  214. 'yesterday' => isset($yesterdayData->day_paid) ? ($yesterdayData->day_paid / 10000) : 0,
  215. 'before_yesterday' => isset($beforeYesterdayData->day_paid) ? ($beforeYesterdayData->day_paid / 10000) : 0,
  216. ];
  217. return $data;
  218. }
  219. // 内存排序
  220. public static function my_array_multisort($data,$sort_order_field,$sort_order=SORT_DESC,$sort_type=SORT_NUMERIC){
  221. $key_arrays = [];
  222. foreach($data as $val){
  223. $key_arrays[]=$val[$sort_order_field];
  224. }
  225. array_multisort($key_arrays,$sort_order,$sort_type,$data);
  226. return $data;
  227. }
  228. // 短剧投放趋势数据
  229. public static function playletTrendData($startDate, $endDate, $accountId, $corpid, $playletId, $page, $pageSize)
  230. {
  231. if(empty($endDate)) {
  232. $endDate = date('Y-m-d');
  233. }
  234. // 整理日期列表
  235. list($dateList, $total) = PlayletTrendStatistics::playletTrendDateList($startDate, $endDate, $accountId, $corpid,
  236. $playletId, $page, $pageSize);
  237. // 提取日期数据
  238. $dates = array_column($dateList->toArray(), 'ref_date');
  239. if(empty($dates)) {
  240. return [['list' => [], 'overview' => []], $total];
  241. }
  242. // 获取日期范围内的统计数据
  243. $dataList = PlayletTrendStatistics::statisticsData(null, null, $dates, $accountId, $corpid, $playletId);
  244. $appIds = array_values(array_unique(array_column($dataList->toArray(), 'app_id')));
  245. # 获取短剧列表
  246. $dramaSeriesList = DramaSeries::getDramaSeriesList();
  247. // 获取公众号基础信息
  248. $accountList = OfficialAccount::getAccountListCondition($appIds);
  249. $result = [];
  250. foreach($dataList->toArray() as $value) {
  251. // 获取公众号名称
  252. $accountName = $accountList->where('mp_app_id', $value['app_id'])->first();
  253. $value['account_name'] = isset($accountName->mp_name) ? $accountName->mp_name : null;
  254. // 当日roi
  255. $value['first_day_roi'] = ($value['day_paid'] != 0) ? round($value['new_user_charge'] / $value['day_paid'], 4) * 100 : 0;
  256. // 回本率
  257. $value['cost_cover_reate'] = round($value['cost_cover_rate'] * 100, 2) . '%';
  258. // 统计数据(总)
  259. $value['chargeData'] = [
  260. // 充:当日回收
  261. 'day_charge' => $value['new_user_charge'],
  262. // 累计充值
  263. 'charge_total' => $value['charge_total'],
  264. // 当日消耗
  265. 'day_paid' => $value['day_paid'],
  266. // 增:当日roi = 当日回收/消耗
  267. 'day_add' => ($value['day_paid'] != 0 ) ?
  268. round(($value['new_user_charge'] / $value['day_paid']) * 100, 2) . '%' : '0%',
  269. // 回:回本率 = 累计充值/消耗
  270. 'day_cover' => ($value['day_paid'] != 0 ) ?
  271. round(($value['charge_total'] / $value['day_paid']) * 100, 2) . '%' : '0%',
  272. // 倍:累计充值/当日回收
  273. 'day_times' => ($value['day_paid'] * $value['first_day_roi'] != 0) ? ($value['charge_total'] / ($value['day_paid'] * $value['first_day_roi'])) : 0,
  274. // day1...day100
  275. 'days' => DataStatisticsService::chargeDataFormat(json_decode($value['charge_data'], 1)),
  276. ];
  277. // $value['first_day_roi'] = $value['first_day_roi'] . '%';
  278. // 设置日期
  279. if(!isset($result[$value['ref_date']])) {
  280. $result[$value['ref_date']]['ref_date'] = $value['ref_date'];
  281. }
  282. // 设置剧集名称
  283. if(!isset($result[$value['ref_date']][$value['playlet_id']])) {
  284. $dramaSeries = $dramaSeriesList->where('id', $value['playlet_id'])->first();
  285. $dramaSeriesName = isset($dramaSeries->name) ? $dramaSeries->name : null;
  286. $result[$value['ref_date']]['playlet_list'][$value['playlet_id']]['playlet_name'] = $dramaSeriesName;
  287. }
  288. // 将单条数据写入数组中
  289. $result[$value['ref_date']]['playlet_list'][$value['playlet_id']]['app_list'][] = $value;
  290. // 统计汇总数据(短剧在某日的汇总)
  291. if(isset($result[$value['ref_date']]['playlet_list'][$value['playlet_id']]['total'])) {
  292. $totalData = $result[$value['ref_date']]['playlet_list'][$value['playlet_id']]['total'];
  293. $chargeTotalData = $result[$value['ref_date']]['playlet_list'][$value['playlet_id']]['total']['chargeData']['days'];
  294. // 可以直接累加的数据
  295. $newTotalData = [
  296. // 企微关注人数
  297. 'scan_follow_count' => $value['scan_follow_count'] + $totalData['scan_follow_count'],
  298. // 当日回收
  299. 'new_user_charge' => $value['new_user_charge'] + $totalData['new_user_charge'],
  300. // 当日消耗
  301. 'day_paid' => $value['day_paid'] + $totalData['day_paid'],
  302. // 累计充值金额
  303. 'charge_total' => $value['charge_total'] + $totalData['charge_total'],
  304. // 新增用户数
  305. 'fans_increase' => $value['fans_increase'] + $totalData['fans_increase'],
  306. // 当日充值人数
  307. 'new_user_charge_uv' => $value['new_user_charge_uv'] + $totalData['new_user_charge_uv'],
  308. // 总充值人数
  309. 'new_user_charge_uv_count' => $value['new_user_charge_uv_count'] + $totalData['new_user_charge_uv_count'],
  310. // 当日充值次数
  311. 'new_user_charge_pv' => $value['new_user_charge_pv'] + $totalData['new_user_charge_pv'],
  312. // 总充值次数
  313. 'new_user_charge_pv_count' => $value['new_user_charge_pv_count'] + $totalData['new_user_charge_pv_count'],
  314. ];
  315. // 累加后需要计算的数据
  316. // 企微关注成本
  317. $newTotalData['follow_cost'] = ($newTotalData['scan_follow_count'] != 0) ?
  318. round($newTotalData['day_paid'] / $newTotalData['scan_follow_count'], 2) : 0;
  319. // 回本率
  320. $newTotalData['cost_cover_reate'] = ($newTotalData['day_paid'] != 0) ?
  321. round(($newTotalData['charge_total'] / $newTotalData['day_paid']) * 100, 2) . '%' : '0%';
  322. // 充值用户成本
  323. $newTotalData['charge_user_cost'] = ($newTotalData['new_user_charge_uv'] != 0) ?
  324. round($newTotalData['day_paid'] / $newTotalData['new_user_charge_uv'], 2) : 0;
  325. // 首日roi
  326. $newTotalData['first_day_roi'] = ($newTotalData['day_paid'] != 0) ?
  327. round($newTotalData['new_user_charge'] / $newTotalData['day_paid'], 4) * 100 : 0;
  328. $newChargeTotalData = DataStatisticsService::chargeDataSummary($chargeTotalData, $value['charge_data'], $newTotalData['first_day_roi']);
  329. // $newTotalData['first_day_roi'] = $newTotalData['first_day_roi'] . '%';
  330. // 统计数据
  331. $newTotalData['chargeData'] = [
  332. // 充:当日回收
  333. 'day_charge' => $newTotalData['new_user_charge'],
  334. // 累计充值
  335. 'charge_total' => $newTotalData['charge_total'],
  336. // 当日消耗
  337. 'day_paid' => $newTotalData['day_paid'],
  338. // 增:当日roi = 当日回收/消耗
  339. 'day_add' => ($newTotalData['day_paid'] != 0 ) ?
  340. round(($newTotalData['new_user_charge'] / $newTotalData['day_paid']) * 100, 2) . '%' : '0%',
  341. // 回:回本率 = 累计充值/消耗
  342. 'day_cover' => ($newTotalData['day_paid'] != 0 ) ?
  343. round(($newTotalData['charge_total'] / $newTotalData['day_paid']) * 100, 2) . '%' : '0%',
  344. // 倍:累计充值/首日回收
  345. 'day_times' => (($value['day_paid'] * $value['first_day_roi'])) ?
  346. round($newTotalData['charge_total'] / ($value['day_paid'] * $value['first_day_roi']), 2) : 0,
  347. // 每日统计
  348. 'days' => DataStatisticsService::chargeDataFormat($newChargeTotalData),
  349. ];
  350. $result[$value['ref_date']]['playlet_list'][$value['playlet_id']]['total'] = $newTotalData;
  351. } else {
  352. // 短剧对应的第一条公众号数据,直接赋值
  353. $result[$value['ref_date']]['playlet_list'][$value['playlet_id']]['total'] = [
  354. // 当日回收
  355. 'new_user_charge' => $value['new_user_charge'],
  356. // 当日消耗
  357. 'day_paid' => $value['day_paid'],
  358. // 企微关注人数
  359. 'scan_follow_count' => $value['scan_follow_count'],
  360. // 企微关注成本
  361. 'follow_cost' => $value['follow_cost'],
  362. // 累计充值金额
  363. 'charge_total' => $value['charge_total'],
  364. // 新增用户数(公众号关注人数)
  365. 'fans_increase' => $value['fans_increase'],
  366. // 回本率
  367. 'cost_cover_reate' => round($value['cost_cover_rate'] * 100, 2) . '%',
  368. // 当日充值人数
  369. 'new_user_charge_uv' => $value['new_user_charge_uv'],
  370. // 总充值人数
  371. 'new_user_charge_uv_count' => $value['new_user_charge_uv_count'],
  372. // 当日充值次数
  373. 'new_user_charge_pv' => $value['new_user_charge_pv'],
  374. // 总充值次数
  375. 'new_user_charge_pv_count' => $value['new_user_charge_pv_count'],
  376. // 充值用户成本
  377. 'charge_user_cost' => $value['charge_user_cost'],
  378. // 统计
  379. 'chargeData' => $value['chargeData'],
  380. ];
  381. }
  382. }
  383. // 键值格式化,便于前端处理
  384. foreach($result as &$item) {
  385. foreach($item['playlet_list'] as &$value) {
  386. $value['app_list'] = array_values($value['app_list']);
  387. }
  388. $item['playlet_list'] = array_values($item['playlet_list']);
  389. }
  390. $result = array_values($result);
  391. $result = self::my_array_multisort($result, 'ref_date', SORT_DESC,SORT_STRING);
  392. // 总概
  393. $overview = DataStatisticsService::getPlayletTrendDataOverview($startDate, $endDate, $accountId, $corpid,
  394. $playletId);
  395. // 获取day1至day100表头
  396. $extra = [];
  397. for($i = 0;$i < PlayletTrendStatistics::DAYS;$i++) {
  398. $title = 'day'.($i+1);
  399. $extra[] = $title;
  400. }
  401. return [['list' => $result, 'overview' => $overview, 'extra' => $extra], $total];
  402. }
  403. // 短剧投放趋势数据总概
  404. public static function getPlayletTrendDataOverview($startDate, $endDate, $accountId, $corpid, $playletId)
  405. {
  406. // 获取日期范围内的统计数据
  407. $dataList = PlayletTrendStatistics::statisticsData($startDate, $endDate, null, $accountId, $corpid, $playletId);
  408. // 提取公众号数据
  409. $appIds = array_values(array_unique(array_column($dataList->toArray(), 'app_id')));
  410. $paidTotal = $chargeTotal = $followTotal = $chargeUVTotal = 0;
  411. foreach($dataList as $item) {
  412. $paidTotal += $item['day_paid'];
  413. $chargeTotal += $item['charge_total'];
  414. $followTotal += $item['scan_follow_count'];
  415. $chargeUVTotal += $item['new_user_charge_uv_count'];
  416. }
  417. # 获取最近三天充值数据
  418. $days = self::$days;
  419. $recentPaid = DjOrder::getTotalRecentPaidNew($appIds, $days, $startDate, $endDate);
  420. # 获取最近三天数据
  421. $today = date('Y-m-d');
  422. $yesterday = date('Y-m-d', strtotime('-1 days'));
  423. $beforeYesterday = date('Y-m-d', strtotime('-2 days'));
  424. # 获取近三天的充值金额
  425. $todayData = $recentPaid->where('paid_date', $today)->first();
  426. $yesterdayData = $recentPaid->where('paid_date', $yesterday)->first();
  427. $beforeYesterdayData = $recentPaid->where('paid_date', $beforeYesterday)->first();
  428. $data = [
  429. 'paid_total' => round($paidTotal, 2),
  430. 'charge_total' => round($chargeTotal, 2),
  431. 'margin_rate' => round($chargeTotal - $paidTotal, 2),
  432. 'cost_recovery_rate' => $paidTotal != 0 ? round(($chargeTotal / $paidTotal) * 100, 2) : 0,
  433. 'follow_total' => $followTotal,
  434. 'average_follow_paid' => $followTotal != 0 ? ($paidTotal / $followTotal) : 0,
  435. 'charge_user_total' => $chargeUVTotal,
  436. 'today' => isset($todayData->day_paid) ? ($todayData->day_paid / 10000) : 0,
  437. 'yesterday' => isset($yesterdayData->day_paid) ? ($yesterdayData->day_paid / 10000) : 0,
  438. 'before_yesterday' => isset($beforeYesterdayData->day_paid) ? ($beforeYesterdayData->day_paid / 10000) : 0,
  439. ];
  440. return $data;
  441. }
  442. // 短剧数据报表
  443. public static function playletData($startDate, $endDate, $accountId, $corpid, $playletId, $sortField, $page, $pageSize)
  444. {
  445. $data = PlayletTrendStatistics::getPlayletDataList($startDate, $endDate, $accountId, $corpid,
  446. $playletId);
  447. $total = count($data);
  448. if(empty($data)) return [[], 0];
  449. // 提取公众号信息
  450. $appIds = array_values(array_unique(array_column($data->toArray(), 'app_id')));
  451. # 获取短剧列表
  452. $dramaSeriesList = DramaSeries::getDramaSeriesList();
  453. // 获取公众号基础信息
  454. $accountList = OfficialAccount::getAccountListCondition($appIds);
  455. // 获取各个日期对应的账号对应投手数据
  456. $promoteList = DramaUserRela::getPromoteByDate($appIds);
  457. // 获取所有投手基础信息
  458. $promoterList = DataStatisticsService::getPromoterList();
  459. # 获取最近三天充值数据
  460. $days = self::$days;
  461. $recentPaid = DjOrder::getRecentPaidNew($appIds, $days, $startDate, $endDate);
  462. # 获取最近三天数据
  463. $today = date('Y-m-d');
  464. $yesterday = date('Y-m-d', strtotime('-1 days'));
  465. $beforeYesterday = date('Y-m-d', strtotime('-2 days'));
  466. // 循环拼接数据
  467. foreach($data as $item) {
  468. // 公众号名称
  469. $accountName = $accountList->where('mp_app_id', $item->app_id)->first();
  470. $item->account_name = isset($accountName->mp_name) ? $accountName->mp_name : null;
  471. // 短剧名称
  472. $dramaSeries = $dramaSeriesList->where('id', $item->playlet_id)->first();
  473. $item->playlet_name = isset($dramaSeries->name) ? $dramaSeries->name : null;
  474. // 投手名称
  475. $promoter_name = [];
  476. // 投手数据
  477. $promoteInfo = $promoteList->where('app_id', $item->app_id)
  478. ->where('start_date', '<=', $item->end_paid)
  479. ->where('end_date', '>=', $item->start_paid)
  480. ->pluck('user_id')
  481. ->toArray();
  482. foreach ($promoteInfo as $promoterId) {
  483. $promoterInfo = isset($promoterList[$promoterId]) ? $promoterList[$promoterId] : null;
  484. $promoterName = isset($promoterInfo['name']) ? $promoterInfo['name'] : null;
  485. if(!empty($promoterName)) {
  486. $promoter_name[] = $promoterName;
  487. }
  488. }
  489. $promoter_name = array_unique($promoter_name);
  490. $item->promoter_name = implode(',', $promoter_name);
  491. // 总毛利润
  492. $item->margin_rate = $item->charge_total - $item->paid_total;
  493. // 回本率
  494. $item->cost_recovery_rate = $item->paid_total != 0 ?
  495. round(($item->charge_total / $item->paid_total) * 100, 2) : 0;
  496. // 平均关注人数成本
  497. $item->average_follow_paid = $item->follow_total != 0 ? round($item->paid_total / $item->follow_total, 2) : 0;
  498. // 充值转化比率
  499. $item->charge_transform = $item->follow_total != 0 ?
  500. round(($item->charge_user_total / $item->follow_total) * 100, 2) : 0;
  501. // 充值转化成本
  502. $item->charge_transform_cost = $item->charge_user_total != 0 ?
  503. round($item->paid_total / $item->charge_user_total, 2) : 0;;
  504. // 今日充值金额
  505. $todayData = $recentPaid->where('paid_date', $today)->where('app_id', $item->app_id)->first();
  506. // 昨日充值金额
  507. $yesterdayData = $recentPaid->where('paid_date', $yesterday)->where('app_id', $item->app_id)->first();
  508. // 前日充值金额
  509. $beforeYesterdayData = $recentPaid->where('paid_date', $beforeYesterday)->where('app_id', $item->app_id)->first();
  510. // 今日充值金额
  511. $item->today = isset($todayData->day_paid) ? ($todayData->day_paid / 10000) : 0;
  512. // 昨日充值金额
  513. $item->yesterday = isset($yesterdayData->day_paid) ? ($yesterdayData->day_paid / 10000) : 0;
  514. // 前日充值金额
  515. $item->before_yesterday = isset($beforeYesterdayData->day_paid) ? ($beforeYesterdayData->day_paid / 10000) : 0;
  516. }
  517. // 排序
  518. $data = DataStatisticsService::my_array_multisort($data->toArray(), $sortField);
  519. // 分片
  520. $result = array_splice($data, ($page -1) * $pageSize, $pageSize);
  521. return [$result, $total];
  522. }
  523. // 新增绑定关系
  524. public static function mpRelaAdd($appId, $dramaId, $userId, $startDate, $endDate)
  525. {
  526. $requestData = [
  527. 'app_id' => $appId,
  528. 'drama_id' => $dramaId,
  529. 'user_id' => $userId,
  530. 'start_date' => $startDate,
  531. 'end_date' => $endDate
  532. ];
  533. try{
  534. $date = $startDate;
  535. if($endDate >= date('Y-m-d')) {
  536. $endDate = date('Y-m-d');
  537. }
  538. while($date <= $endDate) {
  539. $value['app_list'] = [$appId];
  540. $value['ref_date'] = $date;
  541. $value['playlet_id'] = $dramaId;
  542. $value['user_id'] = $userId; //投手标识
  543. $value['order_type'] = 1;
  544. # 投手数据
  545. RedisModel::lPush(PlayletDataStatistics::$redisKey, json_encode($value, JSON_UNESCAPED_UNICODE));
  546. # 短剧数据
  547. // PlayletTrendDataStatistics::updateByOperate($dramaId, $date);
  548. $date = date('Y-m-d', strtotime($date.' +1 day'));
  549. }
  550. # 短剧数据趋势
  551. MpPlayletDataTrendStatistics::updateByOperate($dramaId, $startDate, $endDate);
  552. MpPlayletAccountDataTrendStatistics::updateByOperate($dramaId, $startDate, $endDate);
  553. } catch (\Exception $exception) {
  554. Log::logError('mpRelaAdd 添加关联关系同步短剧离线数据 request:'.json_encode($requestData, JSON_UNESCAPED_UNICODE), [
  555. 'file' => $exception->getFile(),
  556. 'line' => $exception->getLine(),
  557. 'message' => $exception->getMessage(),
  558. 'trace' => $exception->getTraceAsString(),
  559. ], 'interface');
  560. }
  561. }
  562. // 编辑绑定关系
  563. public static function mpRelaEdit($dramaId, $userId, $startDate, $endDate, $enable, $old)
  564. {
  565. $requestData = [
  566. 'drama_id' => $dramaId,
  567. 'user_id' => $userId,
  568. 'start_date' => $startDate,
  569. 'end_date' => $endDate,
  570. 'enable' => $enable,
  571. 'old' => json_encode($old, JSON_UNESCAPED_UNICODE),
  572. ];
  573. try{
  574. // 判断修改的内容是什么
  575. if($enable != $old['enable']) {
  576. // 优先判断是否禁用或者启用了
  577. if(0 == $enable) {
  578. // 禁用了,将对应公众号日期范围内的数据删除
  579. PlayletTrendStatistics::query()
  580. ->where('app_id', $old['app_id'])
  581. ->where('ref_date', '>=', $old['start_date'])
  582. ->where('ref_date', '<=', $old['end_date'])
  583. ->update(['enable' => 0]);
  584. } else {
  585. // 启用,按照新增的逻辑重新跑一遍数据
  586. if($endDate >= date('Y-m-d')) {
  587. $endDate = date('Y-m-d');
  588. }
  589. $date = $startDate;
  590. while($date <= $endDate) {
  591. $value['app_list'] = [$old['app_id']];
  592. $value['ref_date'] = $date;
  593. $value['playlet_id'] = $dramaId;
  594. $value['user_id'] = $userId; //投手标识
  595. $value['order_type'] = 1;
  596. # 投手数据
  597. RedisModel::lPush(PlayletDataStatistics::$redisKey, json_encode($value, JSON_UNESCAPED_UNICODE));
  598. # 短剧数据
  599. // PlayletTrendDataStatistics::updateByOperate($dramaId, $date);
  600. $date = date('Y-m-d', strtotime($date.' +1 day'));
  601. }
  602. }
  603. }
  604. // 在禁用的情况下修改数据,不做处理
  605. if($enable == $old['enable'] && 1 == $enable) {
  606. // 将原来数据中多余日期的数据状态更新
  607. PlayletTrendStatistics::query()
  608. ->where('app_id', $old['app_id'])
  609. ->where('ref_date', '>=', $old['start_date'])
  610. ->where('ref_date', '<=', $old['end_date'])
  611. ->where(function($query) use ($endDate, $startDate){
  612. $query->where('ref_date', '>', $endDate)->orWhere('ref_date', '<', $startDate);
  613. })
  614. ->update(['enable' => 0]);
  615. $date = $startDate;
  616. if($endDate >= date('Y-m-d')) {
  617. $endDate = date('Y-m-d');
  618. }
  619. while($date <= $endDate) {
  620. $value['app_list'] = [$old['app_id']];
  621. $value['ref_date'] = $date;
  622. $value['playlet_id'] = $dramaId;
  623. $value['user_id'] = $userId; //投手标识
  624. $value['order_type'] = 1;
  625. # 投手数据
  626. RedisModel::lPush(PlayletDataStatistics::$redisKey, json_encode($value, JSON_UNESCAPED_UNICODE));
  627. # 短剧数据
  628. // PlayletTrendDataStatistics::updateByOperate($dramaId, $date);
  629. $date = date('Y-m-d', strtotime($date.' +1 day'));
  630. }
  631. }
  632. if($enable != $old['enable'] || $startDate != $old['start_date'] || $endDate != $old['end_date']){
  633. $startDate = (($startDate < $old['start_date']) ? $startDate : $old['start_date']);
  634. $endDate = (($endDate > $old['end_date']) ? $endDate : $old['end_date']);
  635. # 短剧数据趋势
  636. MpPlayletDataTrendStatistics::updateByOperate($dramaId, $startDate, $endDate);
  637. MpPlayletAccountDataTrendStatistics::updateByOperate($dramaId, $startDate, $endDate);
  638. }
  639. } catch (\Exception $exception) {
  640. Log::logError('mpRelaEdit 编辑关联关系同步短剧离线数据 request:'.json_encode($requestData, JSON_UNESCAPED_UNICODE), [
  641. 'file' => $exception->getFile(),
  642. 'line' => $exception->getLine(),
  643. 'message' => $exception->getMessage(),
  644. 'trace' => $exception->getTraceAsString(),
  645. ], 'interface');
  646. }
  647. }
  648. // 新增绑定关系
  649. public static function adqRelaAdd($accountId, $dramaId, $userId, $startDate, $endDate)
  650. {
  651. $requestData = [
  652. 'account_id' => $accountId,
  653. 'drama_id' => $dramaId,
  654. 'user_id' => $userId,
  655. 'start_date' => $startDate,
  656. 'end_date' => $endDate
  657. ];
  658. try{
  659. $date = $startDate;
  660. if($endDate >= date('Y-m-d')) {
  661. $endDate = date('Y-m-d');
  662. }
  663. while($date <= $endDate) {
  664. $value['account_list'] = [$accountId];
  665. $value['ref_date'] = $date;
  666. $value['playlet_id'] = $dramaId;
  667. $value['user_id'] = $userId; //投手标识
  668. # 投手数据
  669. RedisModel::lPush(PlayletDataStatistics::$redisKey, json_encode($value, JSON_UNESCAPED_UNICODE));
  670. # 短剧数据
  671. //PlayletTrendDataStatistics::updateByOperate($dramaId, $date);
  672. # 新版公众号数据 start
  673. // 查询投放账号在当天绑定的公众号ID
  674. $appIdList = OperateDataTrend::getAppIdList($accountId, $date);
  675. if(!empty($appIdList)) {
  676. RedisModel::lPush(OperateDataTrend::UPDATE_OPEATE_DATA_PITCHER, json_encode([
  677. 'app_list' => $appIdList,
  678. 'expense_date' => $date,
  679. ]));
  680. }
  681. # 新版公众号数据 end
  682. $date = date('Y-m-d', strtotime($date.' +1 day'));
  683. }
  684. # 短剧数据趋势
  685. AdqPlayletDataTrendStatistics::updateByOperate($dramaId, $startDate, $endDate);
  686. AdqPlayletAccountDataTrendStatistics::updateByOperate($dramaId, $startDate, $endDate);
  687. } catch (\Exception $exception) {
  688. Log::logError('adqRelaAdd 添加关联关系同步短剧离线数据 request:'.json_encode($requestData, JSON_UNESCAPED_UNICODE), [
  689. 'file' => $exception->getFile(),
  690. 'line' => $exception->getLine(),
  691. 'message' => $exception->getMessage(),
  692. 'trace' => $exception->getTraceAsString(),
  693. ], 'interface');
  694. }
  695. }
  696. // 编辑绑定关系
  697. public static function adqRelaEdit($dramaId, $userId, $startDate, $endDate, $enable, $old)
  698. {
  699. $requestData = [
  700. 'drama_id' => $dramaId,
  701. 'user_id' => $userId,
  702. 'start_date' => $startDate,
  703. 'end_date' => $endDate,
  704. 'enable' => $enable,
  705. 'old' => json_encode($old, JSON_UNESCAPED_UNICODE),
  706. ];
  707. try{
  708. // 判断修改的内容是什么
  709. if($enable != $old['enable']) {
  710. // 优先判断是否禁用或者启用了
  711. if(0 == $enable) {
  712. // 禁用了,将对应公众号日期范围内的数据删除
  713. PlayletTrendStatistics::query()
  714. ->where('app_id', $old['account_id'])
  715. ->where('ref_date', '>=', $old['start_date'])
  716. ->where('ref_date', '<=', $old['end_date'])
  717. ->update(['enable' => 0]);
  718. } else {
  719. // 启用,按照新增的逻辑重新跑一遍数据
  720. if($endDate >= date('Y-m-d')) {
  721. $endDate = date('Y-m-d');
  722. }
  723. $date = $startDate;
  724. while($date <= $endDate) {
  725. $value['account_list'] = [$old['account_id']];
  726. $value['ref_date'] = $date;
  727. $value['playlet_id'] = $dramaId;
  728. $value['user_id'] = $userId; //投手标识
  729. # 投手数据
  730. RedisModel::lPush(PlayletDataStatistics::$redisKey, json_encode($value, JSON_UNESCAPED_UNICODE));
  731. # 短剧数据
  732. //PlayletTrendDataStatistics::updateByOperate($dramaId, $date);
  733. $date = date('Y-m-d', strtotime($date.' +1 day'));
  734. }
  735. }
  736. }
  737. // 在禁用的情况下修改数据,不做处理
  738. if($enable == $old['enable'] && 1 == $enable) {
  739. // 将原来数据中多余日期的数据状态更新
  740. PlayletTrendStatistics::query()
  741. ->where('app_id', $old['account_id'])
  742. ->where('ref_date', '>=', $old['start_date'])
  743. ->where('ref_date', '<=', $old['end_date'])
  744. ->where(function($query) use ($endDate, $startDate){
  745. $query->where('ref_date', '>', $endDate)->orWhere('ref_date', '<', $startDate);
  746. })
  747. ->update(['enable' => 0]);
  748. $date = $startDate;
  749. if($endDate >= date('Y-m-d')) {
  750. $endDate = date('Y-m-d');
  751. }
  752. while($date <= $endDate) {
  753. $value['account_list'] = [$old['account_id']];
  754. $value['ref_date'] = $date;
  755. $value['playlet_id'] = $dramaId;
  756. $value['user_id'] = $userId; //投手标识
  757. $value['order_type'] = 2;
  758. # 投手数据
  759. RedisModel::lPush(PlayletDataStatistics::$redisKey, json_encode($value, JSON_UNESCAPED_UNICODE));
  760. # 短剧数据
  761. //PlayletTrendDataStatistics::updateByOperate($dramaId, $date);
  762. $date = date('Y-m-d', strtotime($date.' +1 day'));
  763. }
  764. }
  765. if($enable != $old['enable'] || $startDate != $old['start_date'] || $endDate != $old['end_date']){
  766. # 取最大范围日期
  767. $startDate = (($startDate < $old['start_date']) ? $startDate : $old['start_date']);
  768. $endDate = (($endDate > $old['end_date']) ? $endDate : $old['end_date']);
  769. # 短剧数据趋势
  770. AdqPlayletDataTrendStatistics::updateByOperate($dramaId, $startDate, $endDate);
  771. AdqPlayletAccountDataTrendStatistics::updateByOperate($dramaId, $startDate, $endDate);
  772. $date = $startDate;
  773. while($date <= $endDate) {
  774. $value['account_list'] = [$old['account_id']];
  775. $value['ref_date'] = $date;
  776. $value['playlet_id'] = $dramaId;
  777. $value['user_id'] = $userId; //投手标识
  778. $value['order_type'] = 2;
  779. # 新版公众号数据 start
  780. // 查询投放账号在当天绑定的公众号ID
  781. $appIdList = OperateDataTrend::getAppIdList($old['account_id'], $date);
  782. if(!empty($appIdList)) {
  783. RedisModel::lPush(OperateDataTrend::UPDATE_OPEATE_DATA_PITCHER, json_encode([
  784. 'app_list' => $appIdList,
  785. 'expense_date' => $date,
  786. ]));
  787. }
  788. # 新版公众号数据 end
  789. $date = date('Y-m-d', strtotime($date.' +1 day'));
  790. }
  791. }
  792. } catch (\Exception $exception) {
  793. Log::logError('adqRelaEdit 编辑关联关系同步短剧离线数据 request:'.json_encode($requestData, JSON_UNESCAPED_UNICODE), [
  794. 'file' => $exception->getFile(),
  795. 'line' => $exception->getLine(),
  796. 'message' => $exception->getMessage(),
  797. 'trace' => $exception->getTraceAsString(),
  798. ], 'interface');
  799. }
  800. }
  801. public static function chargeDataSummary($totalChargeData, $chargeData, $firstDayRoi)
  802. {
  803. $newChargeData = [];
  804. for($i = 0;$i < PlayletTrendStatistics::DAYS;$i++) {
  805. if (isset($newChargeData[$i])) {
  806. //已存在,累加
  807. $newChargeData[$i]['day_charge'] = isset($chargeData[$i]['day_charge']) ? $totalChargeData[$i]['day_charge'] + $chargeData[$i]['day_charge'] : 0;
  808. $newChargeData[$i]['charge_total'] = isset($chargeData[$i]['charge_total']) ? $totalChargeData[$i]['charge_total'] + $chargeData[$i]['charge_total'] : 0;
  809. $newChargeData[$i]['day_paid'] = isset($chargeData[$i]['day_paid']) ? $totalChargeData[$i]['day_paid'] + $chargeData[$i]['day_paid'] : 0;
  810. } else {
  811. //不存在,赋值
  812. $newChargeData[$i]['day_charge'] = isset($chargeData[$i]['day_charge']) ? $chargeData[$i]['day_charge'] : 0;
  813. $newChargeData[$i]['charge_total'] = isset($chargeData[$i]['charge_total']) ? $chargeData[$i]['charge_total'] : 0;
  814. $newChargeData[$i]['day_paid'] = isset($chargeData[$i]['day_paid']) ? $chargeData[$i]['day_paid'] : 0;
  815. }
  816. $newChargeData[$i]['day_add'] = (isset($newChargeData[$i]['day_paid']) && $newChargeData[$i]['day_paid'] != 0) ? round($newChargeData[$i]['day_charge'] / $newChargeData[$i]['day_paid'], 4) : 0;
  817. $newChargeData[$i]['day_cover'] = (isset($newChargeData[$i]['day_paid']) && $newChargeData[$i]['day_paid'] != 0) ? round($newChargeData[$i]['charge_total'] / $newChargeData[$i]['day_paid'], 4) : 0;
  818. $newChargeData[$i]['day_times'] = (isset($chargeData[$i]['day_paid']) && ($chargeData[$i]['day_paid'] * $firstDayRoi) != 0) ? ($newChargeData[$i]['charge_total'] / $chargeData[$i]['day_paid'] * $firstDayRoi) : 0;
  819. }
  820. return $newChargeData;
  821. }
  822. public static function chargeDataFormat($chargeData)
  823. {
  824. $dataFormat = [];
  825. for($i = 0;$i < PlayletTrendStatistics::DAYS;$i++) {
  826. $title = 'day'.($i+1);
  827. $data = isset($chargeData[$i]) ? $chargeData[$i] : [];
  828. if(!empty($data)) {
  829. $data['day_add'] = !empty($data['day_add']) ? round($data['day_add'], 4) * 100 . '%' : '0%';
  830. $data['day_cover'] = !empty($data['day_cover']) ? round($data['day_cover'], 4) * 100 . '%' : '0%';
  831. }
  832. $dataFormat[$title] = $data;
  833. }
  834. return $dataFormat;
  835. }
  836. public static function chargeAfterSubscribed($subscribedAt, $days, $accountId, $roi, $paid, $orderType = 1)
  837. {
  838. # 校验是否是日期以及$days的合法性
  839. if((date("Y-m-d", strtotime($subscribedAt)) != $subscribedAt) || !is_int($days)) {
  840. Log::logError('日期格式非法',[
  841. 'subscribedAt' => $subscribedAt,
  842. 'days' => $days,
  843. 'accountId' => $accountId
  844. ], 'ChargeAfterSubscribed');
  845. return [];
  846. }
  847. # 日期处理
  848. $expiryAt = date("Y-m-d", strtotime('+'.$days. ' days', strtotime($subscribedAt)));
  849. if($expiryAt >date("Y-m-d")) $expiryAt = date("Y-m-d");
  850. // $chargeData = RedisModel::getAfterDecode(self::CHARGE_PAID . $appId . '_' . $subscribedAt . '_' . $expiryAt);
  851. $chargeData = [];
  852. if(empty($chargeData)) {
  853. if(1 == $orderType) {
  854. # 充值数据
  855. $data = DjOrder::getChargeAfterSubscribed($accountId, $subscribedAt, $expiryAt, $subscribedAt);
  856. } else {
  857. # 充值数据
  858. $data = DjOrder::getAdqChargeAfterSubscribed($accountId, $subscribedAt, $expiryAt, $subscribedAt);
  859. }
  860. # 获取后N天每天的充值数据及截止第N天的统计数据
  861. $chargeData = [];
  862. $chargePriceTotal = 0;
  863. for($i=0; $i<$days; $i++) {
  864. $refDate = date("Y-m-d", strtotime('+'.$i. ' days', strtotime($subscribedAt)));
  865. if($refDate>date("Y-m-d")) {
  866. $chargeData[$i] = [];
  867. continue;
  868. }
  869. # 第N天的充值数据
  870. $chargePrice = $data->where('paid_at', '>=', strtotime($refDate . ' 00:00:00') * 1000)
  871. ->where('paid_at', '<=', strtotime($refDate . ' 23:59:59') * 1000)
  872. ->sum('price');
  873. $chargePrice = $chargePrice / 10000;
  874. $chargePriceTotal += $chargePrice;
  875. $chargeData[$i] = [
  876. 'day_charge' => $chargePrice,
  877. 'charge_total' => $chargePriceTotal,
  878. 'day_paid' => $paid,
  879. 'day_add' => $paid != 0 ? ($chargePrice / $paid) : 0,
  880. 'day_cover' => $paid != 0 ? ($chargePriceTotal / $paid) : 0,
  881. 'day_times' => ($paid * $roi) != 0 ? ($chargePriceTotal / ($paid * $roi)) : 0
  882. ];
  883. }
  884. }
  885. return $chargeData;
  886. }
  887. public static function playletDataTrend($startDate, $endDate, $playletId, $page, $pageSize, $sysGroupId)
  888. {
  889. $playletList = DramaSeries::getDramaSeriesList($sysGroupId);
  890. $playletIdList = $playletList->pluck('id')->toArray();
  891. $offset = ($page - 1) * $pageSize;
  892. $playletList = Statistics::query()
  893. ->where(function($query) use ($playletId, $startDate, $endDate) {
  894. if($playletId) $query->where('playlet_id', $playletId);
  895. if($startDate) $query->where('ref_date', '>=', $startDate);
  896. if($endDate) $query->where('ref_date', '<=', $endDate);
  897. })
  898. ->where('enable', 1)
  899. ->whereIn('playlet_id', $playletIdList)
  900. ->orderBy('ref_date', 'desc')
  901. ->offset($offset)
  902. ->limit($pageSize)
  903. ->get();
  904. $playletCount = Statistics::query()
  905. ->where(function($query) use ($playletId, $startDate, $endDate) {
  906. if($playletId) $query->where('playlet_id', $playletId);
  907. if($startDate) $query->where('ref_date', '>=', $startDate);
  908. if($endDate) $query->where('ref_date', '<=', $endDate);
  909. })
  910. ->whereIn('playlet_id', $playletIdList)
  911. ->where('enable', 1)
  912. ->count();
  913. // 提取短剧id
  914. $playletIdList = array_column($playletList->toArray(), 'playlet_id');
  915. $dramaSeries = DramaSeries::getDramaSeriesList($sysGroupId);
  916. $dramaSeriesList = $dramaSeries->whereIn('id', $playletIdList)
  917. ->keyBy('id')
  918. ->toArray();
  919. foreach($playletList as $playlet) {
  920. // 统计数据格式化
  921. $playlet->charge_data = DataStatisticsService::chargeDataFormat(json_decode($playlet->charge_data, 1));
  922. // 拼接短剧名称
  923. $playlet->playlet_name = isset($dramaSeriesList[$playlet->playlet_id]['name']) ?
  924. $dramaSeriesList[$playlet->playlet_id]['name'] : '';
  925. $playlet->first_day_roi = round($playlet->first_day_roi * 100, 2) . '%';
  926. $playlet->cost_cover_rate = round($playlet->cost_cover_rate * 100, 2) . '%';
  927. // 充值用户成本(去重用户)
  928. $playlet->charge_user_cost_unique = $playlet->new_user_charge_uv_unique > 0 ?
  929. round(($playlet->day_paid / $playlet->new_user_charge_uv_unique), 2) : 0;
  930. }
  931. // 计算总概数据
  932. $overview = DataStatisticsService::getPlayletDataTrendOverview($playletId, $startDate, $endDate, $sysGroupId);
  933. // 获取day1至day100表头
  934. $extra = [];
  935. for($i = 0;$i < PlayletTrendStatistics::DAYS;$i++) {
  936. $title = 'day'.($i+1);
  937. $extra[] = $title;
  938. }
  939. return [['list' => $playletList, 'overview' => $overview, 'extra' => $extra], $playletCount];
  940. }
  941. public static function getPlayletDataTrendOverview($playletId, $startDate, $endDate, $sysGroupId)
  942. {
  943. $playletList = DramaSeries::getDramaSeriesList($sysGroupId);
  944. $playletIdList = $playletList->pluck('id')->toArray();
  945. # 按条件查询小说基础数据
  946. $playletData = Statistics::query()
  947. ->where(function($query) use ($playletId, $startDate, $endDate) {
  948. if($playletId) $query->where('playlet_id', $playletId);
  949. if($startDate) $query->where('ref_date', '>=', $startDate);
  950. if($endDate) $query->where('ref_date', '<=', $endDate);
  951. })
  952. ->whereIn('playlet_id', $playletIdList)
  953. ->get();
  954. if(empty($playletData)) {
  955. return [];
  956. }
  957. $playletData = $playletData->toArray();
  958. # 循环累加 因为首日roi无法通过sum求和计算
  959. $data = [
  960. 'subscribe_date' => '',
  961. 'day_paid' => 0,
  962. 'charge_total' => 0,
  963. 'fans_increase' => 0,
  964. 'new_user_charge_uv' => 0,
  965. 'new_user_charge_pv' => 0,
  966. 'new_user_charge_uv_count' => 0,
  967. 'new_user_charge_pv_count' => 0,
  968. 'first_day_roi' => 0,
  969. 'cost_cover_rate' => 0,
  970. 'new_user_cost' => 0,
  971. 'charge_user_cost' => 0,
  972. ];
  973. $realStartDate = null;
  974. $realEndDate = null;
  975. foreach($playletData as $playlet) {
  976. $item = isset($item) ? $item : ['day_charge' => 0, 'charge_total' => 0, 'day_paid' => 0];
  977. // $data['subscribe_date'] = $startDate.'~'.$endDate;
  978. // if(empty($realStartDate) || (!empty($realStartDate) && ($realStartDate > $playlet['ref_date']))) {
  979. // $realStartDate = $playlet['ref_date'];
  980. // }
  981. // if(empty($realEndDate) || (!empty($realEndDate) && ($playlet['ref_date'] > $realEndDate))) {
  982. // $realEndDate = $playlet['ref_date'];
  983. // }
  984. $data['day_paid'] = isset($data['day_paid']) ? $data['day_paid'] + $playlet['day_paid'] : 0;
  985. $data['charge_total'] = isset($data['charge_total']) ? round(($data['charge_total'] + $playlet['charge_total']), 2) : 0;
  986. $data['fans_increase'] = isset($data['fans_increase']) ? $data['fans_increase'] + $playlet['fans_increase'] : 0;
  987. $data['new_user_charge_uv'] = isset($data['new_user_charge_uv']) ? $data['new_user_charge_uv'] + $playlet['new_user_charge_uv'] : 0;
  988. $data['new_user_charge_pv'] = isset($data['new_user_charge_pv']) ? $data['new_user_charge_pv'] + $playlet['new_user_charge_pv'] : 0;
  989. $data['new_user_charge_uv_count'] = isset($data['new_user_charge_uv_count']) ? $data['new_user_charge_uv_count'] + $playlet['new_user_charge_uv_count'] : 0;
  990. $data['new_user_charge_pv_count'] = isset($data['new_user_charge_pv_count']) ? $data['new_user_charge_pv_count'] + $playlet['new_user_charge_pv_count'] : 0;
  991. $data['cost_cover_rate'] = $data['day_paid'] != 0 ? round($data['charge_total'] / $data['day_paid'], 4) * 100 . '%' : '0%';
  992. $data['new_user_cost'] = $data['fans_increase'] != 0 ? round($data['day_paid'] / $data['fans_increase'], 2) : 0;
  993. $data['charge_user_cost'] = $data['new_user_charge_uv_count'] != 0 ? round($data['day_paid'] / $data['new_user_charge_uv_count'], 2) : 0;
  994. $data['new_user_charge_cost'] = $data['new_user_charge_uv'] != 0 ? round($data['day_paid'] / $data['new_user_charge_uv'], 2) : 0;
  995. $chargeData = json_decode($playlet['charge_data'], 1);
  996. $data['new_user_charge'] = 0;
  997. foreach($chargeData as $key => $charge) {
  998. if(0 == $key) {
  999. $item['day_charge'] = isset($charge['day_charge']) ? $item['day_charge'] + $charge['day_charge'] : $item['day_charge'];
  1000. $item['charge_total'] = isset($charge['charge_total']) ? $item['charge_total'] + $charge['charge_total'] : $item['charge_total'];
  1001. $item['day_paid'] = isset($charge['day_paid']) ? $item['day_paid'] + $charge['day_paid'] : $item['day_paid'];
  1002. $data['first_day_roi'] = isset($item['day_paid']) && isset($item['day_charge']) && $item['day_paid'] != 0 ? round($item['day_charge'] / $item['day_paid'], 4) * 100 . '%' : '0%';
  1003. $data['new_user_charge'] += $item['day_charge'];
  1004. }
  1005. }
  1006. $data['new_user_charge'] = round($data['new_user_charge'], 2);
  1007. }
  1008. return $data;
  1009. }
  1010. /**
  1011. * 粉丝激活趋势
  1012. * @param $startDate string 查询起始日期
  1013. * @param $endDate string 查询截止日期
  1014. * @param $closingDate string 收益截止日期
  1015. * @param $accountId string 公众号appid
  1016. * @param $minPaid integer 最低消耗
  1017. * @param $maxPaid integer 最高消耗
  1018. * @param $page integer 当前页码数
  1019. * @param $pageSize integer 每页显示条数
  1020. * @param $sysGroupId integer 系统权限组id
  1021. * @return mixed
  1022. * */
  1023. public static function activeFansData($startDate, $endDate, $closingDate, $accountId, $minPaid, $maxPaid, $page,
  1024. $pageSize, $sysGroupId)
  1025. {
  1026. $minPaid = $minPaid * 100;
  1027. $maxPaid = $maxPaid * 100;
  1028. $mpAppIdList = OfficialAccount::getSysGroupMpAppIdList($sysGroupId, 'DataStatisticsService.activeFansData');
  1029. # 获取日期范围内充值信息
  1030. list($activateData, $total) = ActiveFansData::getActiveFansTrend(
  1031. $startDate, $endDate, $closingDate, $accountId, $minPaid, $maxPaid, $page, $pageSize, $mpAppIdList
  1032. );
  1033. if(empty($activateData))
  1034. return [[], 0];
  1035. # 公众号数据获取
  1036. $appIds = array_unique($activateData->pluck('app_id')->toArray());
  1037. $accountList = OfficialAccount::getAccountListCondition($appIds);
  1038. # 获取列表内的日期
  1039. $dateList = array_unique($activateData->pluck('expense_date')->toArray());
  1040. # 获取各日期对应的花费
  1041. // $paidList = TencentAdDailyReport::getPaidData($appIds, $dateList);
  1042. # 获取激活趋势数据
  1043. $activeDataList = ActiveFansData::select(['app_id', 'expense_date', 'ref_date', 'active_fans'])
  1044. ->where('enable', 1)->whereIn('expense_date', $dateList)
  1045. ->whereIn('app_id', $appIds)->get();
  1046. foreach($activateData as $paid) {
  1047. # 当日新用户累计充值金额、充值人数
  1048. // $newUserCharge = DjOrder::getNewUserCharge($paid->expense_date, $paid->app_id);
  1049. // $paid->new_user_charge_uv = isset($newUserCharge->new_user_charge_uv) ? $newUserCharge->new_user_charge_uv : null;
  1050. # 新用户累计充值金额
  1051. // $paid->charge_total = isset($newUserCharge->charge_total) ? $newUserCharge->charge_total / 10000 : null;
  1052. $paid->charge_total = $paid->charge_total / 100;
  1053. # 公众号信息
  1054. $accountInfo = $accountList->where('mp_app_id', $paid->app_id)->first();
  1055. $paid->account_name = isset($accountInfo->mp_name) ? $accountInfo->mp_name : null;
  1056. # 当日消耗
  1057. // $paidInfo = $paidList->where('app_id', $paid->app_id)
  1058. // ->where('ref_date', $paid->expense_date)
  1059. // ->first();
  1060. $paid->day_paid = $paid->paid / 100;
  1061. # 回本率
  1062. $paid->cost_cover_rate = $paid->day_paid ? (round($paid->charge_total / $paid->day_paid, 4) * 100) . '%' : 0;
  1063. # 企微关注数
  1064. $paid->scan_follow_count = isset($paidInfo->scan_follow_count) ? $paidInfo->scan_follow_count : 0;
  1065. # 企微关注成本
  1066. $paid->follow_cost = $paid->scan_follow_count > 0 ? round($paid->day_paid / $paid->scan_follow_count, 3) : 0;
  1067. # 首日用户成本
  1068. $activeFirstDay = ActiveFansData::where('expense_date', $paid->expense_date)->where('ref_date', $paid->expense_date)->where('app_id', $paid->app_id)->value('active_fans');
  1069. $paid->charge_user_cost = $activeFirstDay ? ($paid->day_paid / $activeFirstDay) : 0;
  1070. # 累计用户成本
  1071. $paid->charge_user_cost_all = $paid->new_user_charge_uv ? ($paid->day_paid / $paid->new_user_charge_uv) : 0;
  1072. $trendData = [];
  1073. $activeFansTotal = 0;
  1074. $activateList = $activeDataList->where('expense_date', $paid->expense_date)->where('app_id', $paid->app_id);
  1075. for($i = 0;$i < ActiveFansData::ACTIVATE_FANS_RECORD_DAYS;$i++) {
  1076. $statDate = date("Y-m-d", strtotime('+'.$i. ' days', strtotime($paid->expense_date)));
  1077. if($statDate>date("Y-m-d")) {
  1078. $trendData[$i] = [];
  1079. continue;
  1080. }
  1081. $activeInfo = $activateList->where('ref_date', $statDate)->first();
  1082. // if($paid->app_id == 'wx080137b49399f0ed'){
  1083. // Log::logInfo('粉丝激活趋势数据', [
  1084. // 'date' => $statDate,
  1085. // 'info' => $activeInfo,
  1086. // 'list' => $activateList->all()
  1087. // ], '0516');
  1088. // }
  1089. # 激活人数
  1090. $activeUserCount = isset($activeInfo->active_fans) ? $activeInfo->active_fans : null;
  1091. $activeFansTotal += $activeUserCount;
  1092. # 成本
  1093. $cost = $activeFansTotal ? round($paid->day_paid / $activeFansTotal, 2) : 0;
  1094. $trendData[$i] = [
  1095. 'date' => $statDate,
  1096. 'fans' => $activeUserCount,
  1097. 'cost' => $cost,
  1098. 'rate' => $paid->new_user_charge_uv ? (round($activeUserCount / $paid->new_user_charge_uv, 4) * 100) . '%' : null
  1099. ];
  1100. }
  1101. $paid->active_trend = $trendData;
  1102. }
  1103. // 获取day1至day100表头
  1104. $extra = [];
  1105. for($i = 0;$i < ActiveFansData::ACTIVATE_FANS_RECORD_DAYS;$i++) {
  1106. $title = 'day'.($i+1);
  1107. $extra[] = $title;
  1108. }
  1109. return [['list' => $activateData, 'extra' => $extra], $total];
  1110. }
  1111. /**
  1112. * 公众号数据(新版)
  1113. * @param $startDate string 查询起始日期
  1114. * @param $endDate string 查询截止日期
  1115. * @param $accountId string 公众号app_id
  1116. * @param $sortField string 排序字段
  1117. * @param $page int 页码
  1118. * @param $pageSize int 每页展示数据条数
  1119. * @param $sysGroupId integer 系统权限组id
  1120. * @return array
  1121. */
  1122. public static function OfficialAccountData($startDate, $endDate, $accountId, $sortField, $page, $pageSize, $sysGroupId)
  1123. {
  1124. $requestData = [
  1125. 'start_date' => $startDate,
  1126. 'end_date' => $endDate,
  1127. 'account_id' => $accountId,
  1128. 'sort_field' => $sortField,
  1129. 'page' => $page,
  1130. 'page_size' => $pageSize,
  1131. 'sys_group_id' => $sysGroupId,
  1132. ];
  1133. // 获取day1至day100表头
  1134. $extra = [];
  1135. for($i = 0;$i < PlayletTrendStatistics::DAYS;$i++) {
  1136. $title = 'day'.($i+1);
  1137. $extra[] = $title;
  1138. }
  1139. try{
  1140. $mpAppIdList = OfficialAccount::getSysGroupMpAppIdList($sysGroupId, 'DataStatisticsService.OfficialAccountData');
  1141. if(empty($mpAppIdList)) {
  1142. Log::logError('DataStatisticsService.OfficialAccountData', [
  1143. 'params' => $requestData,
  1144. 'err_msg' => '当前账号公众号权限为空',
  1145. ], 'interface');
  1146. return [['list' => [], 'overview' => [], 'extra' => $extra], 0];
  1147. }
  1148. $offset = ($page - 1) * $pageSize;
  1149. $appDataList = OfficialAccountData::query()
  1150. ->select(['ref_date', 'app_id', 'day_paid', 'first_day_roi', 'charge_total', 'cost_cover_rate', 'fans_increase',
  1151. 'new_user_cost', 'new_user_charge_uv', 'new_user_charge_pv', 'new_user_charge_uv_count', 'new_user_charge_pv_count',
  1152. 'charge_user_cost', 'other_pay_uv', 'other_pay_money', 'charge_data', 'first_day_charge'])
  1153. ->where(function($query) use ($accountId, $startDate, $endDate) {
  1154. if($accountId) $query->where('app_id', $accountId);
  1155. if($startDate) $query->where('ref_date', '>=', $startDate);
  1156. if($endDate) $query->where('ref_date', '<=', $endDate);
  1157. })
  1158. ->where('enable', 1)
  1159. ->whereIn('app_id', $mpAppIdList)
  1160. ->orderBy('ref_date', 'desc')
  1161. ->offset($offset)
  1162. ->limit($pageSize)
  1163. ->get();
  1164. $appDataCount = OfficialAccountData::query()
  1165. ->where(function($query) use ($accountId, $startDate, $endDate) {
  1166. if($accountId) $query->where('app_id', $accountId);
  1167. if($startDate) $query->where('ref_date', '>=', $startDate);
  1168. if($endDate) $query->where('ref_date', '<=', $endDate);
  1169. })
  1170. ->whereIn('app_id', $mpAppIdList)
  1171. ->where('enable', 1)
  1172. ->count();
  1173. // 提取短剧id
  1174. $appIdList = array_column($appDataList->toArray(), 'app_id');
  1175. $appData = OfficialAccount::query()
  1176. ->where('enable', 1)
  1177. ->whereIn('mp_app_id', $appIdList)
  1178. ->get();
  1179. foreach($appDataList as $app) {
  1180. // 统计数据格式化
  1181. $app->charge_data = DataStatisticsService::chargeDataFormat(json_decode($app->charge_data, 1));
  1182. // 拼接公众号名称
  1183. $appInfo = $appData->where('mp_app_id', $app->app_id)->first();
  1184. $app->app_name = isset($appInfo->mp_name) ? $appInfo->mp_name : null;
  1185. $app->first_day_roi = round($app->first_day_roi * 100, 2) . '%';
  1186. $app->cost_cover_rate = round($app->cost_cover_rate * 100, 2) . '%';
  1187. $app->new_charge_cost = ($app->new_user_charge_uv > 0) ? round($app->day_charge/$app->new_user_charge_uv, 2) : 0;
  1188. }
  1189. // 计算总概数据
  1190. $overview = DataStatisticsService::OfficialAccountDataOverview($accountId, $startDate, $endDate, $mpAppIdList);
  1191. return [['list' => $appDataList, 'overview' => $overview, 'extra' => $extra], $appDataCount];
  1192. } catch (\Exception $exception) {
  1193. Log::logError('DataStatisticsService.OfficialAccountData', [
  1194. 'params' => $requestData,
  1195. 'message' => $exception->getMessage(),
  1196. 'line' => $exception->getLine(),
  1197. 'trace' => $exception->getTraceAsString(),
  1198. ], 'interface');
  1199. EmailQueue::rPush('公众号数据', json_encode([
  1200. 'params' => $requestData,
  1201. 'message' => $exception->getMessage(),
  1202. 'line' => $exception->getLine(),
  1203. 'trace' => $exception->getTraceAsString(),
  1204. ]), ['song.shen@kuxuan-inc.com'], []);
  1205. return [['list' => [], 'overview' => [], 'extra' => $extra], 0];
  1206. }
  1207. }
  1208. public static function OfficialAccountDataOverview($accountId, $startDate, $endDate, $mpAppIdList)
  1209. {
  1210. # 按条件查询小说基础数据
  1211. $appData = OfficialAccountData::query()
  1212. ->where(function($query) use ($accountId, $startDate, $endDate) {
  1213. if($accountId) $query->where('app_id', $accountId);
  1214. if($startDate) $query->where('ref_date', '>=', $startDate);
  1215. if($endDate) $query->where('ref_date', '<=', $endDate);
  1216. })
  1217. ->whereIn('app_id', $mpAppIdList)
  1218. ->get();
  1219. if(empty($appData)) {
  1220. return [];
  1221. }
  1222. $appData = $appData->toArray();
  1223. # 循环累加 因为首日roi无法通过sum求和计算
  1224. $data = [
  1225. 'subscribe_date' => '',
  1226. 'day_paid' => 0,
  1227. 'charge_total' => 0,
  1228. 'fans_increase' => 0,
  1229. 'new_user_charge_uv' => 0,
  1230. 'new_user_charge_pv' => 0,
  1231. 'new_user_charge_uv_count' => 0,
  1232. 'new_user_charge_pv_count' => 0,
  1233. 'first_day_roi' => 0,
  1234. 'cost_cover_rate' => 0,
  1235. 'new_user_cost' => 0,
  1236. 'charge_user_cost' => 0,
  1237. 'first_day_charge' => 0,
  1238. ];
  1239. $realStartDate = null;
  1240. $realEndDate = null;
  1241. foreach($appData as $app) {
  1242. $item = isset($item) ? $item : ['day_charge' => 0, 'charge_total' => 0, 'day_paid' => 0];
  1243. $data['first_day_charge'] = isset($data['first_day_charge']) ? round($data['first_day_charge'] + $app['first_day_charge'], 2) : 0;
  1244. $data['day_paid'] = isset($data['day_paid']) ? $data['day_paid'] + $app['day_paid'] : $app['day_paid'];
  1245. $data['charge_total'] = isset($data['charge_total']) ? round(($data['charge_total'] + $app['charge_total']), 2) :
  1246. $app['charge_total'];
  1247. $data['fans_increase'] = isset($data['fans_increase']) ? $data['fans_increase'] + $app['fans_increase'] : $app['fans_increase'];
  1248. $data['new_user_charge_uv'] = isset($data['new_user_charge_uv']) ? $data['new_user_charge_uv'] + $app['new_user_charge_uv'] :
  1249. $app['new_user_charge_uv'];
  1250. $data['new_user_charge_pv'] = isset($data['new_user_charge_pv']) ? $data['new_user_charge_pv'] + $app['new_user_charge_pv'] :
  1251. $app['new_user_charge_pv'];
  1252. $data['new_user_charge_uv_count'] = isset($data['new_user_charge_uv_count']) ? $data['new_user_charge_uv_count'] + $app['new_user_charge_uv_count'] :
  1253. $app['new_user_charge_uv_count'];
  1254. $data['new_user_charge_pv_count'] = isset($data['new_user_charge_pv_count']) ? $data['new_user_charge_pv_count'] + $app['new_user_charge_pv_count'] :
  1255. $app['new_user_charge_pv_count'];
  1256. $data['cost_cover_rate'] = $data['day_paid'] != 0 ? round($data['charge_total'] / $data['day_paid'], 4) * 100 . '%' : '0%';
  1257. $data['new_user_cost'] = $data['fans_increase'] != 0 ? round($data['day_paid'] / $data['fans_increase'], 2) : 0;
  1258. $data['charge_user_cost'] = $data['new_user_charge_uv_count'] != 0 ? round($data['day_paid'] / $data['new_user_charge_uv_count'], 2) : 0;
  1259. $chargeData = json_decode($app['charge_data'], 1);
  1260. $charge = isset($chargeData[0]) ? $chargeData[0] : [];
  1261. $item['day_charge'] = isset($charge['day_charge']) ? $item['day_charge'] + $charge['day_charge'] : $item['day_charge'];
  1262. $item['charge_total'] = isset($charge['charge_total']) ? $item['charge_total'] + $charge['charge_total'] : $item['charge_total'];
  1263. $item['day_paid'] = isset($charge['day_paid']) ? $item['day_paid'] + $charge['day_paid'] : $item['day_paid'];
  1264. $data['first_day_roi'] = isset($item['day_paid']) && isset($item['day_charge']) && $item['day_paid'] != 0 ? round($item['day_charge'] / $item['day_paid'], 4) * 100 . '%' : '0%';
  1265. }
  1266. return $data;
  1267. }
  1268. /**
  1269. * 根据配置查询去重后的首日充值人数以及累计充值人数
  1270. * @param $startDate string 起始日期
  1271. * @param $endDate string 截止日期
  1272. * @param $playletId integer 剧集id
  1273. * @param $pitcherId integer 投手id
  1274. * @param $sysGroupId integer 系统权限组id
  1275. * @param $type int 查询类型
  1276. * @param $adminId int 当前登录人ID
  1277. * @param $isSystemAdmin int 是否为系统管理员
  1278. * @param $appId string 公众号app_id 非mp投放账号概念
  1279. * @param $launchAccountId int adq投放账号
  1280. * @return array
  1281. */
  1282. public static function statisticsDataUniqueUser ($startDate, $endDate, $playletId, $pitcherId, $sysGroupId, $type
  1283. , $adminId, $isSystemAdmin, $appId = null, $launchAccountId = null)
  1284. {
  1285. $data = [];
  1286. $responseData = [
  1287. 'start_date' => $startDate,
  1288. 'end_date' => $endDate,
  1289. 'playlet_id' => $playletId,
  1290. 'pitcher_id' => $pitcherId,
  1291. 'sys_group_id' => $sysGroupId,
  1292. 'type' => $type,
  1293. 'admin_id' => $adminId,
  1294. 'is_system_admin' => $isSystemAdmin,
  1295. 'app_id' => $appId,
  1296. 'launch_account_id' => $launchAccountId
  1297. ];
  1298. $mpAccountList = OfficialAccount::getAccountMpAppIdList($adminId, $sysGroupId, $isSystemAdmin);
  1299. $adqAccountList = PitcherService::adqAccountListForUser($adminId, $sysGroupId, $isSystemAdmin);
  1300. $launchAccountIdList = [];
  1301. if($launchAccountId) {
  1302. $launchAccountIdList = [$launchAccountId];
  1303. } else if($appId) {
  1304. $launchAccountIdList = AdqUser::getAccountIdList($appId);
  1305. }
  1306. try{
  1307. if(is_array($appId)) {
  1308. // 从缓存中读取数据
  1309. $redisKey = 'Playlet::statisticsDataUniqueUser-'.$startDate.'-'.$endDate.'-'.$playletId.'-'.$pitcherId.'-'
  1310. .$sysGroupId.'-'.$type.'-'.$adminId.'-'.$isSystemAdmin.'-'.json_encode($appId).'-'.$launchAccountId;
  1311. } else {
  1312. // 从缓存中读取数据
  1313. $redisKey = 'Playlet::statisticsDataUniqueUser-'.$startDate.'-'.$endDate.'-'.$playletId.'-'.$pitcherId.'-'
  1314. .$sysGroupId.'-'.$type.'-'.$adminId.'-'.$isSystemAdmin.'-'.$appId.'-'.$launchAccountId;
  1315. }
  1316. $redisData = RedisModel::get($redisKey);
  1317. if(!empty($redisData)) {
  1318. return json_decode($redisData, 1);
  1319. }
  1320. # 根据条件查询配置信息
  1321. $confList = DramaUserRela::query()
  1322. ->where('sys_group_id', $sysGroupId)
  1323. ->where('enable', 1)
  1324. ->where(function($query) use ($playletId, $pitcherId, $appId, $launchAccountId, $launchAccountIdList) {
  1325. if($playletId) $query->where('drama_id', $playletId);
  1326. if($pitcherId) $query->where('user_id', $pitcherId);
  1327. if($appId || $launchAccountId) $query->whereIn('account_id', $launchAccountIdList);
  1328. })->where(function ($query) use($mpAccountList, $adqAccountList) {
  1329. $query->whereIn('app_id', $mpAccountList)->orWhereIn('account_id', $adqAccountList);
  1330. })->where('start_date', '<=', $endDate)
  1331. ->where('end_date', '>=', $startDate)
  1332. ->get();
  1333. # 将查询出的配置信息整理
  1334. if($confList->isEmpty()) {
  1335. return $data;
  1336. }
  1337. $accountData = [];
  1338. $confList = $confList->toArray();
  1339. foreach($confList as $confInfo) {
  1340. $item['app_id'] = $confInfo['app_id'];
  1341. $item['account_id'] = $confInfo['account_id'];
  1342. if($confInfo['start_date'] < $startDate) {
  1343. $item['start_date'] = $startDate;
  1344. } else {
  1345. $item['start_date'] = $confInfo['start_date'];
  1346. }
  1347. if($confInfo['end_date'] > $endDate) {
  1348. $item['end_date'] = $endDate;
  1349. } else {
  1350. $item['end_date'] = $confInfo['end_date'];
  1351. }
  1352. $accountData[] = $item;
  1353. }
  1354. switch($type) {
  1355. case 1:
  1356. $data['new_user_charge_uv_unique'] = DjOrder::getFirstDayChargeUserNum($accountData);
  1357. break;
  1358. case 2:
  1359. $data['new_user_charge_uv_count_unique'] = DjOrderService::getChargeUserTotalUnique($accountData);
  1360. break;
  1361. case 3:
  1362. $data['new_user_charge_uv_unique'] = DjOrder::getFirstDayChargeUserNum($accountData);
  1363. $data['new_user_charge_uv_count_unique'] = DjOrderService::getChargeUserTotalUnique($accountData);
  1364. break;
  1365. default:
  1366. break;
  1367. }
  1368. } catch (\Exception $exception) {
  1369. Log::logError('DataStatisticsService.statisticsDataUniqueUser', [
  1370. 'params' => $responseData,
  1371. 'file' => $exception->getFile(),
  1372. 'line' => $exception->getLine(),
  1373. 'message' => $exception->getMessage(),
  1374. 'trace' => $exception->getTraceAsString(),
  1375. ], 'interface');
  1376. EmailQueue::rPush('短剧及投手数据查询去重后的充值人数异常', json_encode([
  1377. 'params' => $responseData,
  1378. 'file' => $exception->getFile(),
  1379. 'line' => $exception->getLine(),
  1380. 'message' => $exception->getMessage(),
  1381. 'trace' => $exception->getTraceAsString(),
  1382. ]), ['song.shen@kuxuan-inc.com'], []);
  1383. }
  1384. RedisModel::set($redisKey, json_encode($data));
  1385. RedisModel::expire($redisKey, 600);
  1386. return $data;
  1387. }
  1388. /*
  1389. * 数据循环统计
  1390. */
  1391. public static function dataCycleList($beginDate, $endDate, $page, $pageSize, $sysGroupId, $firstOrderCostMin,
  1392. $firstOrderCostMax, $firstOrderCostUniqueMin, $firstOrderCostUniqueMax, $perFollowCostMin, $perFollowCostMax,
  1393. $totalRoiMin, $totalRoiMax, $firstDayRoiMin, $firstDayRoiMax, $orderType, $platOrderType, $closingDate, $paidMin,
  1394. $paidMax, $sortField, $sortType)
  1395. {
  1396. $requestData = [
  1397. 'begin_date' => $beginDate,
  1398. 'end_date' => $endDate,
  1399. 'page' => $page,
  1400. 'page_size' => $pageSize,
  1401. 'sys_group_id' => $sysGroupId,
  1402. 'first_order_cost_min' => $firstOrderCostMin,
  1403. 'first_order_cost_max' => $firstOrderCostMax,
  1404. 'first_order_cost_unique_min' => $firstOrderCostUniqueMin,
  1405. 'first_order_cost_unique_max' => $firstOrderCostUniqueMax,
  1406. 'per_follow_cost_min' => $perFollowCostMin,
  1407. 'per_follow_cost_max' => $perFollowCostMax,
  1408. 'total_roi_min' => $totalRoiMin,
  1409. 'total_roi_max' => $totalRoiMax,
  1410. 'first_day_roi_min' => $firstDayRoiMin,
  1411. 'first_day_roi_max' => $firstDayRoiMax,
  1412. 'paid_min' => $paidMin,
  1413. 'paid_max' => $paidMax,
  1414. 'sort_field' => $sortField,
  1415. 'order_type' => $orderType,
  1416. 'plat_order_type' => $platOrderType,
  1417. 'closing_date' => $closingDate,
  1418. 'sort_type' => $sortType,
  1419. ];
  1420. $count = 0;
  1421. $overview = [];
  1422. $data = [];
  1423. try{
  1424. //补充头列表
  1425. $head = [
  1426. ['column' => 'date', 'name' => '用户注册时间', 'notes' => '', 'enable_to_sort' => true],
  1427. ['column' => 'advertiser_cost', 'name' => '投放消耗', 'notes' => '', 'enable_to_sort' => true],
  1428. ['column' => 'follow_uv', 'name' => '企微关注数', 'notes' => '', 'enable_to_sort' => true],
  1429. ['column' => 'per_follow_cost', 'name' => '企微关注成本', 'notes' => '', 'enable_to_sort' => true],
  1430. ['column' => 'first_order_cost', 'name' => '下单成本', 'notes' => '投放消耗/当日新增用户首单人数', 'enable_to_sort' => true],
  1431. ['column' => 'first_order_cost_unique', 'name' => '下单成本(去重)', 'notes' => '投放消耗/当日新增用户首单人数(去重)', 'enable_to_sort' => true],
  1432. ['column' => 'total_cvt_amt', 'name' => '总回收金额', 'notes' => '', 'enable_to_sort' => true],
  1433. ['column' => 'total_roi', 'name' => '总回收', 'notes' => '总回收金额/投放消耗', 'enable_to_sort' => true],
  1434. ];
  1435. $overviewHead = $listHead = $head;
  1436. foreach (DjRegUserRangeReport::count_range_days() as $day) {
  1437. $listHead[] = ['column'=>'day'.$day.'_roi' , 'name' => 'day'.$day , 'notes' => '', 'enable_to_sort' => false];
  1438. }
  1439. // 1mp 2adq
  1440. if(1 == $orderType) {
  1441. $query = DjDataCycleMp::query();
  1442. } else {
  1443. $query = DjDataCycleAdq::query();
  1444. }
  1445. $query->selectRaw('max(advertiser_cost) as advertiser_cost, max(total_cvt_amt) as total_cvt_amt, ' .
  1446. 'max(first_order_ucnt_unique) as first_order_ucnt_unique, max(first_day_roi) as first_day_roi, ' .
  1447. 'max(follow_uv) as follow_uv, max(first_order_ucnt) as first_order_ucnt, max(first_order_cost) ' .
  1448. 'as first_order_cost, max(first_order_active_cost) as first_order_cost_unique, max(per_follow_cost) ' .
  1449. ' as per_follow_cost, max(total_roi) as total_roi, expense_date as date, max(first_order_active_ucnt) ' .
  1450. " as first_order_active_ucnt");
  1451. // null 全选 0小程序 1H5
  1452. if(!is_null($platOrderType)) {
  1453. $query->where('plat_order_type', $platOrderType);
  1454. } else {
  1455. $query->where('plat_order_type', 2);
  1456. }
  1457. $query->where('sys_group_id', $sysGroupId)
  1458. ->where('enable', 1)
  1459. ->where('expense_date', '>=', $beginDate)
  1460. ->where('expense_date', '<=', $endDate);
  1461. if(!is_null($closingDate)) $query->where('ref_date', '<=', $closingDate);
  1462. if(!is_null($paidMin)) $query->where('advertiser_cost', '>=', $paidMin);
  1463. if(!is_null($paidMax)) $query->where('advertiser_cost', '<=', $paidMax);
  1464. if(!is_null($firstOrderCostMin)) $query->where('first_order_cost', '>=', $firstOrderCostMin);
  1465. if(!is_null($firstOrderCostMax)) $query->where('first_order_cost', '<=', $firstOrderCostMax);
  1466. if(!is_null($firstOrderCostUniqueMin)) $query->where('first_order_active_cost', '>=', $firstOrderCostUniqueMin);
  1467. if(!is_null($firstOrderCostUniqueMax)) $query->where('first_order_active_cost', '<=', $firstOrderCostUniqueMax);
  1468. if(!is_null($perFollowCostMin)) $query->where('per_follow_cost', '>=', $perFollowCostMin);
  1469. if(!is_null($perFollowCostMax)) $query->where('per_follow_cost', '<=', $perFollowCostMax);
  1470. if(!is_null($totalRoiMin)) $query->whereRaw('(total_roi*100) >= ' . $totalRoiMin);
  1471. if(!is_null($totalRoiMax)) $query->whereRaw('(total_roi*100) <=' . $totalRoiMax);
  1472. if(!is_null($firstDayRoiMin)) $query->whereRaw('(first_day_roi*100) >=' . $firstDayRoiMin);
  1473. if(!is_null($firstDayRoiMax)) $query->whereRaw('(first_day_roi*100) <=' . $firstDayRoiMax);
  1474. $query->groupBy(['expense_date']);
  1475. $countQuery = clone $query;
  1476. $count = $countQuery->get()->count();
  1477. $list = $query->orderBy($sortField, $sortType)
  1478. ->offset(($page - 1) * $pageSize)
  1479. ->limit($pageSize)
  1480. ->get();
  1481. $dateList = array_unique($list->pluck('date')->toArray());
  1482. if(1 == $orderType){
  1483. # 获取激活趋势数据
  1484. $activeDataList = DjDataCycleMp::query()
  1485. ->select(['expense_date', 'ref_date', 'daily_day_charge'])
  1486. ->where('enable', 1)
  1487. ->whereIn('expense_date', $dateList)
  1488. ->where('sys_group_id', $sysGroupId)
  1489. ->where(function($query) use ($platOrderType) {
  1490. if(!is_null($platOrderType)) {
  1491. $query->where('plat_order_type', $platOrderType);
  1492. } else {
  1493. $query->where('plat_order_type', 2);
  1494. }
  1495. })
  1496. ->get();
  1497. } else {
  1498. # 获取激活趋势数据
  1499. $activeDataList = DjDataCycleAdq::query()
  1500. ->select(['expense_date', 'ref_date', 'daily_day_charge'])
  1501. ->where('enable', 1)
  1502. ->whereIn('expense_date', $dateList)
  1503. ->where('sys_group_id', $sysGroupId)
  1504. ->where(function($query) use ($platOrderType) {
  1505. if(!is_null($platOrderType)) {
  1506. $query->where('plat_order_type', $platOrderType);
  1507. } else {
  1508. $query->where('plat_order_type', 2);
  1509. }
  1510. })
  1511. ->get();
  1512. }
  1513. $searchDays = DjRegUserRangeReport::count_range_days();
  1514. $maxSearchDay = end($searchDays);
  1515. $finishDate = is_null($closingDate) ? date("Y-m-d") : $closingDate;
  1516. foreach($list as $item) {
  1517. //总ROi
  1518. $item->total_roi = empty($item->total_roi) ? '0.00%' : ($item->total_roi * 100) . "%";
  1519. // 首日roi
  1520. $item->first_day_roi = empty($item->first_day_roi) ? '0.00%' : ($item->first_day_roi * 100) . '%';
  1521. $activateList = $activeDataList->where('expense_date', $item->date);
  1522. for($i = 0;$i < $maxSearchDay;$i++) {
  1523. $statDate = date("Y-m-d", strtotime('+'.$i. ' days', strtotime($item->date)));
  1524. if($statDate > $finishDate) {
  1525. $chargePrice = 0;
  1526. } else {
  1527. $activeInfo = $activateList->where('ref_date', $statDate)->first();
  1528. $chargePrice = $activeInfo->daily_day_charge ?? 0;
  1529. }
  1530. $day = $i+1;
  1531. $k = 'day'.$day.'_roi';
  1532. $item->$k = $item->advertiser_cost != 0 ? round($chargePrice/$item->advertiser_cost, 4) * 100 . '%' : '0.00%';
  1533. }
  1534. }
  1535. $overviewList = self::dataCycleListOverview($beginDate, $endDate, $sysGroupId, $firstOrderCostMin, $firstOrderCostMax,
  1536. $firstOrderCostUniqueMin, $firstOrderCostUniqueMax, $perFollowCostMin, $perFollowCostMax, $totalRoiMin,
  1537. $totalRoiMax, $firstDayRoiMin, $firstDayRoiMax, $orderType, $platOrderType, $closingDate, $paidMin, $paidMax);
  1538. $overview['head'] = $overviewHead;
  1539. $overview['list'] = $overviewList;
  1540. $data['head'] = $listHead;
  1541. $data['list'] = $list;
  1542. } catch (\Exception $exception) {
  1543. Log::logError('DataStatisticsService.dataCycleList', [
  1544. 'request_data' => $requestData,
  1545. 'err_msg' => '数据循环统计接口发生异常',
  1546. 'line' => $exception->getLine(),
  1547. 'message' => $exception->getMessage(),
  1548. 'trace' => $exception->getTraceAsString(),
  1549. ], 'interface');
  1550. EmailQueue::rPush('数据循环统计接口发生异常', json_encode([
  1551. 'request_data' => $requestData,
  1552. 'line' => $exception->getLine(),
  1553. 'message' => $exception->getMessage(),
  1554. 'trace' => $exception->getTraceAsString(),
  1555. ]), ['song.shen@kuxuan-inc.com'], '猎羽');
  1556. }
  1557. return [$count, $data, $overview];
  1558. }
  1559. public static function dataCycleListOverview($beginDate, $endDate, $sysGroupId, $firstOrderCostMin,
  1560. $firstOrderCostMax, $firstOrderCostUniqueMin, $firstOrderCostUniqueMax, $perFollowCostMin, $perFollowCostMax,
  1561. $totalRoiMin, $totalRoiMax, $firstDayRoiMin, $firstDayRoiMax, $orderType, $platOrderType, $closingDate, $paidMin,
  1562. $paidMax)
  1563. {
  1564. $data = ['date' => '汇总', 'advertiser_cost' => 0, 'follow_uv' => 0, 'per_follow_cost' => 0, 'first_order_cost' => 0,
  1565. 'first_order_cost_unique' => 0, 'total_cvt_amt' => 0, 'total_roi' => '0.00%'];
  1566. // 1mp 2adq
  1567. if(1 == $orderType) {
  1568. $query = DjDataCycleMp::query();
  1569. } else {
  1570. $query = DjDataCycleAdq::query();
  1571. }
  1572. $query->selectRaw('expense_date, max(advertiser_cost) as advertiser_cost, max(total_cvt_amt) as total_cvt_amt, ' .
  1573. 'max(first_order_ucnt) as first_order_ucnt, max(follow_uv) as follow_uv, max(first_order_ucnt_unique) ' .
  1574. 'as first_order_ucnt_unique');
  1575. // null 全选 0小程序 1H5
  1576. if(!is_null($platOrderType)) {
  1577. $query->where('plat_order_type', $platOrderType);
  1578. } else {
  1579. $query->where('plat_order_type', 2);
  1580. }
  1581. $query->where('sys_group_id', $sysGroupId)
  1582. ->where('enable', 1)
  1583. ->where('expense_date', '>=', $beginDate)
  1584. ->where('expense_date', '<=', $endDate);
  1585. if(!is_null($closingDate)) $query->where('ref_date', '<=', $closingDate);
  1586. if(!is_null($paidMin)) $query->where('advertiser_cost', '>=', $paidMin);
  1587. if(!is_null($paidMax)) $query->where('advertiser_cost', '<=', $paidMax);
  1588. if(!is_null($firstOrderCostMin)) $query->where('first_order_cost', '>=', $firstOrderCostMin);
  1589. if(!is_null($firstOrderCostMax)) $query->where('first_order_cost', '<=', $firstOrderCostMax);
  1590. if(!is_null($firstOrderCostUniqueMin)) $query->where('first_order_active_cost', '>=', $firstOrderCostUniqueMin);
  1591. if(!is_null($firstOrderCostUniqueMax)) $query->where('first_order_active_cost', '<=', $firstOrderCostUniqueMax);
  1592. if(!is_null($perFollowCostMin)) $query->where('per_follow_cost', '>=', $perFollowCostMin);
  1593. if(!is_null($perFollowCostMax)) $query->where('per_follow_cost', '<=', $perFollowCostMax);
  1594. if(!is_null($totalRoiMin)) $query->whereRaw('(total_roi*100) >= ' . $totalRoiMin);
  1595. if(!is_null($totalRoiMax)) $query->whereRaw('(total_roi*100) <= ' . $totalRoiMax);
  1596. if(!is_null($firstDayRoiMin)) $query->whereRaw('(first_day_roi*100) >= ' . $firstDayRoiMin);
  1597. if(!is_null($firstDayRoiMax)) $query->whereRaw('(first_day_roi*100) <= ' . $firstDayRoiMax);
  1598. $query->groupBy(['expense_date']);
  1599. $list = $query->get();
  1600. if($list->isNotEmpty()) {
  1601. $dateList = array_unique($list->pluck('expense_date')->toArray());
  1602. $minDate = min($dateList);
  1603. $maxDate = max($dateList);
  1604. $orderQuery = DjOrder::query()
  1605. ->where('is_ad_user', 1)
  1606. ->where('pay_status', 1);
  1607. $orderQuery->whereBetween("order_pay_time",[
  1608. strtotime("$minDate 00:00:00")*1000, //订单范围开始时间
  1609. strtotime("$maxDate 23:59:59")*1000 //订单范围结束时间
  1610. ])->whereBetween("mp_user_register_time",[
  1611. strtotime("$minDate 00:00:00")*1000, //注册当日开始时间,毫秒
  1612. strtotime("$maxDate 23:59:59")*1000 //注册当日结束时间,毫秒
  1613. ])
  1614. ->where('order_type', $orderType);
  1615. if(!is_null($platOrderType)) {
  1616. $orderQuery->where('plat_order_type', $platOrderType);
  1617. }
  1618. // 1mp 2adq
  1619. if(1 == $orderType) {
  1620. $accountIdArr = OfficialAccountRelation::query()
  1621. ->where('sys_group_id', $sysGroupId)
  1622. ->where('enable', 1)
  1623. ->get()
  1624. ->pluck('app_id')
  1625. ->toArray();
  1626. $orderQuery->whereIn("bind_app_id", $accountIdArr);
  1627. } else if (2 == $orderType) {
  1628. $accountIdArr = OfficialWebUserActionSetId::query()
  1629. ->where('sys_group_id', $sysGroupId)
  1630. ->where('enable', 1)
  1631. ->get()
  1632. ->pluck('account_id')
  1633. ->toArray();
  1634. $orderQuery->whereIn("adq_account_id", $accountIdArr);
  1635. }
  1636. $orderQuery->whereRaw('FROM_UNIXTIME(LEFT(`mp_user_register_time`, 10), "%Y-%m-%d") = ' .
  1637. 'FROM_UNIXTIME(LEFT(`order_pay_time`, 10), "%Y-%m-%d")');
  1638. $first_order_ucnt_unique = $orderQuery->selectRaw('count(distinct(external_userid)) as count')->first()->count;
  1639. $data['advertiser_cost'] = round($list->sum('advertiser_cost'), 2);
  1640. $data['follow_uv'] = $list->sum('follow_uv');
  1641. $data['per_follow_cost'] = ($data['follow_uv'] != 0) ? round($data['advertiser_cost'] / $data['follow_uv'], 2) : 0;
  1642. $first_order_ucnt = $list->sum('first_order_ucnt');
  1643. $data['first_order_cost'] = ($first_order_ucnt != 0) ? round($data['advertiser_cost'] / $first_order_ucnt, 2) : 0;
  1644. $data['first_order_cost_unique'] = ($first_order_ucnt_unique != 0) ? round($data['advertiser_cost'] / $first_order_ucnt_unique, 2) : 0;
  1645. $data['total_cvt_amt'] = round($list->sum('total_cvt_amt'), 2);
  1646. $data['total_roi'] = ($data['advertiser_cost'] != 0) ? round($data['total_cvt_amt'] / $data['advertiser_cost'], 4) * 100 . '%' : '0.00%';
  1647. }
  1648. return $data;
  1649. }
  1650. /**
  1651. * 获取数据看板更新时间
  1652. * */
  1653. public static function getDataUptime($type)
  1654. {
  1655. switch($type) {
  1656. case 'account_data_trend':
  1657. case 'account_data_trend_mp':
  1658. case 'account_data_trend_adq':
  1659. $datetime = RedisModel::get(AccountDataTrend::ACCOUNT_DATA_TREND_UPTIME);
  1660. if(!$datetime) {
  1661. EmailQueue::rPush('获取账号数据趋势更新时间异常', $type, ['xiaohua.hou@kuxuan-inc.com'], '猎羽');
  1662. $datetime = date('Y-m-d H:i:s');
  1663. }
  1664. break;
  1665. case 'playlet_data_trend_mp': // mp 短剧数据趋势
  1666. $datetime = RedisModel::get(PlayletAccountDataTrend::MP_DATA_TREND_UPTIME);
  1667. if(!$datetime) {
  1668. $datetime = MpPlayletTrendData::query()->orderBy('updated_at', 'desc')->limit(1)
  1669. ->value('updated_at');
  1670. if($datetime)
  1671. RedisModel::set(PlayletAccountDataTrend::MP_DATA_TREND_UPTIME, $datetime);
  1672. }
  1673. break;
  1674. case 'playlet_data_trend_adq': // adq 短剧数据趋势
  1675. $datetime = RedisModel::get(PlayletAccountDataTrend::ADQ_DATA_TREND_UPTIME);
  1676. if(!$datetime) {
  1677. $datetime = AdqPlayletTrendData::query()->orderBy('updated_at', 'desc')->limit(1)
  1678. ->value('updated_at');
  1679. if($datetime)
  1680. RedisModel::set(PlayletAccountDataTrend::ADQ_DATA_TREND_UPTIME, $datetime);
  1681. }
  1682. break;
  1683. case 'playlet_data_trend': // 总表 短剧数据趋势
  1684. $datetime = RedisModel::get(PlayletAccountDataTrend::MP_DATA_TREND_UPTIME);
  1685. if(!$datetime) {
  1686. $datetime = PlayletDataTrend::query()->orderBy('updated_at', 'desc')->limit(1)
  1687. ->value('updated_at');
  1688. if($datetime)
  1689. RedisModel::set(PlayletAccountDataTrend::MP_DATA_TREND_UPTIME, $datetime);
  1690. }
  1691. break;
  1692. case 'data_cycle_mp': // mp 数据循环统计
  1693. $datetime = RedisModel::get(DjRegUserRangeReport::DATA_TREND_UPTIME);
  1694. if(!$datetime) { // Redis中无数据
  1695. $datetime = DjRegUserRangeReport::query()->orderBy('updated_at', 'desc')->limit(1)
  1696. ->value('updated_at');
  1697. if($datetime)
  1698. RedisModel::set(DjRegUserRangeReport::DATA_TREND_UPTIME, $datetime);
  1699. }
  1700. break;
  1701. case 'data_cycle_adq': // adq 数据循环统计
  1702. $datetime = RedisModel::get(DjRegUserRangeReport::DATA_TREND_UPTIME);
  1703. if(!$datetime) {
  1704. $datetime = DjRegUserRangeReport::query()->orderBy('updated_at', 'desc')->limit(1)
  1705. ->value('updated_at');
  1706. if($datetime)
  1707. RedisModel::set(DjRegUserRangeReport::DATA_TREND_UPTIME, $datetime);
  1708. }
  1709. break;
  1710. case 'data_cycle_nur':// 花生数据循环统计
  1711. $datetime = RedisModel::get(DjDataCycleNur::UPTIME);
  1712. if(!$datetime){
  1713. $datetime = DjDataCycleNur::query()->orderBy('updated_at', 'desc')->limit(1)
  1714. ->value('updated_at');
  1715. if($datetime){
  1716. RedisModel::set(DjDataCycleNur::UPTIME, $datetime);
  1717. }
  1718. }
  1719. break;
  1720. case 'operate_data_trend':
  1721. $datetime = RedisModel::get(OperateDataTrend::ACCOUNT_DATA_TREND_UPTIME);
  1722. break;
  1723. case 'cumulative_recovery_data':
  1724. $datetime = RedisModel::get(DjCumulativeRecoveryData::UPDATE_TIME);
  1725. break;
  1726. case 'account_funds':
  1727. $datetime = RedisModel::get(TencentAdAuth::ADQ_ACCOUNT_FUNDS_UPDATE_TIME);
  1728. break;
  1729. default:
  1730. $datetime = null;
  1731. break;
  1732. }
  1733. return $datetime;
  1734. }
  1735. public static function huaShengDataCycleList($adminId, $isSystemAdmin, $beginDate,$endDate,$page,$pageSize
  1736. , $sysGroupId, $firstOrderCostMin,$firstOrderCostMax, $perFollowCostMin, $perFollowCostMax, $totalRoiMin
  1737. , $totalRoiMax, $firstDayRoiMin, $firstDayRoiMax, $closingDate, $paidMin, $paidMax, $sortField, $sortType
  1738. , $appId, $platOrderType, $orderSource)
  1739. {
  1740. $requestData = [
  1741. 'admin_id' => $adminId,
  1742. 'is_system_admin' => $isSystemAdmin,
  1743. 'begin_date' => $beginDate,
  1744. 'end_date' => $endDate,
  1745. 'page' => $page,
  1746. 'page_size' => $pageSize,
  1747. 'sys_group_id' => $sysGroupId,
  1748. 'first_order_cost_min' => $firstOrderCostMin,
  1749. 'first_order_cost_max' => $firstOrderCostMax,
  1750. 'per_follow_cost_min' => $perFollowCostMin,
  1751. 'per_follow_cost_max' => $perFollowCostMax,
  1752. 'total_roi_min' => $totalRoiMin,
  1753. 'total_roi_max' => $totalRoiMax,
  1754. 'first_day_roi_min' => $firstDayRoiMin,
  1755. 'first_day_roi_max' => $firstDayRoiMax,
  1756. 'paid_min' => $paidMin,
  1757. 'paid_max' => $paidMax,
  1758. 'sort_field' => $sortField,
  1759. 'closing_date' => $closingDate,
  1760. 'sort_type' => $sortType,
  1761. 'order_source' => $orderSource,
  1762. ];
  1763. $count = 0;
  1764. $overview = [];
  1765. $data = [];
  1766. try{
  1767. //补充头列表
  1768. $head = [
  1769. ['column' => 'date', 'name' => '日期', 'notes' => '', 'enable_to_sort' => true],
  1770. ['column' => 'app_name', 'name' => '账户', 'notes' => '', 'enable_to_sort' => false],
  1771. ['column' => 'advertiser_cost', 'name' => '消耗', 'notes' => '', 'enable_to_sort' => true],
  1772. ['column' => 'follow_uv', 'name' => '企微添加人数', 'notes' => '', 'enable_to_sort' => true],
  1773. ['column' => 'per_follow_cost', 'name' => '企微成本', 'notes' => '当日消耗/企微添加人数',
  1774. 'enable_to_sort' => true],
  1775. ['column' => 'first_order_cnt', 'name' => '首日下单量(次数)', 'notes' => '', 'enable_to_sort' => true],
  1776. ['column' => 'first_order_ucnt', 'name' => '首日下单量(人数)', 'notes' => '', 'enable_to_sort' => true],
  1777. ['column' => 'first_order_cost', 'name' => '新用户下单成本', 'notes' => '当日消耗/新用户充值人数',
  1778. 'enable_to_sort' => true],
  1779. ['column' => 'first_day_charge', 'name' => '首日回收金额', 'notes' => '当日新用户充值金额',
  1780. 'enable_to_sort' => true],
  1781. ['column' => 'first_day_roi', 'name' => '首日ROI', 'notes' => '当日新用户充值金额/当日消耗',
  1782. 'enable_to_sort' => true],
  1783. ['column' => 'total_cvt_ucnt', 'name' => '累计下单人数', 'notes' => '链接/小程序注册时间为当天的用户累积充值人数',
  1784. 'enable_to_sort' => true],
  1785. ['column' => 'total_order_cost', 'name' => '累计下单成本', 'notes' => '当日消耗/累积下单人数',
  1786. 'enable_to_sort' => true],
  1787. ['column' => 'total_cvt_amt', 'name' => '累计下单金额', 'notes' => '链接/小程序注册时间为这一天用户的累积下单金额',
  1788. 'enable_to_sort' => true],
  1789. ['column' => 'total_roi', 'name' => '累计回本率', 'notes' => '链接/小程序注册时间为这一天用户的累积下单金额/当日消耗',
  1790. 'enable_to_sort' => true],
  1791. ];
  1792. $overviewHead = $listHead = $head;
  1793. foreach (DjRegUserRangeReport::count_range_days() as $day) {
  1794. $listHead[] = ['column'=>'day'.$day.'_roi' , 'name' => 'day'.$day , 'notes' => '', 'enable_to_sort' => false];
  1795. }
  1796. # 当前登录账号绑定的公众号列表
  1797. $accountList = OfficialAccount::getAccountMpAppIdList($adminId, $sysGroupId, $isSystemAdmin);
  1798. # 在系统中配置的公众号列表
  1799. $appList = AccountConfigNoUserRelation::query()->where('enable', 1)
  1800. ->where('sys_group_id', $sysGroupId)->where(function($query) use ($appId) {
  1801. if($appId) $query->where('app_id', $appId);
  1802. })->whereIn('app_id', $accountList)->get();
  1803. $appIdList = $appList->isNotEmpty() ? array_unique(array_column($appList->toArray(), 'app_id')) : [];
  1804. // 批量获取公众号名称
  1805. $appDataList = OfficialAccount::query()->whereIn('mp_app_id', $appIdList)->get();
  1806. $query = DjDataCycleNur::query();
  1807. // null 全选 0小程序 1H5
  1808. if(!is_null($platOrderType)) {
  1809. $query->where('plat_order_type', $platOrderType);
  1810. } else {
  1811. $query->where('plat_order_type', 2);
  1812. }
  1813. $query->whereIn('app_id', $appIdList)
  1814. ->where('enable', 1)
  1815. ->where('expense_date', '>=', $beginDate)
  1816. ->where('expense_date', '<=', $endDate);
  1817. if(!is_null($closingDate)) $query->where('ref_date', '<=', $closingDate);
  1818. if(!is_null($paidMin)) $query->where('advertiser_cost', '>=', $paidMin);
  1819. if(!is_null($paidMax)) $query->where('advertiser_cost', '<=', $paidMax);
  1820. if(!is_null($firstOrderCostMin)) $query->where('first_order_cost', '>=', $firstOrderCostMin);
  1821. if(!is_null($firstOrderCostMax)) $query->where('first_order_cost', '<=', $firstOrderCostMax);
  1822. if(!is_null($perFollowCostMin)) $query->where('per_follow_cost', '>=', $perFollowCostMin);
  1823. if(!is_null($perFollowCostMax)) $query->where('per_follow_cost', '<=', $perFollowCostMax);
  1824. if(!is_null($totalRoiMin)) $query->whereRaw('(total_roi*100) >= ' . $totalRoiMin);
  1825. if(!is_null($totalRoiMax)) $query->whereRaw('(total_roi*100) <=' . $totalRoiMax);
  1826. if(!is_null($firstDayRoiMin)) $query->whereRaw('(first_day_roi*100) >=' . $firstDayRoiMin);
  1827. if(!is_null($firstDayRoiMax)) $query->whereRaw('(first_day_roi*100) <=' . $firstDayRoiMax);
  1828. if(!empty($orderSource)) $query->where('order_source', $orderSource);
  1829. $query->groupBy(['expense_date', 'app_id']);
  1830. $countQuery = clone $query;
  1831. $query->selectRaw('expense_date as date, app_id, max(advertiser_cost) as advertiser_cost, ' .
  1832. 'max(follow_uv) as follow_uv, max(per_follow_cost) as per_follow_cost, max(first_order_cnt) as ' .
  1833. 'first_order_cnt, max(first_order_cost) as first_order_cost, max(first_day_charge) as first_day_charge,' .
  1834. ' max(first_day_roi) as first_day_roi, max(total_cvt_ucnt) as total_cvt_ucnt, min(total_order_cost) ' .
  1835. 'as total_order_cost, max(total_cvt_amt) as total_cvt_amt, max(total_roi) as total_roi, max(first_order_ucnt) ' .
  1836. ' as first_order_ucnt');
  1837. $count = $countQuery->get()->count();
  1838. $list = $query->orderBy($sortField, $sortType)
  1839. ->offset(($page - 1) * $pageSize)
  1840. ->limit($pageSize)
  1841. ->get();
  1842. $dateList = array_unique($list->pluck('date')->toArray());
  1843. # 获取激活趋势数据
  1844. $activeDataList = DjDataCycleNur::query()
  1845. ->select(['expense_date', 'ref_date', 'daily_day_charge', 'app_id'])
  1846. ->where('enable', 1)
  1847. ->whereIn('expense_date', $dateList)
  1848. ->whereIn('app_id', $appIdList)
  1849. ->where(function($query) use ($platOrderType, $orderSource){
  1850. // null 全选 0小程序 1H5
  1851. if(!is_null($platOrderType)) {
  1852. $query->where('plat_order_type', $platOrderType);
  1853. } else {
  1854. $query->where('plat_order_type', 2);
  1855. }
  1856. })->where('order_source', $orderSource)->get();
  1857. $searchDays = DjRegUserRangeReport::count_range_days();
  1858. $maxSearchDay = end($searchDays);
  1859. $finishDate = is_null($closingDate) ? date("Y-m-d") : $closingDate;
  1860. foreach($list as $item) {
  1861. // 公众号名称
  1862. $appData = $appDataList->where('mp_app_id', $item->app_id)->first();
  1863. $item->app_name = $appData->mp_name ?? '';
  1864. //总ROi
  1865. $item->total_roi = empty($item->total_roi) ? '0%' : ($item->total_roi * 100) . "%";
  1866. // 首日roi
  1867. $item->first_day_roi = empty($item->first_day_roi) ? '0%' : ($item->first_day_roi * 100) . '%';
  1868. $activateList = $activeDataList->where('expense_date', $item->date)->where('app_id', $item->app_id);
  1869. for($i = 0;$i < $maxSearchDay;$i++) {
  1870. $statDate = date("Y-m-d", strtotime('+'.$i. ' days', strtotime($item->date)));
  1871. if($statDate > $finishDate) {
  1872. $chargePrice = 0;
  1873. } else {
  1874. $activeInfo = $activateList->where('ref_date', $statDate)->first();
  1875. $chargePrice = $activeInfo->daily_day_charge ?? 0;
  1876. }
  1877. $day = $i+1;
  1878. $k = 'day'.$day.'_roi';
  1879. $item->$k = $item->advertiser_cost != 0 ? round($chargePrice/$item->advertiser_cost, 4) * 100 . '%' : '0%';
  1880. }
  1881. }
  1882. $overviewList = self::huaShengDataCycleListOverview($appIdList, $beginDate, $endDate, $firstOrderCostMin
  1883. , $firstOrderCostMax, $perFollowCostMin, $perFollowCostMax, $totalRoiMin, $totalRoiMax, $firstDayRoiMin
  1884. , $firstDayRoiMax, $closingDate, $paidMin, $paidMax, $platOrderType, $orderSource);
  1885. $overview['head'] = $overviewHead;
  1886. $overview['list'] = $overviewList;
  1887. $data['head'] = $listHead;
  1888. $data['list'] = $list;
  1889. } catch (\Exception $exception) {
  1890. Log::logError('HuaShengDataStatisticsService.dataCycleList', [
  1891. 'request_data' => $requestData,
  1892. 'err_msg' => '数据循环统计接口发生异常',
  1893. 'line' => $exception->getLine(),
  1894. 'message' => $exception->getMessage(),
  1895. 'trace' => $exception->getTraceAsString(),
  1896. ], 'interface');
  1897. EmailQueue::rPush('花生数据循环统计接口发生异常', json_encode([
  1898. 'request_data' => $requestData,
  1899. 'line' => $exception->getLine(),
  1900. 'message' => $exception->getMessage(),
  1901. 'trace' => $exception->getTraceAsString(),
  1902. ]), ['song.shen@kuxuan-inc.com'], '猎羽');
  1903. }
  1904. return [$count, $data, $overview];
  1905. }
  1906. public static function huaShengDataCycleListOverview($appIdList, $beginDate, $endDate, $firstOrderCostMin
  1907. , $firstOrderCostMax, $perFollowCostMin, $perFollowCostMax, $totalRoiMin, $totalRoiMax, $firstDayRoiMin
  1908. , $firstDayRoiMax, $closingDate, $paidMin, $paidMax, $platOrderType, $orderSource)
  1909. {
  1910. $data = ['date' => '汇总', 'app_name' => '账户', 'advertiser_cost' => 0, 'follow_uv' => 0, 'per_follow_cost' => 0,
  1911. 'first_order_cnt' => 0, 'first_order_ucnt' => 0, 'first_order_cost' => 0,' first_day_charge' => 0,
  1912. 'first_day_roi' => '0%', 'total_cvt_ucnt' => 0, 'total_order_cost' => 0, 'total_cvt_amt' => 0, 'total_roi' => '0%'];
  1913. $query = DjDataCycleNur::query();
  1914. // null 全选 0小程序 1H5
  1915. if(!is_null($platOrderType)) {
  1916. $query->where('plat_order_type', $platOrderType);
  1917. } else {
  1918. $query->where('plat_order_type', 2);
  1919. }
  1920. $query->selectRaw('expense_date as date, app_id, max(advertiser_cost) as advertiser_cost, ' .
  1921. 'max(follow_uv) as follow_uv, max(first_order_cnt) as first_order_cnt, max(first_order_ucnt) as ' .
  1922. ' first_order_ucnt, max(first_day_charge) as first_day_charge, max(first_order_ucnt) as first_order_ucnt, ' .
  1923. ' max(total_cvt_ucnt) as total_cvt_ucnt, max(total_cvt_amt) as total_cvt_amt');
  1924. $query->whereIn('app_id', $appIdList)
  1925. ->where('enable', 1)
  1926. ->where('expense_date', '>=', $beginDate)
  1927. ->where('expense_date', '<=', $endDate);
  1928. if(!is_null($closingDate)) $query->where('ref_date', '<=', $closingDate);
  1929. if(!is_null($paidMin)) $query->where('advertiser_cost', '>=', $paidMin);
  1930. if(!is_null($paidMax)) $query->where('advertiser_cost', '<=', $paidMax);
  1931. if(!is_null($firstOrderCostMin)) $query->where('first_order_cost', '>=', $firstOrderCostMin);
  1932. if(!is_null($firstOrderCostMax)) $query->where('first_order_cost', '<=', $firstOrderCostMax);
  1933. if(!is_null($perFollowCostMin)) $query->where('per_follow_cost', '>=', $perFollowCostMin);
  1934. if(!is_null($perFollowCostMax)) $query->where('per_follow_cost', '<=', $perFollowCostMax);
  1935. if(!is_null($totalRoiMin)) $query->whereRaw('(total_roi*100) >= ' . $totalRoiMin);
  1936. if(!is_null($totalRoiMax)) $query->whereRaw('(total_roi*100) <= ' . $totalRoiMax);
  1937. if(!is_null($firstDayRoiMin)) $query->whereRaw('(first_day_roi*100) >= ' . $firstDayRoiMin);
  1938. if(!is_null($firstDayRoiMax)) $query->whereRaw('(first_day_roi*100) <= ' . $firstDayRoiMax);
  1939. if(!empty($orderSource)) $query->where('order_source', $orderSource);
  1940. $query->groupBy(['expense_date', 'app_id']);
  1941. $list = $query->get();
  1942. if($list->isNotEmpty()) {
  1943. $data['advertiser_cost'] = round($list->sum('advertiser_cost'), 2);
  1944. $data['follow_uv'] = $list->sum('follow_uv');
  1945. $data['per_follow_cost'] = ($data['follow_uv'] != 0) ? round($data['advertiser_cost'] / $data['follow_uv'], 2) : 0;
  1946. $data['first_order_cnt'] = $list->sum('first_order_cnt');// 首日下单次数
  1947. $data['first_order_ucnt'] = $list->sum('first_order_ucnt');// 首日下单人数
  1948. $first_order_ucnt = $list->sum('first_order_ucnt');
  1949. $data['first_order_cost'] = ($first_order_ucnt != 0) ? round($data['advertiser_cost'] /
  1950. $first_order_ucnt, 2) : 0;
  1951. $data['first_day_charge'] = round($list->sum('first_day_charge'), 2);
  1952. $data['first_day_roi'] = ($data['advertiser_cost'] != 0) ? round($data['first_day_charge'] /
  1953. $data['advertiser_cost'], 4) * 100 . '%' : '0%';
  1954. $data['total_cvt_ucnt'] = $list->sum('total_cvt_ucnt');
  1955. $data['total_order_cost'] = ($data['total_cvt_ucnt'] != 0) ? round($data['advertiser_cost'] /
  1956. $data['total_cvt_ucnt'], 2) : 0;
  1957. $data['total_cvt_amt'] = round($list->sum('total_cvt_amt'), 2);
  1958. $data['total_roi'] = ($data['advertiser_cost'] != 0) ? round($data['total_cvt_amt'] /
  1959. $data['advertiser_cost'], 4) * 100 . '%' : '0%';
  1960. }
  1961. return $data;
  1962. }
  1963. public static function dealDaysCharge($refDate, &$item, $charge_data) {
  1964. $item['day3_charge'] = 0;
  1965. $item['day7_charge'] = 0;
  1966. for ($i = 0; $i < 7; $i++) {
  1967. if($i < 3) {
  1968. $item['day3_charge'] += (!empty($charge_data[$i]['day_charge']) ? $charge_data[$i]['day_charge'] : 0);
  1969. }
  1970. if($i < 7) {
  1971. $item['day7_charge'] += (!empty($charge_data[$i]['day_charge']) ? $charge_data[$i]['day_charge'] : 0);
  1972. }
  1973. }
  1974. if(strtotime(date('Y-m-d')) - strtotime($refDate) < 2 * 86400) {
  1975. $item['day3_charge'] = 0;
  1976. $item['day7_charge'] = 0;
  1977. } else if(strtotime(date('Y-m-d')) - strtotime($refDate) < 6 * 86400) {
  1978. $item['day7_charge'] = 0;
  1979. }
  1980. }
  1981. public static function pitcherAdqAccountData($params, $sortField, $sortType) {
  1982. $head = [
  1983. ['column' => 'pitcher_name', 'name' => '优化师', 'notes' => '', 'enable_to_sort' => false],
  1984. ['column' => 'account_nums', 'name' => '账户数', 'notes' => '有消耗的账户', 'enable_to_sort' => true],
  1985. ['column' => 'avg_days', 'name' => '平均投放天数', 'notes' => '有消耗的投放天数/账户数', 'enable_to_sort' => true],
  1986. ['column' => 'total_paid', 'name' => '总消耗', 'notes' => '', 'enable_to_sort' => true],
  1987. // ['column' => 'account_nums_0', 'name' => '<1.5k', 'notes' => '', 'enable_to_sort' => true],
  1988. // ['column' => 'account_nums_15', 'name' => '1.5k~5k', 'notes' => '', 'enable_to_sort' => true],
  1989. // ['column' => 'account_nums_50', 'name' => '5k~1w', 'notes' => '', 'enable_to_sort' => true],
  1990. // ['column' => 'account_nums_100', 'name' => '>1w', 'notes' => '', 'enable_to_sort' => true],
  1991. // ['column' => 'paid_0', 'name' => '<1.5k', 'notes' => '', 'enable_to_sort' => true],
  1992. // ['column' => 'paid_15', 'name' => '1.5~5k', 'notes' => '', 'enable_to_sort' => true],
  1993. // ['column' => 'paid_50', 'name' => '5k~1w', 'notes' => '', 'enable_to_sort' => true],
  1994. // ['column' => 'paid_100', 'name' => '>1w', 'notes' => '', 'enable_to_sort' => true],
  1995. ['column' => 'account_nums_0_rate_format', 'name' => '<1.5k(账户数)', 'notes' => '', 'enable_to_sort' => true],
  1996. ['column' => 'account_nums_15_rate_format', 'name' => '1.5k~5k(账户数)', 'notes' => '', 'enable_to_sort' => true],
  1997. ['column' => 'account_nums_50_rate_format', 'name' => '5k~1w(账户数)', 'notes' => '', 'enable_to_sort' => true],
  1998. ['column' => 'account_nums_100_rate_format', 'name' => '>1w(账户数)', 'notes' => '', 'enable_to_sort' => true],
  1999. ['column' => 'paid_0_rate_format', 'name' => '<1.5k(消耗)', 'notes' => '', 'enable_to_sort' => true],
  2000. ['column' => 'paid_15_rate_format', 'name' => '1.5k~5k(消耗)', 'notes' => '', 'enable_to_sort' => true],
  2001. ['column' => 'paid_50_rate_format', 'name' => '5k~1w(消耗)', 'notes' => '', 'enable_to_sort' => true],
  2002. ['column' => 'paid_100_rate_format', 'name' => '>1w(消耗)', 'notes' => '', 'enable_to_sort' => true],
  2003. ['column' => 'account_cumulative_consumption', 'name' => '单账户累计消耗', 'notes' => '总消耗/账户数', 'enable_to_sort' => true],
  2004. ['column' => 'account_daily_consumption', 'name' => '单账户日均消耗', 'notes' => '单账户累计消耗/平均投放天数', 'enable_to_sort' => true],
  2005. ];
  2006. $allAccountIdList = PitcherService::adqAccountListForUser($params['admin_id'], $params['sys_group_id'], $params['is_system_admin']);
  2007. # 批量查询投手名称
  2008. $pitcherNameList = Users::query()->where('enable', 1)->where('group_admin_id', $params['sys_group_id'])
  2009. ->select(['id', 'name'])->get();
  2010. $userIdList = array_column($pitcherNameList->toArray(), 'id');
  2011. # 找到日期范围内所有的绑定关系配置
  2012. $confList = DramaUserRela::getAdqConfListByDate($params['start_date'], $params['end_date'], $allAccountIdList, $userIdList);
  2013. # 提取配置内的投放账号以及投手
  2014. $pitcherIdList = array_unique(array_filter(array_column($confList->toArray(), 'user_id')));
  2015. $accountIdList = array_unique(array_filter(array_column($confList->toArray(), 'account_id')));
  2016. # 获取投放账号在日期范围内的所有投放消耗数据
  2017. $accountPaidList = TencentAdDailyReport::query()->where('enable', 1)->where('ref_date', '>=', $params['start_date'])
  2018. ->where('ref_date', '<=', $params['end_date'])->whereIn('account_id', $accountIdList)
  2019. ->where('paid', '>', 0)
  2020. ->select(['account_id', 'paid', 'ref_date'])->get();
  2021. $pitcherDataList = [];
  2022. $accountDateList = [];
  2023. foreach($confList as $confInfo) {
  2024. $confStartDate = ($confInfo->start_date < $params['start_date']) ? $params['start_date'] : $confInfo->start_date;
  2025. if(!empty($confInfo->disable_date)) {
  2026. $confEndDate = $confInfo->disable_date;
  2027. } else {
  2028. $confEndDate = $confInfo->end_date;
  2029. }
  2030. $confEndDate = ($confEndDate > $params['end_date']) ? $params['end_date'] : $confEndDate;
  2031. if($confEndDate < $confStartDate) {
  2032. continue;
  2033. }
  2034. # 统计配置中的投放天数
  2035. // $days = round((strtotime($confEndDate.' 23:59:59') - strtotime($confStartDate.' 00:00:00'))/86400);
  2036. // $pitcherDataList[$confInfo->user_id]['days'] = isset($pitcherDataList[$confInfo->user_id]['days']) ?
  2037. // ($pitcherDataList[$confInfo->user_id]['days'] + $days) : $days;
  2038. # 投放配置中投手关联的投放账号,如果这里注释掉,则只会统计有消耗金额的账户数
  2039. // $pitcherDataList[$confInfo->user_id]['account'][$confInfo->account_id] = 0;
  2040. $date = $confStartDate;
  2041. while($date <= $confEndDate) {
  2042. $key = $confInfo->account_id.'#'.$date;
  2043. $accountDateList[$key][] = $confInfo->user_id;
  2044. $date = date('Y-m-d', strtotime($date. ' +1 day'));
  2045. }
  2046. }
  2047. foreach($accountPaidList as $accountPaidInfo) {
  2048. $info = $accountDateList[$accountPaidInfo->account_id.'#'.$accountPaidInfo->ref_date] ?? [];
  2049. $amount = $accountPaidInfo->paid/100;
  2050. if(!empty($info)) {
  2051. foreach($info as $userId) {
  2052. $pitcherDataList[$userId]['account'][$accountPaidInfo->account_id] = isset($pitcherDataList[$userId]['account'][$accountPaidInfo->account_id])
  2053. ? ($pitcherDataList[$userId]['account'][$accountPaidInfo->account_id] + $amount) : $amount;
  2054. $pitcherDataList[$userId]['amount'] = isset($pitcherDataList[$userId]['amount']) ?
  2055. round($pitcherDataList[$userId]['amount'] + $amount, 2) : $amount;
  2056. # 只统计有消耗的投放天数
  2057. $pitcherDataList[$userId]['days'] = isset($pitcherDataList[$userId]['days']) ?
  2058. ($pitcherDataList[$userId]['days'] + 1) : 1;
  2059. }
  2060. }
  2061. }
  2062. # 整理投手投放消耗数据
  2063. $dataList = [];
  2064. foreach($pitcherIdList as $pitcherId) {
  2065. $item = [];
  2066. $pitcherInfo = $pitcherNameList->where('id', $pitcherId)->first();
  2067. $item['pitcher_name'] = !empty($pitcherInfo->name) ? $pitcherInfo->name : '';
  2068. $item['pitcher_id'] = $pitcherId;
  2069. $dataInfo = $pitcherDataList[$pitcherId] ?? [];
  2070. $item['account_nums'] = isset($dataInfo['account']) ? count($dataInfo['account']) : 0;
  2071. $item['total_days'] = $dataInfo['days'] ?? 0;
  2072. $item['avg_days'] = 0;
  2073. $item['total_paid'] = $dataInfo['amount'] ?? 0;
  2074. $item['account_nums_0'] = 0;
  2075. $item['account_nums_15'] = 0;
  2076. $item['account_nums_50'] = 0;
  2077. $item['account_nums_100'] = 0;
  2078. $item['paid_0'] = 0;
  2079. $item['paid_15'] = 0;
  2080. $item['paid_50'] = 0;
  2081. $item['paid_100'] = 0;
  2082. $item['account_nums_0_rate_format'] = '0%';
  2083. $item['account_nums_15_rate_format'] = '0%';
  2084. $item['account_nums_50_rate_format'] = '0%';
  2085. $item['account_nums_100_rate_format'] = '0%';
  2086. $item['account_nums_0_rate'] = 0;
  2087. $item['account_nums_15_rate'] = 0;
  2088. $item['account_nums_50_rate'] = 0;
  2089. $item['account_nums_100_rate'] = 0;
  2090. $item['paid_0_rate_format'] = '0%';
  2091. $item['paid_15_rate_format'] = '0%';
  2092. $item['paid_50_rate_format'] = '0%';
  2093. $item['paid_100_rate_format'] = '0%';
  2094. $item['paid_0_rate'] = 0;
  2095. $item['paid_15_rate'] = 0;
  2096. $item['paid_50_rate'] = 0;
  2097. $item['paid_100_rate'] = 0;
  2098. $item['account_cumulative_consumption'] = 0;
  2099. $item['account_daily_consumption'] = 0;
  2100. if(!empty($dataInfo['account'])) {
  2101. $item['account_nums_0_arr'] = $item['account_nums_15_arr'] = $item['account_nums_50_arr'] = $item['account_nums_100_arr'] = [];
  2102. foreach($dataInfo['account'] as $key => $value) {
  2103. if($value <= 1500) {
  2104. $item['account_nums_0_arr'][] = $key;
  2105. $item['paid_0'] = isset($item['paid_0']) ? round($item['paid_0'] + $value, 2) : $value;
  2106. }
  2107. if($value > 1500 && $value <= 5000) {
  2108. $item['account_nums_15_arr'][] = $key;
  2109. $item['paid_15'] = isset($item['paid_15']) ? round($item['paid_15'] + $value, 2) : $value;
  2110. }
  2111. if($value > 5000 && $value <= 10000) {
  2112. $item['account_nums_50_arr'][] = $key;
  2113. $item['paid_50'] = isset($item['paid_50']) ? round($item['paid_50'] + $value, 2) : $value;
  2114. }
  2115. if($value > 10000) {
  2116. $item['account_nums_100_arr'][] = $key;
  2117. $item['paid_100'] = isset($item['paid_100']) ? round($item['paid_100'] + $value, 2) : $value;
  2118. }
  2119. }
  2120. $item['avg_days'] = $item['account_nums'] >0 ? round($item['total_days'] / $item['account_nums']) : 0;
  2121. $item['account_nums_0'] = count($item['account_nums_0_arr']);
  2122. $item['account_nums_15'] = count($item['account_nums_15_arr']);
  2123. $item['account_nums_50'] = count($item['account_nums_50_arr']);
  2124. $item['account_nums_100'] = count($item['account_nums_100_arr']);
  2125. $item['account_nums_0_rate'] = $item['account_nums'] > 0 ? round($item['account_nums_0'] / $item['account_nums'], 4) : 0;
  2126. $item['account_nums_15_rate'] = $item['account_nums'] > 0 ? round($item['account_nums_15'] / $item['account_nums'], 4) : 0;
  2127. $item['account_nums_50_rate'] = $item['account_nums'] > 0 ? round($item['account_nums_50'] / $item['account_nums'], 4) : 0;
  2128. $item['account_nums_100_rate'] = $item['account_nums'] > 0 ? round($item['account_nums_100'] / $item['account_nums'], 4) : 0;
  2129. $item['account_nums_0_rate_format'] = $item['account_nums_0_rate'] * 100 . '%';
  2130. $item['account_nums_15_rate_format'] = $item['account_nums_15_rate'] * 100 . '%';
  2131. $item['account_nums_50_rate_format'] = $item['account_nums_50_rate'] * 100 . '%';
  2132. $item['account_nums_100_rate_format'] = $item['account_nums_100_rate'] * 100 . '%';
  2133. $item['paid_0_rate'] = $item['total_paid'] > 0 ? round($item['paid_0'] / $item['total_paid'], 4) : 0;
  2134. $item['paid_15_rate'] = $item['total_paid'] > 0 ? round($item['paid_15'] / $item['total_paid'], 4) : 0;
  2135. $item['paid_50_rate'] = $item['total_paid'] > 0 ? round($item['paid_50'] / $item['total_paid'], 4) : 0;
  2136. $item['paid_100_rate'] = $item['total_paid'] > 0 ? round($item['paid_100'] / $item['total_paid'], 4) : 0;
  2137. $item['paid_0_rate_format'] = $item['paid_0_rate'] * 100 . '%';
  2138. $item['paid_15_rate_format'] = $item['paid_15_rate'] * 100 . '%';
  2139. $item['paid_50_rate_format'] = $item['paid_50_rate'] * 100 . '%';
  2140. $item['paid_100_rate_format'] = $item['paid_100_rate'] * 100 . '%';
  2141. $item['account_cumulative_consumption'] = $item['account_nums'] > 0 ? round($item['total_paid'] / $item['account_nums'], 2) : 0;
  2142. $item['account_daily_consumption'] = $item['avg_days'] > 0 ? round($item['account_cumulative_consumption'] / $item['avg_days'], 2) : 0;
  2143. unset($item['account_nums_0_arr']);
  2144. unset($item['account_nums_15_arr']);
  2145. unset($item['account_nums_50_arr']);
  2146. unset($item['account_nums_100_arr']);
  2147. }
  2148. $dataList[] = $item;
  2149. }
  2150. # 分页排序
  2151. // 对排序字段做处理
  2152. if(strpos($sortField, '_format') !== false ) {
  2153. $sortField = substr($sortField, 0, -7);
  2154. }
  2155. $sortArr = array_column($dataList, $sortField);
  2156. array_multisort($sortArr, $sortType=='asc'? SORT_ASC : SORT_DESC, $dataList);
  2157. // $result = array_slice($result, ($page - 1) * $pageSize, $pageSize);
  2158. # 返回表头以及数据
  2159. return ['data' => $dataList, 'extra' => $head];
  2160. }
  2161. }