企微短剧业务系统

CircleMassMsgRecordService.php 19KB

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