企微短剧业务系统

CircleMassMsgRecordService.php 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: shensong
  5. * Date: 2022/11/10
  6. * Time: 14:39
  7. */
  8. namespace App\Service;
  9. use App\Log;
  10. use App\Models\CircleMassMsgRecord;
  11. use App\Models\CustomerDetails;
  12. use App\Models\DjUser;
  13. use App\Models\Es\CircleMassMsgSendDetailEs;
  14. use App\Models\System\Users;
  15. use App\Support\EmailQueue;
  16. use PhpOffice\PhpSpreadsheet\IOFactory;
  17. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  18. class CircleMassMsgRecordService
  19. {
  20. /**
  21. * 获取群发列表
  22. * @param $corpid string 企业ID
  23. * @param $ruleId integer
  24. * @param $sendTimeStart string 发送时间查询-起始时间
  25. * @param $sendTimeEnd string 发送时间查询-结束时间
  26. * @param $sortColumn string 排序字段
  27. * @param $sortMethod string 排序方式*
  28. * @param $page integer 当前页码数
  29. * @param $pageSize integer 每页显示条数
  30. * @return mixed
  31. * */
  32. public static function recordList($corpid, $ruleId, $sendTimeStart, $sendTimeEnd, $sortColumn, $sortMethod
  33. , $page, $pageSize, &$errno)
  34. {
  35. try {
  36. list($list, $count) = CircleMassMsgRecord::getRecordLists($corpid, $ruleId, $sendTimeStart
  37. , $sendTimeEnd, $sortColumn, $sortMethod, $page, $pageSize);
  38. # 将该企微下的所有客服信息查询出来备用
  39. $adminData = Users::query()->select(['id','name'])->get();
  40. # 处理数据
  41. foreach($list as $datum) {
  42. # 消息条数
  43. // $contentCount = empty($datum->content) ? 0 : 1;
  44. // $attachmentCount = empty($datum->attachments) ? 0 : count(json_decode($datum->attachments, true));
  45. // $datum->msg_count = $contentCount + $attachmentCount;
  46. # 解析json,读取发送规则以及标题
  47. $filterData = json_decode($datum->filter_data, 1);
  48. $adminId = $filterData['admin_id'] ?? 0;
  49. # 创建人信息
  50. $adminInfo = $adminData->where('id', $adminId)->first();
  51. $datum->creator = isset($adminInfo->name) ? $adminInfo->name : '';
  52. # 群发标题
  53. $datum->name = $filterData['name'] ?? '';
  54. # 群发规则
  55. $datum->rule_detail = self::getRuleDetail($filterData);
  56. unset($datum->filter_data);
  57. }
  58. } catch (\Exception $e) {
  59. Log::logError('获取群发列表过程发生异常', [
  60. 'file' => $e->getFile(),
  61. 'line' => $e->getLine(),
  62. 'msg' => $e->getMessage(),
  63. 'trace' => $e->getTraceAsString(),
  64. ], 'CircleMassMsgRuleList');
  65. $errno = 3601;
  66. return [[], 0];
  67. }
  68. return [$list, $count];
  69. }
  70. public static function getRuleDetail($ruleInfo)
  71. {
  72. // # 客户添加客服时间
  73. // $addTime = turn_minute($ruleInfo['interval_time']);
  74. //
  75. // $ruleDetail = '客户添加客服'.$addTime.'后;';
  76. //
  77. // # 通知时间
  78. // if(1 == $ruleInfo['interval_type']) {
  79. // // 指定时间
  80. // $timePointsArr = json_decode($ruleInfo['time_points'], 1);
  81. // foreach($timePointsArr as $timePoint) {
  82. // $ruleDetail .= '当天'.$timePoint.'提醒发送;';
  83. // }
  84. //
  85. // $ruleDetail = rtrim(';', $ruleDetail);
  86. // } else {
  87. // // 间隔时间
  88. // $ruleDetail .= '后台每'.$ruleInfo['interval'].'小时分批提醒发送';
  89. // }
  90. # 描述信息
  91. $days = floor($ruleInfo['interval_time'] / 60 / 24);
  92. $hour = ($ruleInfo['interval_time'] / 60) % 24;
  93. $minute = $ruleInfo['interval_time'] % 60;
  94. $ruleDetail = '客户加客服' .$days. '天' .$hour. '小时' .$minute. '分钟后,';
  95. if($ruleInfo['interval_type'] == 1) {
  96. $ruleDetail .= '当天';
  97. $timePoints = json_decode($ruleInfo['time_points'], true);
  98. foreach ($timePoints as $points) {
  99. $ruleDetail .= $points . ' ';
  100. }
  101. $ruleDetail .= "提醒发送";
  102. } elseif ($ruleInfo['interval_type'] == 2) {
  103. $ruleDetail .= '后台每'.$ruleInfo['interval'].'小时分批提醒发送';
  104. }
  105. return $ruleDetail;
  106. }
  107. /**
  108. * 获取群发详情
  109. * @param $corpid string 企业ID
  110. * @param $ruleId integer 群发规则ID
  111. * @param $sendNum integer
  112. * @param $sendDate string
  113. * @return mixed
  114. * */
  115. public static function recordDetail($corpid, $ruleId, $sendNum, $sendDate, &$errno)
  116. {
  117. try{
  118. $detail = [];
  119. # 获取发送记录
  120. $recordList = CircleMassMsgRecord::query()->selectRaw('corpid, rule_id, send_num, create_time
  121. , send_date, content, attachments, filter_data, status')->where('corpid', $corpid)
  122. ->where('rule_id', $ruleId)->where('send_num', $sendNum)
  123. ->where('send_date', $sendDate)->get();
  124. if($recordList->isEmpty()) return [];
  125. # 判断发送状态
  126. $statusList = array_column($recordList->toArray(), 'status');
  127. foreach($recordList as $recordInfo) {
  128. $detail = json_decode(json_encode($recordInfo), 1);
  129. }
  130. $detail['status'] = self::getRecordSendStatus($statusList);
  131. # 群发规则信息
  132. $ruleInfo = json_decode($detail['filter_data'], 1);
  133. $detail['rule_info'] = $ruleInfo;
  134. if($ruleInfo['is_all'] != 1 && !empty($ruleInfo['senders'])) {
  135. # 发送人
  136. $detail['sender_name'] = DjUser::query()->where("corpid",$corpid)
  137. ->whereIn("user_id",explode(',',$ruleInfo['senders']))
  138. ->pluck("name")->toArray();
  139. }
  140. # 群发规则
  141. $detail['rule_detail'] = self::getRuleDetail($ruleInfo);
  142. # 获取创建人信息
  143. $detail['creator'] = Users::query()->where('id', $ruleInfo['admin_id'])->value('name');
  144. # 处理附件
  145. $attachments = json_decode($detail['attachments'], 1);
  146. if(!empty($attachments)) {
  147. foreach ($attachments as $key=>&$attachment) {
  148. if(isset($attachment['msgtype']) && $attachment['msgtype'] == 'radar') { // 雷达附件信息回显
  149. $radarId = $attachment['radar']['radar_id'] ?? 0;
  150. $radarInfo = RadarService::getRadarContent($corpid, $radarId);
  151. if(empty($radarInfo)) {
  152. unset($attachment[$key]);
  153. continue;
  154. }
  155. $attachment['radar'] = $radarInfo;
  156. }
  157. }
  158. }
  159. # 消息条数
  160. $contentCount = empty($detail['content']) ? 0 : 1;
  161. $attachmentCount = empty($detail['attachments']) ? 0 : count($attachments);
  162. $detail['msg_count'] = $contentCount + $attachmentCount;
  163. $detail['attachments'] = json_encode($attachments, 256);
  164. } catch (\Exception $e) {
  165. Log::logError('群发详情获取过程发生异常', [
  166. 'line' => $e->getLine(),
  167. 'msg' => $e->getMessage(),
  168. 'trace' => $e->getTraceAsString(),
  169. 'rule_id' => $ruleId,
  170. 'send_date' => $sendDate,
  171. 'send_num' => $sendNum,
  172. ], 'circleRecordDetail');
  173. $errno = 3602;
  174. return [];
  175. }
  176. return $detail;
  177. }
  178. public static function getRecordSendStatus($statusList)
  179. {
  180. if(array_search('-1', $statusList)){// 含有-1(发送失败状态)
  181. return -1;
  182. } else if(array_search('1', $statusList)){// 含有1 (发送中状态)
  183. return 2;
  184. } else {// 全部为2(发送完成状态)
  185. return 3;
  186. }
  187. }
  188. public static function overview($corpid, $ruleId, $sendNum, $sendDate)
  189. {
  190. $sendRecordTotal = CircleMassMsgRecord::query()->where("corpid",$corpid)
  191. ->where("rule_id",$ruleId)
  192. ->where('send_num', $sendNum)
  193. ->where('send_date', $sendDate)
  194. ->selectRaw("count(CASE WHEN status = 1 THEN 1 END) as un_sender_count") // 未发送成员
  195. ->selectRaw("count(CASE WHEN status = 2 THEN 1 END) as sender_count") // 已发送成员
  196. ->selectRaw("sum(send_success) as send_success") // 发送成功
  197. ->selectRaw("sum(send_fail) as send_fail") // 未送达人数
  198. ->first();
  199. $sendRecordTotal->send_success = empty($sendRecordTotal->send_success) ? 0 : $sendRecordTotal->send_success;
  200. $sendRecordTotal->send_fail = empty($sendRecordTotal->send_fail) ? 0 : $sendRecordTotal->send_fail;
  201. $retData = [];
  202. #已发送人员
  203. $retData['executed_sender_count'] = $sendRecordTotal->sender_count;
  204. #发送用户
  205. $retData['receive_customer_count'] = $sendRecordTotal->send_success;
  206. #未发送成员
  207. $retData['un_execute_sender_count'] = $sendRecordTotal->un_sender_count;
  208. #未送达客户
  209. $retData['un_receive_customer_count'] = $sendRecordTotal->send_fail ;
  210. #客户接收达上限
  211. $retData['receive_fail_with_limit'] = CircleMassMsgSendDetailEs::massSendCustCount($ruleId, $sendNum, $sendDate, null,3);
  212. #因为不是好友发送失败
  213. $retData['receive_fail_with_not_friend'] = CircleMassMsgSendDetailEs::massSendCustCount($ruleId, $sendNum, $sendDate, null,4);
  214. return $retData;
  215. }
  216. public static function senderList($corpid, $ruleId, $sendNum, $sendDate, $type, $keyword, $page, $pageSize)
  217. {
  218. /**已发送成员统计**/
  219. $sendRecord = CircleMassMsgRecord::query()->where("corpid",$corpid)
  220. ->where("rule_id",$ruleId)
  221. ->where('send_num', $sendNum)
  222. ->where('send_date', $sendDate)
  223. ->select(["sender", "filter_data"])
  224. ->selectRaw("min(send_time) as send_time") //发送时间
  225. ->selectRaw("sum(send_fail) as send_fail") //失败次数
  226. ->selectRaw("sum(send_success) as send_success") //成功次数
  227. ->groupBy("sender")
  228. ->get()->toArray();
  229. $ruleInfo = isset($sendRecord[0]['filter_data']) ? json_decode($sendRecord[0]['filter_data'], 1) : [];
  230. $lastSendTime = isset($ruleInfo['last_send_time']) ? strtotime($ruleInfo['last_send_time']) : null;
  231. $nextSendTime = isset($ruleInfo['next_send_time']) ? strtotime($ruleInfo['next_send_time']) : null;
  232. $sendRecord = array_column($sendRecord, null, 'sender');
  233. #根据查询类型不同,获取当页展示的成员ID集合
  234. $allSendersIdArr = [];
  235. switch ($type){
  236. case "all": //全部成员
  237. $allSendersIdArr = CircleMassMsgRecord::query()
  238. ->select('sender')
  239. ->where("rule_id",$ruleId)
  240. ->where('send_num', $sendNum)
  241. ->where('send_date', $sendDate)
  242. ->pluck('sender');
  243. break;
  244. case "sent": //已发送的成员
  245. $allSendersIdArr = CircleMassMsgRecord::query()
  246. ->select('sender')
  247. ->where("rule_id",$ruleId)
  248. ->where('send_num', $sendNum)
  249. ->where('send_date', $sendDate)
  250. ->where('status', 2)
  251. ->pluck('sender');
  252. break;
  253. case "unsent": //未发送的成员
  254. $allSendersIdArr = CircleMassMsgRecord::query()
  255. ->select('sender')
  256. ->where("rule_id",$ruleId)
  257. ->where('send_num', $sendNum)
  258. ->where('send_date', $sendDate)
  259. ->where('status', 1)
  260. ->pluck('sender');
  261. break;
  262. case "fail": //发送失败的成员
  263. $allSendersIdArr = CircleMassMsgRecord::query()
  264. ->select('sender')
  265. ->where("rule_id",$ruleId)
  266. ->where('send_num', $sendNum)
  267. ->where('send_date', $sendDate)
  268. ->where('status', -1)
  269. ->pluck('sender');
  270. break;
  271. }
  272. if(!empty($allSendersIdArr) && !is_array($allSendersIdArr)) $allSendersIdArr = $allSendersIdArr->toArray();
  273. /**查询发送成员**/
  274. $query = DjUser::query()->where("corpid",$corpid)
  275. ->whereIn("user_id",$allSendersIdArr);
  276. /**关键词查询**/
  277. if(!empty($keyword)){
  278. $query->where("name","like","%$keyword%");
  279. }
  280. $total = $query->count(); //总计总数
  281. $list = $query->select("user_id","name","avatar")
  282. ->offset(($page-1)*$pageSize)
  283. ->limit($pageSize)
  284. ->get()->toArray();
  285. /**查询客服对应客户数**/
  286. $userCustomerCountList = CustomerDetails::suffix($corpid)
  287. ->selectRaw('user_id, count(1) as count')
  288. ->where('loss_status', 1)
  289. ->where('corpid', $corpid)
  290. ->whereIn('user_id', array_column($list,'user_id'))
  291. ->where(function($query) use ($lastSendTime, $nextSendTime) {
  292. if($lastSendTime && $nextSendTime){
  293. $query->where('createtime', '>=', $lastSendTime)
  294. ->where('createtime', '<', $nextSendTime);
  295. }
  296. })
  297. ->groupBy('user_id')
  298. ->get()
  299. ->keyBy('user_id')
  300. ->toArray();
  301. foreach ($list as $k => $item){
  302. #补充最早群发时间
  303. $list[$k]['send_time'] = isset($sendRecord[$item['user_id']]['send_time']) ? $sendRecord[$item['user_id']]['send_time'] : '';
  304. #补充好友数量
  305. $list[$k]['customer_num'] = isset($userCustomerCountList[$item['user_id']]['count']) ? $userCustomerCountList[$item['user_id']]['count'] : 0;
  306. #已送达人数
  307. $list[$k]['send_fail'] = isset($sendRecord[$item['user_id']]['send_fail']) ? $sendRecord[$item['user_id']]['send_fail'] : 0;
  308. #未送达人数
  309. $list[$k]['send_success'] = isset($sendRecord[$item['user_id']]['send_success']) ? $sendRecord[$item['user_id']]['send_success'] : 0;
  310. }
  311. return [$total,$list];
  312. }
  313. /*
  314. * 输出excel
  315. * @param $corpid
  316. * @param $ruleId
  317. * @param $sendNum
  318. * @param $sendDate
  319. * @param $type
  320. * @param $keyword
  321. * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
  322. * @return mixed
  323. */
  324. public static function senderListExport($corpid, $ruleId, $sendNum, $sendDate, $type,$keyword)
  325. {
  326. try {
  327. list($total, $list) = self::senderList($corpid, $ruleId, $sendNum, $sendDate, $type, $keyword, 1, 100000);
  328. $spreadSheet = new Spreadsheet();
  329. $sheet = $spreadSheet->getActiveSheet();
  330. $rowIndex = 1;
  331. $columnIndex = 1;
  332. /**设置标题**/
  333. $title = ['成员昵称', '群发时间', '好友数量', '已送达人数', '发送失败次数'];
  334. foreach ($title as $name) {
  335. $sheet->setCellValueByColumnAndRow($columnIndex++, $rowIndex, $name);
  336. }
  337. /**列表内容**/
  338. foreach ($list as $row => $item) {
  339. $rowIndex++;
  340. $columnIndex = 1; //重置列索引
  341. $sheet->setCellValueByColumnAndRow($columnIndex++, $rowIndex, $item['name']);
  342. $sheet->setCellValueByColumnAndRow($columnIndex++, $rowIndex, $item['send_time']);
  343. $sheet->setCellValueByColumnAndRow($columnIndex++, $rowIndex, $item['customer_num']);
  344. $sheet->setCellValueByColumnAndRow($columnIndex++, $rowIndex, $item['send_success']);
  345. $sheet->setCellValueByColumnAndRow($columnIndex++, $rowIndex, $item['send_fail']);
  346. }
  347. $exportType = [
  348. 'all' => '全部成员',
  349. 'sent' => '已发送成员',
  350. 'unsent' => '未发送成员',
  351. 'fail' => '发送失败成员',
  352. ];
  353. $fileNameExt = isset($exportType[$type]) ? $exportType[$type] : "列表";
  354. $writer = IOFactory::createWriter($spreadSheet, 'Xlsx');
  355. header('Content-Type:application/vnd.ms-excel');
  356. header('Content-Disposition:attachment;filename=客户群发成员详情导出-' . $fileNameExt . '.xlsx');
  357. header('Cache-Control:max-age=0');
  358. $writer->save('php://output');
  359. } catch (\Exception $e) {
  360. $logContent = [
  361. 'params' => "corpid=$corpid , ruleId=$ruleId , sendNum=$sendNum, sendDate=$sendDate, type=$type, keyword=$keyword",
  362. 'msg' => $e->getMessage(),
  363. 'line' => $e->getLine()
  364. ];
  365. EmailQueue::rPush('导出客户群发成员详情出现错误', json_encode($logContent), ['song.shen@kuxuan-inc.com'], '猎羽');
  366. return false;
  367. }
  368. }
  369. /*
  370. * 群发详情-客户详情列表
  371. */
  372. public static function massSendCustList($ruleId, $sendNum, $sendDate, $sender, $name, $type, $page, $pageSize)
  373. {
  374. return CircleMassMsgSendDetailEs::massSendCustList($ruleId, $sendNum, $sendDate, null, $sender, $name, $type, $page, $pageSize);
  375. }
  376. /*
  377. * 群发详情-客户详情列表
  378. **/
  379. public static function massSendCustListExport($ruleId, $sendNum, $sendDate, $sender, $name, $type)
  380. {
  381. try {
  382. $spreadSheet = new Spreadsheet();
  383. $sheet = $spreadSheet->getActiveSheet();
  384. $rowIndex = 1;
  385. $columnIndex = 1;
  386. /**设置标题**/
  387. $title = ['客户昵称','发送成员','送达时间','状态描述'];
  388. foreach ($title as $titleVal){
  389. $sheet->setCellValueByColumnAndRow($columnIndex++,$rowIndex,$titleVal);
  390. }
  391. $page = 1;
  392. $pageSize = 1000;
  393. while (1){
  394. list($list,$total) = CircleMassMsgSendDetailEs::massSendCustList($ruleId, $sendNum, $sendDate, null
  395. , $sender, $name, $type, $page++, $pageSize);
  396. /**列表内容**/
  397. foreach ($list as $row =>$item){
  398. $rowIndex++;
  399. $columnIndex=1; //重置列索引
  400. $sheet->setCellValueByColumnAndRow($columnIndex++,$rowIndex,$item['external_username']);
  401. $sheet->setCellValueByColumnAndRow($columnIndex++,$rowIndex,$item['sender_name']);
  402. $sheet->setCellValueByColumnAndRow($columnIndex++,$rowIndex,$item['send_time']);
  403. $sheet->setCellValueByColumnAndRow($columnIndex++,$rowIndex,$item['status_description']);
  404. }
  405. if ($total<$pageSize) break; //停止游标获取完整列表
  406. }
  407. $writer = IOFactory::createWriter($spreadSheet, 'Xlsx');
  408. header('Content-Type:application/vnd.ms-excel');
  409. header('Content-Disposition:attachment;filename=客户群发详情导出.xlsx');
  410. header('Cache-Control:max-age=0');
  411. $writer->save('php://output');
  412. } catch(\Exception $e) {
  413. $logContent = [
  414. 'params' => " ruleId=$ruleId , sendNum=$sendNum, sendDate=$sendDate, sender=$sender , name=$name , type=$type",
  415. 'msg' => $e->getMessage(),
  416. 'line' => $e->getLine()
  417. ];
  418. EmailQueue::rPush('导出客户群发详情出现错误', json_encode($logContent), ['song.shen@kuxuan-inc.com'], []);
  419. return false;
  420. }
  421. }
  422. }