企微短剧业务系统

MassMsgRuleService.php 43KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026
  1. <?php
  2. namespace App\Service;
  3. use App\Log;
  4. use App\Models\AuthorizeCorp;
  5. use App\Models\CustomerAssignmentRecord;
  6. use App\Models\CustomerDetails;
  7. use App\Models\DjUser;
  8. use App\Models\MassMsg;
  9. use App\Models\MassMsgRecord;
  10. use App\Models\Material;
  11. use App\Models\OperationGroup;
  12. use App\Models\RadarCustomerDetail;
  13. use App\Models\System\Users;
  14. use App\Models\Tag;
  15. use App\Models\TencentAdConf;
  16. use App\Models\View\CustomerDetail;
  17. use App\RedisModel;
  18. use App\Support\EmailQueue;
  19. use PhpOffice\PhpSpreadsheet\IOFactory;
  20. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  21. use App\Models\Es\MassMsgSendDetailEs;
  22. class MassMsgRuleService
  23. {
  24. /*
  25. * 设置群发消息规则
  26. * */
  27. public static function setRule($ruleId, $params)
  28. {
  29. try {
  30. if(2 == $params['operate_type']){
  31. if(1 == $params['is_operation']) {
  32. $senders = MassMsgRuleService::getSenderListByOperatorGroupId($params['operator_group_id']);
  33. $params['senders'] = '';
  34. } else {
  35. $senders = json_decode($params['multiple_senders'], 1);
  36. $params['senders'] = $params['multiple_senders'];
  37. }
  38. $corpIdList = MassMsgRuleService::getCorpIdListBySenders($senders);
  39. $params['corpid'] = implode(',', $corpIdList);
  40. } else {
  41. # 校验附件信息合法性
  42. $attachmentsVerifyCode = MassMsgRuleService::attachmentsVerify($params['attachments'], $params['corpid']);
  43. if($attachmentsVerifyCode) return $attachmentsVerifyCode;
  44. }
  45. if($ruleId){ // 编辑群发规则
  46. unset($params['multiple_senders']);
  47. # 判断是否为未发送状态
  48. $ruleInfo = MassMsg::where('id', $ruleId)->first();
  49. if(!isset($ruleInfo->enable) || !in_array($ruleInfo->enable, [-1, 1])) {
  50. return 2102;
  51. }
  52. $result = MassMsg::where('id', $ruleId)->update($params);
  53. } else {
  54. # 设置新群发规则
  55. $massMsgModel = new MassMsg();
  56. $massMsgModel->admin_id = $params['admin_id'];
  57. $massMsgModel->corpid = $params['corpid'];
  58. $massMsgModel->name = $params['name'];
  59. $massMsgModel->send_type = $params['send_type'];
  60. $massMsgModel->send_time = $params['send_time'];
  61. $massMsgModel->senders = $params['senders'];
  62. $massMsgModel->chat_type = $params['chat_type'];
  63. $massMsgModel->customer_filter = $params['customer_filter'];
  64. $massMsgModel->content = $params['content'];
  65. $massMsgModel->attachments = html_entity_decode($params['attachments']);
  66. $massMsgModel->gender = $params['gender'];
  67. $massMsgModel->is_customize_time = $params['is_customize_time'];
  68. $massMsgModel->timeline = $params['timeline'];
  69. $massMsgModel->add_time_start = $params['add_time_start'];
  70. $massMsgModel->add_time_end = $params['add_time_end'];
  71. $massMsgModel->tag_screen_type = $params['tag_screen_type'];
  72. $massMsgModel->tag_list = $params['tag_list'];
  73. $massMsgModel->exclude_tag_list = $params['exclude_tag_list'];
  74. // 付费情况
  75. $massMsgModel->pay_status = $params['pay_status'];
  76. $massMsgModel->pay_num_min = $params['pay_num_min'];
  77. $massMsgModel->pay_num_max = $params['pay_num_max'];
  78. # 运营类型
  79. $massMsgModel->operate_type = $params['operate_type'];
  80. # 添加客服n days之后
  81. $massMsgModel->add_days_later = $params['add_days_later'];
  82. $massMsgModel->operator_group_id = $params['operator_group_id'];
  83. $massMsgModel->is_operation = $params['is_operation'];
  84. $result = $massMsgModel->save();
  85. $ruleId = isset($massMsgModel->id) ? $massMsgModel->id : '';
  86. }
  87. if(!$result) return 2103;
  88. if(2 == $params['operate_type']){
  89. RedisModel::lPush(MassMsg::MASS_MSG_ESTIMATED_MULTIPLE_CORP_USER_COUNT_RDS, $ruleId);
  90. } else {
  91. RedisModel::lPush(MassMsg::MASS_MSG_ESTIMATED_USER_COUNT_RDS, $ruleId);
  92. }
  93. } catch (\Exception $e) {
  94. Log::logError('群发规则设置发生异常', [
  95. 'line' => $e->getLine(),
  96. 'msg' => $e->getMessage(),
  97. 'params' => $params
  98. ], 'SetMassMsgRule');
  99. return 2101;
  100. }
  101. return 0;
  102. }
  103. public static function getSenderListByOperatorGroupId($operatorGroupId)
  104. {
  105. $errno = 0;
  106. $operationDetail = OperationGroupService::groupDetail($operatorGroupId, -1, $errno);
  107. if($errno > 0) {
  108. EmailQueue::rPush('根据运营组ID查询运营信息失败', json_encode(['group_id' => $operatorGroupId]), ['song.shen@kuxan-inc.com'], '猎羽');
  109. return [];
  110. }
  111. return array_map(function($item) {
  112. $item['corpid'] = $item['app_id'];
  113. return $item;
  114. },json_decode(json_encode($operationDetail->corp_user_list), 1));
  115. }
  116. /*
  117. * 获取群发条件匹配的外部联系人数
  118. * */
  119. public static function getCustomerMatchCount($params, &$total)
  120. {
  121. try {
  122. $corpid = $params['corpid'];
  123. if(!is_array($params['senders'])) {
  124. $senders = !empty($params['senders']) ? explode(',', $params['senders']) : [];
  125. } else {
  126. $senders = $params['senders'];
  127. }
  128. // $search = array('loss_status' => 1);
  129. $search = array('can_receive' => 1, 'loss_status' => 1);
  130. $customerFilter = $params['customer_filter'];
  131. if($customerFilter){
  132. $gender = (!is_null($params['gender']) && $params['gender'] != '') ? explode(',', $params['gender']) : [];
  133. $isCustomizeTime = $params['is_customize_time'];
  134. if($isCustomizeTime) {
  135. $addTimeStart = $params['add_time_start'];
  136. $addTimeEnd = $params['add_time_end'];
  137. } else {
  138. $timeLine = $params['timeline'];
  139. $sendTime = is_null($params['send_time']) ? date('Y-m-d H:i:s') : $params['send_time'];
  140. $addTimeStart = date('Y-m-d H:i:s', strtotime('-'.$timeLine.' hours', strtotime($sendTime)));
  141. $addTimeEnd = $sendTime;
  142. }
  143. $addDaysLater = $params['add_days_later'] ?? null;
  144. if($addDaysLater) {
  145. $addDaysStart = null;
  146. $addDaysEnd = date('Y-m-d H:i:s', strtotime('-'.$addDaysLater.' days', time()));
  147. $addTimeStart = !empty($addTimeStart) ? $addTimeStart : $addDaysStart;
  148. if(!empty($addTimeEnd)) {
  149. $addTimeEnd = ($addTimeEnd < $addDaysEnd) ? $addTimeEnd : $addDaysEnd;
  150. } else {
  151. $addTimeEnd = $addDaysEnd;
  152. }
  153. }
  154. $tagScreenType = $params['tag_screen_type'] ?? null;
  155. $tagList = $params['tag_list'] ?? null;
  156. $excludeTagList = $params['exclude_tag_list'] ?? null;
  157. // 付费情况
  158. $payStatus = $params['pay_status'];
  159. $payNumMin = $params['pay_num_min'];
  160. $payNumMax = $params['pay_num_max'];
  161. if(!empty($gender)) {
  162. $search['gender'] = $gender;
  163. }
  164. if($addTimeStart) {
  165. $addTimeStartTimestamp = strtotime($addTimeStart);
  166. if($addTimeStartTimestamp !== false) {
  167. $search['add_time_start'] = $addTimeStartTimestamp;
  168. }
  169. }
  170. if($addTimeEnd) {
  171. $addTimeEndTimestamp = strtotime($addTimeEnd);
  172. if($addTimeEndTimestamp !== false) {
  173. $search['add_time_end'] = $addTimeEndTimestamp;
  174. }
  175. }
  176. if($tagScreenType) {
  177. $search['tag_screen_type'] = $tagScreenType;
  178. if($tagScreenType == 1 && empty($tagList)) {
  179. $total = 0;
  180. return 400;
  181. }
  182. $search['tag_list'] = explode(',', $tagList);
  183. # 检测标签内容为标签名称还是md5值,若为前者则将标签名称替换
  184. if(isset($search['tag_list'][0]) && strlen($search['tag_list'][0]) != 32) {
  185. $search['tag_list'] = Tag::getTagListByName($corpid, $search['tag_list']);
  186. }
  187. }
  188. if($excludeTagList) {
  189. $search['exclude_tag_list'] = explode(',', $excludeTagList);
  190. if(isset($search['exclude_tag_list'][0]) && strlen($search['exclude_tag_list'][0]) != 32) {
  191. $search['exclude_tag_list'] = Tag::getTagListByName($corpid, $search['exclude_tag_list']);
  192. }
  193. }
  194. if (is_numeric($payStatus)) {
  195. $search['pay_status'] = $payStatus;
  196. }
  197. if (is_numeric($payNumMin) && $payNumMin > 0) {
  198. $search['pay_num_min'] = $payNumMin;
  199. }
  200. if (is_numeric($payNumMax) && $payNumMax > 0) {
  201. $search['pay_num_max'] = $payNumMax;
  202. }
  203. }
  204. if(!empty($senders)) {
  205. $search['user_ids'] = $senders;
  206. }
  207. Log::logInfo('', $search, 'multiple_corp_tag_test');
  208. $total = CustomerDetails::getCustomerMatchCount($corpid, $search);
  209. } catch (\Exception $e) {
  210. Log::logError('获取群发条件匹配的外部联系人数过程发生异常', [
  211. 'line' => $e->getLine(),
  212. 'msg' => $e->getMessage()
  213. ], 'GetCustomerTotal-Exception');
  214. $total = 0;
  215. }
  216. return 0;
  217. }
  218. public static function getMultipleCorpCustomerMatchCount($params, &$total)
  219. {
  220. try {
  221. $data = $params;
  222. if(1 == $params['is_operation']) {
  223. $senders = self::getSenderListByOperatorGroupId($params['operator_group_id']);//从运营组中查询客服以及企微信息
  224. } else {
  225. $senders = json_decode($params['multiple_senders'], 1);
  226. }
  227. foreach ($senders as $item) {
  228. $singleTotal = 0;
  229. $data['corpid'] = $item['corpid'];
  230. $data['senders'] = implode(',', $item['user_list']);
  231. self::getCustomerMatchCount($data, $singleTotal);
  232. $total += $singleTotal;
  233. }
  234. } catch (\Exception $exception) {
  235. Log::logError('多企微获取群发条件匹配的外部联系人数过程发生异常', [
  236. 'params' => $params,
  237. 'line' => $exception->getLine(),
  238. 'msg' => $exception->getMessage()
  239. ], 'GetCustomerTotal-Exception');
  240. EmailQueue::rPush('多企微获取群发条件匹配的外部联系人数过程发生异常', '', ['song.shen@kuxuan-inc.com'], '猎羽');
  241. }
  242. return 0;
  243. }
  244. /*
  245. * 更新群发规则状态
  246. * @param $ruleId integer 群发规则ID
  247. * @param $status integer 群发规则待更新到的状态值 状态 -2:已删除 -1:发送失败 1:待发送 2:正在发送中 3:发送完成
  248. * */
  249. public static function updateStatus($ruleId, $status)
  250. {
  251. # 修改群发规则状态为已发送
  252. $updateStatus = MassMsg::updateRuleStatus($ruleId, $status);
  253. if(!$updateStatus) {
  254. Log::logError('规则状态更新失败', [
  255. 'rule_id' => $ruleId,
  256. 'status' => $status
  257. ], 'updateRuleStatus');
  258. }
  259. return $updateStatus;
  260. }
  261. /**
  262. * 更新群发状态
  263. * */
  264. public static function updateRuleStatus($corpid, $ruleId, $status)
  265. {
  266. # 验证规则是否存在
  267. $isExist = MassMsg::where('id', $ruleId)->exists();
  268. if(!$isExist) return 2106;
  269. # 变更规则状态
  270. $result = MassMsg::where('id', $ruleId)->update(['enable' => $status]);
  271. if(!$result) return 2107;
  272. return 0;
  273. }
  274. /*
  275. * 获取群发详情
  276. * @param $corpid string 企业ID
  277. * @param $ruleId integer 群发规则ID
  278. * */
  279. public static function ruleDetail($corpid, $ruleId, &$errno)
  280. {
  281. try{
  282. $detail = MassMsg::selectRaw('id as rule_id, admin_id, corpid, name, send_type, send_time,'
  283. .' senders, content, attachments, enable, gender, add_time_start, add_time_end, tag_screen_type, tag_list,'
  284. .' exclude_tag_list, customer_filter, estimated_user, pay_status, pay_num_min, pay_num_max,'
  285. .' is_customize_time, timeline, operate_type, operator_group_id, add_days_later, is_operation')
  286. ->where('id', $ruleId)->first();
  287. if(empty($detail)) return [];
  288. if(1 == $detail->operate_type) {
  289. # 发送人
  290. $detail->sender_name = DjUser::where("corpid",$corpid)
  291. ->whereIn("user_id",explode(',',$detail->senders))
  292. ->pluck("name")->toArray();
  293. $detail->sender_group = null;
  294. } else {
  295. $senderList = [];
  296. $senderSqlList = [];
  297. $senderArray = json_decode($detail->senders, 1);
  298. if(1 != $detail->is_operation) {
  299. # 直接查询
  300. foreach($senderArray as $value) {
  301. foreach($value['user_list'] as $userId) {
  302. $senderList[] = ['corpid' => $value['corpid'], 'user_id' => $userId];
  303. $senderSqlList[] = '("'.$value['corpid'].'","'.$userId.'")';
  304. }
  305. }
  306. $corpIdList = array_unique(array_column($senderArray, 'corpid'));
  307. $corpInfoList = AuthorizeCorp::whereIn('corpid', $corpIdList)->get();
  308. if(empty($senderSqlList)) {
  309. $senderInfoList = DjUser::where('enable', 1)->get();
  310. } else {
  311. $senderInfoList = DjUser::whereRaw('(corpid, user_id) in (' . implode(',',$senderSqlList) . ')')
  312. ->where('enable', 1)->get();
  313. }
  314. $senderNameList = [];
  315. foreach ($senderList as $item) {
  316. $corpName = $corpInfoList->where('corpid', $item['corpid'])->first();
  317. $corpName = $corpName->corp_name ?? '';
  318. $senderName = $senderInfoList->where('corpid', $item['corpid'])->where('user_id', $item['user_id'])->first();
  319. $senderName = $senderName->name ?? '';
  320. $senderNameList[] = $corpName.'-'.$senderName;
  321. }
  322. $detail->sender_name = $senderNameList;
  323. $detail->multiple_senders = json_decode($detail->senders, 1);
  324. $detail->sender_group = null;
  325. } else {
  326. # 查询运营组信息
  327. $detail->sender_name = null;
  328. $detail->sender_group = OperationGroup::where('id', $detail->operator_group_id)
  329. ->value('group_name');
  330. }
  331. }
  332. # 获取创建人信息
  333. $detail->creator = Users::where('id', $detail->admin_id)->value('name');
  334. $attachments = json_decode($detail->attachments, true);
  335. if(!empty($attachments)) {
  336. foreach ($attachments as $key=>&$attachment) {
  337. if(isset($attachment['msgtype']) && $attachment['msgtype'] == 'radar') { // 雷达附件信息回显
  338. $radarId = $attachment['radar']['radar_id'] ?? 0;
  339. $radarInfo = RadarService::getRadarContent($corpid, $radarId);
  340. if(empty($radarInfo)) {
  341. unset($attachment[$key]);
  342. continue;
  343. }
  344. $attachment['radar'] = $radarInfo;
  345. }
  346. }
  347. }
  348. # 消息条数
  349. $contentCount = empty($detail->content) ? 0 : 1;
  350. $attachmentCount = empty($detail->attachments) ? 0 : count($attachments);
  351. $detail->msg_count = $contentCount + $attachmentCount;
  352. $detail->attachments = json_encode($attachments, 256);
  353. } catch (\Exception $e) {
  354. Log::logError('群发详情获取过程发生异常', [
  355. 'line' => $e->getLine(),
  356. 'msg' => $e->getMessage(),
  357. 'rule_id' => $ruleId
  358. ], 'ruleDetail');
  359. $errno = 2104;
  360. return [];
  361. }
  362. return $detail;
  363. }
  364. /*
  365. * 获取群发列表
  366. * @param $corpid string 企业ID
  367. * @param $creatorId integer 创建人ID集合
  368. * @param $sendTimeStart string 发送时间查询-起始时间
  369. * @param $sendTimeEnd string 发送时间查询-结束时间
  370. * @param $createTimeStart string 创建时间查询-结束时间
  371. * @param $createTimeEnd string 创建时间查询-结束时间
  372. * @param $sortColumn string 排序字段
  373. * @param $sortMethod string 排序方式*
  374. * @param $page integer 当前页码数
  375. * @param $pageSize integer 每页显示条数
  376. * @param $errno
  377. * @return mixed
  378. * */
  379. public static function ruleList($corpid, $creatorId, $keyword, $sendTimeStart, $sendTimeEnd, $createTimeStart,$createTimeEnd
  380. ,$sortColumn,$sortMethod, $page, $pageSize, &$errno)
  381. {
  382. try {
  383. list($list, $count) = MassMsg::getRuleLists(
  384. $corpid, $creatorId, $keyword, $sendTimeStart, $sendTimeEnd,$createTimeStart
  385. ,$createTimeEnd,$sortColumn,$sortMethod, $page, $pageSize
  386. );
  387. # 获取创建人信息
  388. $adminIds = $list->pluck('admin_id');
  389. $adminData = Users::query()->select(['id','name'])->whereIn('id', $adminIds)->get();
  390. # 统计发送信息
  391. $ruleIds = $list->pluck('rule_id');
  392. $sendStatData = MassMsgRecord::selectRaw("rule_id, sum(send_success) as send_success, "
  393. ."sum(send_fail) as send_fail, sum(send_total) as send_total")
  394. ->whereIn('rule_id', $ruleIds)->where('type', 1)->groupBy(['rule_id'])->get();
  395. $errStatData = MassMsgRecord::query()->select(['errcode', 'rule_id', 'sender', 'corpid'])
  396. ->whereIn('rule_id', $ruleIds)->where('errcode', '!=', 0)->get();
  397. $senderIdList = [];
  398. foreach ($errStatData as $item) {
  399. $senderIdList[] = '("'.$item['corpid'].'","'.$item['sender'].'")';
  400. }
  401. if(!empty($senderIdList)) {
  402. $senderInfoList = DjUser::query()->whereRaw('(corpid, user_id) in ('. implode(',', $senderIdList).')')
  403. ->where('enable', 1)->get();
  404. } else {
  405. $senderInfoList = DjUser::query()->where('enable', 1)->get();
  406. }
  407. $corpInfoList = AuthorizeCorp::query()->get();
  408. $errcodeConf = config('qyWechat.errcode');
  409. # 处理数据
  410. foreach($list as $datum) {
  411. # 展示企微名称
  412. $corpIdList = explode(',', $datum->corpid);
  413. $corpDataList = $corpInfoList->whereIn('corpid', $corpIdList)->all();
  414. $datum->corp_name = array_column($corpDataList, 'corp_name');
  415. # 消息条数
  416. $contentCount = empty($datum->content) ? 0 : 1;
  417. $attachmentCount = empty($datum->attachments) ? 0 : count(json_decode($datum->attachments, true));
  418. $datum->msg_count = $contentCount + $attachmentCount;
  419. # 创建人信息
  420. $adminInfo = $adminData->where('id', $datum->admin_id)->first();
  421. $datum->creator = isset($adminInfo->name) ? $adminInfo->name : '';
  422. # 统计发送情况
  423. $sendStatInfo = $sendStatData->where('rule_id', $datum->rule_id)->first();
  424. $datum->send_success = isset($sendStatInfo->send_success) ? $sendStatInfo->send_success : 0;
  425. $datum->send_fail = isset($sendStatInfo->send_fail) ? $sendStatInfo->send_fail : 0;
  426. $datum->send_total = isset($sendStatInfo->send_total) ? $sendStatInfo->send_total : 0;
  427. unset($datum->admin_id);
  428. # 查询失败信息
  429. $errList = $errStatData->where('rule_id', $datum->rule_id)->all();
  430. $err_msg_list = [];
  431. if(!empty($errList)){
  432. foreach ($errList as $item) {// 无可发送的客户
  433. // 处理原因:当一次需要发送的客户数很多时,需要处理成多次请求发送,最后一次请求时人数不足,
  434. // 导致有可能这一批次所有人均无法正常发送,此种为正常情况,不予展示
  435. if($datum->enable != -1 && $item['errcode'] == 41048) {
  436. continue;
  437. }
  438. $err_msg = $errcodeConf[$item['errcode']] ?? null;
  439. if(60111 == $item['errcode']) {//UserID不存在
  440. // 当出现此种错误时,将客服名称一并拼接起来展示
  441. $senderName = $senderInfoList->where('corpid', $item['corpid'])
  442. ->where('user_id', $item['sender'])->first();
  443. $senderName = $senderName->name ?? '';
  444. if(1 == $datum->operate_type) {
  445. $err_msg = $senderName.$err_msg;
  446. } else {
  447. $corpName = $corpInfoList->where('corpid', $item['corpid'])->first();
  448. $corpName = $corpName->corp_name ?? '';
  449. $err_msg = $corpName.$senderName.$err_msg;
  450. }
  451. }
  452. $err_msg = $datum->enable != -1 ? '部分失败由于'.$err_msg : $err_msg;
  453. if(!empty($err_msg) && !in_array($err_msg, $err_msg_list)) {
  454. $err_msg_list[] = $err_msg;
  455. }
  456. }
  457. }
  458. $datum->err_msg = $err_msg_list;
  459. }
  460. } catch (\Exception $e) {
  461. Log::logError('获取群发列表过程发生异常', [
  462. 'line' => $e->getLine(),
  463. 'msg' => $e->getMessage(),
  464. ], 'MassMsgRuleList');
  465. $errno = 2105;
  466. return [[], 0];
  467. }
  468. return [$list, $count];
  469. }
  470. /*
  471. * 成员消息群发未发送提醒功能
  472. * @param $corpid
  473. * @param $ruleId
  474. * @return int
  475. */
  476. public static function noticeUser($corpid, $ruleId)
  477. {
  478. #获取规则信息
  479. $rule_info = MassMsg::query()->where("id",$ruleId)->first();
  480. if(empty($rule_info)) {
  481. return 1001; //参数错误
  482. }
  483. #检查是否是待发送状态
  484. if(!in_array($rule_info->enable,[1,2])){
  485. return 2108; //只有待发送和发送中的可以操作提醒
  486. }
  487. if(2 == $rule_info->operate_type) {
  488. return 0;
  489. }
  490. # 群发规则
  491. $toPublishedUserList = explode(',',$rule_info->senders);
  492. /***规则处于正在发送的状态,排除已发送过的成员名单***/
  493. if($rule_info->enable == 2){
  494. #已发送过的成员名单集合
  495. $sent_user_id_arr = MassMsgRecord::query()->where("rule_id",$ruleId)
  496. ->pluck("sender")->toArray(); //已发送过的成员列表
  497. foreach ($toPublishedUserList as $k=>$sender_user_id){
  498. if(in_array($sender_user_id,$sent_user_id_arr)){
  499. unset($toPublishedUserList[$k]);
  500. }
  501. }
  502. }
  503. if(empty($toPublishedUserList)) return 2109; //群发消息指定成员为空
  504. # 发送消息提醒
  505. $responseData = ApplicationMsgService::sendTextMsg($corpid, '请及时确认客户群发消息任务。', $toPublishedUserList);
  506. if(isset($responseData['errcode']) && $responseData['errcode'] != 0) {
  507. $logData = [
  508. 'corpid' => $corpid,
  509. 'rule_id' => $ruleId,
  510. 'responseData' => $responseData
  511. ];
  512. EmailQueue::rPush('发送客户群发消息提醒失败', json_encode($logData, JSON_UNESCAPED_UNICODE), ['zhiyuan.chen@kuxuan-inc.com'], '发送客户群发消息提醒失败');
  513. Log::logError('发送客户群发消息提醒失败', $logData, 'noticeUserFailed');
  514. return 2512;
  515. }
  516. # 记录发送日志
  517. Log::logInfo('发送客户群发消息提醒成功', [
  518. 'corpid' => $corpid,
  519. 'rule_id' => $ruleId,
  520. 'responseData' => $responseData
  521. ], 'noticeUserLog');
  522. return 0;
  523. }
  524. /*
  525. * 统计客户群发消息的数据概览
  526. * @param $corpid
  527. * @param $ruleId
  528. */
  529. public static function overview($corpid, $ruleId)
  530. {
  531. $send_record_total = MassMsgRecord::query()
  532. ->where("rule_id",$ruleId)
  533. // ->selectRaw("count(CASE WHEN status = 1 THEN distinct(user_id) END) as un_sender_count") // 未发送成员
  534. // ->selectRaw("count(CASE WHEN status = 2 THEN distinct(user_id) END) as sender_count") // 已发送成员
  535. ->selectRaw("sum(send_success) as send_success") // 发送成功
  536. ->selectRaw("sum(send_fail) as send_fail") // 未送达人数
  537. ->first();
  538. $send_record_total->send_success = empty($send_record_total->send_success)? 0 : $send_record_total->send_success;
  539. $send_record_total->send_fail = empty($send_record_total->send_fail)?0:$send_record_total->send_fail;
  540. $send_time = MassMsg::query()->where('id', $ruleId)->value('send_time');
  541. $senderList = MassMsgRecord::query()->where('rule_id', $ruleId)
  542. ->where('status', 2)->selectRaw('sender, corpid')->groupBy(['corpid', 'sender'])->get();
  543. $senderCount = count($senderList);
  544. $unSenderList = MassMsgRecord::query()->where('rule_id', $ruleId)
  545. ->where('status', 1)->selectRaw('sender, corpid')->groupBy(['corpid', 'sender'])->get();
  546. $unSenderCount = count($unSenderList);
  547. $ret_data = [];
  548. #已发送人员
  549. $ret_data['executed_sender_count'] = $senderCount;
  550. #发送用户
  551. $ret_data['receive_customer_count'] = $send_record_total->send_success;
  552. #未发送成员
  553. $ret_data['un_execute_sender_count'] = $unSenderCount;
  554. #未送达客户
  555. $ret_data['un_receive_customer_count'] = $send_record_total->send_fail ;
  556. #客户接收达上限
  557. $ret_data['receive_fail_with_limit'] = MassMsgSendDetailEs::massSendCustCount($ruleId,$send_time,3);
  558. #因为不是好友发送失败
  559. $ret_data['receive_fail_with_not_friend'] = MassMsgSendDetailEs::massSendCustCount($ruleId,$send_time,4);
  560. return $ret_data;
  561. }
  562. /*
  563. * 成员详情列表
  564. * @param $corpid
  565. * @param $ruleId
  566. * @param $search_type [选择查询的类型:all、sent、unsent、fail]
  567. * @param $keyword
  568. * @param $page
  569. * @param $page_size
  570. */
  571. public static function sender_list($corpid, $ruleId, $search_type, $keyword, $page, $page_size)
  572. {
  573. if(500 == $page_size) {
  574. return self::get_all_sender_list($ruleId);
  575. }
  576. /**已发送成员统计**/
  577. $send_record = MassMsgRecord::where("rule_id",$ruleId)
  578. ->select(["corpid", "sender"])
  579. ->selectRaw("min(send_time) as send_time") //发送时间
  580. ->selectRaw("sum(send_fail) as send_fail") //失败次数
  581. ->selectRaw("sum(send_success) as send_success") //成功次数
  582. ->where('type', 1)
  583. ->groupBy(["corpid", "sender"])
  584. ->get();
  585. #根据查询类型不同,获取当页展示的成员ID集合
  586. $all_senders_id_arr = [];
  587. switch ($search_type){
  588. case "all": //全部成员
  589. $all_senders_id_arr = MassMsgRecord::query()->select(["corpid", "sender"])->where("rule_id",$ruleId)
  590. ->get()->toArray();
  591. break;
  592. case "sent": //已发送的成员
  593. $all_senders_id_arr = MassMsgRecord::query()->select(["corpid", "sender"])->where("rule_id",$ruleId)
  594. ->where('status', 2)->get()->toArray();
  595. break;
  596. case "unsent": //未发送的成员
  597. $all_senders_id_arr = MassMsgRecord::query()->select(["corpid", "sender"])->where("rule_id",$ruleId)
  598. ->where('status', 1)->get()->toArray();
  599. break;
  600. case "fail": //发送失败的成员
  601. $all_senders_id_arr = MassMsgRecord::query()->select(["corpid", "sender"])->where("rule_id",$ruleId)
  602. ->where('status', -1)->get()->toArray();
  603. break;
  604. }
  605. $all_senders_id_arr = array_map(function($item){
  606. return '("' . $item['corpid'] . '","' . $item['sender'] . '")';
  607. }, $all_senders_id_arr);
  608. if(empty($all_senders_id_arr)){
  609. return [0, []];
  610. }
  611. /**查询发送成员**/
  612. $query = DjUser::whereRaw("(corpid, user_id) in (" . implode(',', $all_senders_id_arr) . ')');
  613. /**关键词查询**/
  614. if(!empty($keyword)){
  615. $query->where("name","like","%$keyword%");
  616. }
  617. $total = $query->count(); //总计总数
  618. $list = $query->select("user_id", "name", "avatar", "corpid")
  619. ->offset(($page-1)*$page_size)
  620. ->limit($page_size)
  621. ->get()->toArray();
  622. $corpInfoList = AuthorizeCorp::where('enable', 1)->get();
  623. foreach ($list as $k => $item) {
  624. #补充最早群发时间
  625. $sendInfo = $send_record->where('corpid', $item['corpid'])->where('sender', $item['user_id'])->first();
  626. $sendTime = $sendInfo->send_time ?? '';
  627. $list[$k]['send_time'] = $sendTime;
  628. #补充好友数量
  629. $list[$k]['customer_num'] = CustomerDetails::suffix($item['corpid'])->where('corpid', $item['corpid'])
  630. ->where('user_id', $item['user_id'])->where('enable', 1)->whereIn('loss_status', [0, 1])->count();
  631. #已送达人数
  632. $sendFail = $sendInfo->send_fail ?? 0;
  633. $list[$k]['send_fail'] = $sendFail;
  634. #未送达人数
  635. $sendSuccess = $sendInfo->send_success ?? 0;
  636. $list[$k]['send_success'] = $sendSuccess;
  637. $corpInfo = $corpInfoList->where('corpid', $item['corpid'])->first();
  638. $list[$k]['corp_name'] = $corpInfo->corp_name ?? '';
  639. }
  640. return [$total,$list];
  641. }
  642. public static function get_all_sender_list($ruleId) {
  643. $all_senders_id_arr = MassMsgRecord::query()->select(["corpid", "sender"])->where("rule_id",$ruleId)
  644. ->get()->toArray();
  645. $all_senders_id_arr = array_map(function($item){
  646. return '("' . $item['corpid'] . '","' . $item['sender'] . '")';
  647. }, $all_senders_id_arr);
  648. if(empty($all_senders_id_arr)){
  649. return [0, []];
  650. }
  651. /**查询发送成员**/
  652. $query = DjUser::whereRaw("(corpid, user_id) in (" . implode(',', $all_senders_id_arr) . ')');
  653. $total = $query->count(); //总计总数
  654. $list = $query->select("user_id", "name", "avatar", "corpid")->get()->toArray();
  655. $corpInfoList = AuthorizeCorp::where('enable', 1)->get();
  656. foreach ($list as $k => $item) {
  657. $corpInfo = $corpInfoList->where('corpid', $item['corpid'])->first();
  658. $list[$k]['corp_name'] = $corpInfo->corp_name ?? '';
  659. }
  660. return [$total,$list];
  661. }
  662. /*
  663. * 输出excel
  664. * @param $corpid
  665. * @param $ruleId
  666. * @param $type
  667. * @param $keyword
  668. * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
  669. */
  670. public static function sender_list_export($corpid, $ruleId,$type,$keyword)
  671. {
  672. try {
  673. list( $total,$list ) = self::sender_list($corpid,$ruleId,$type,$keyword,1,100000);
  674. $spreadsheet = new Spreadsheet();
  675. $sheet = $spreadsheet->getActiveSheet();
  676. $row_index = 1;
  677. $column_index = 1;
  678. /**设置标题**/
  679. $title = ['企微主体', '成员昵称', '群发时间', '好友数量', '已送达人数', '发送失败次数'];
  680. foreach ($title as $name){
  681. $sheet->setCellValueByColumnAndRow($column_index++,$row_index,$name);
  682. }
  683. /**列表内容**/
  684. foreach ($list as $row =>$item){
  685. $row_index++;
  686. $column_index=1; //重置列索引
  687. $sheet->setCellValueByColumnAndRow($column_index++,$row_index,$item['corp_name']);
  688. $sheet->setCellValueByColumnAndRow($column_index++,$row_index,$item['name']);
  689. $sheet->setCellValueByColumnAndRow($column_index++,$row_index,$item['send_time']);
  690. $sheet->setCellValueByColumnAndRow($column_index++,$row_index,$item['customer_num']);
  691. $sheet->setCellValueByColumnAndRow($column_index++,$row_index,$item['send_success']);
  692. $sheet->setCellValueByColumnAndRow($column_index++,$row_index,$item['send_fail']);
  693. }
  694. $export_type = [
  695. 'all' => '全部成员',
  696. 'sent' => '已发送成员',
  697. 'unsent' => '未发送成员',
  698. 'fail' => '发送失败成员',
  699. ];
  700. $file_name_ext = isset($export_type[$type])?$export_type[$type]:"列表";
  701. $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
  702. header('Content-Type:application/vnd.ms-excel');
  703. header('Content-Disposition:attachment;filename=客户群发成员详情导出-'.$file_name_ext.'.xlsx');
  704. header('Cache-Control:max-age=0');
  705. $writer->save('php://output');
  706. } catch(\Exception $e) {
  707. $log_content = [
  708. 'params' => "corpid=$corpid , ruleId=$ruleId , type=$type , keyword=$keyword",
  709. 'msg' => $e->getMessage(),
  710. 'line' => $e->getLine()
  711. ];
  712. EmailQueue::rPush('导出客户群发成员详情出现错误', json_encode($log_content), ['zhiyuan.chen@kuxuan-inc.com'], []);
  713. return false;
  714. }
  715. }
  716. /*
  717. * 群发详情-客户详情列表
  718. */
  719. public static function massSendCustList($rule_id, $sender, $name, $type, $page, $pagesize, $senderCorpid)
  720. {
  721. $send_time = MassMsg::where('id', $rule_id)->value('send_time');
  722. if(empty($send_time)){
  723. return [[], 0];
  724. }
  725. # 获取非重试情况下的msgId
  726. $msgIds = MassMsgRecord::where('rule_id', $rule_id)->where('type', 1)->where('msg_id', '>', '')->pluck('msg_id')->toArray();
  727. list($data, $count) = MassMsgSendDetailEs::massSendCustList($rule_id, $send_time, $sender, $name, $type, $page, $pagesize, $msgIds, $senderCorpid);
  728. $corpIdList = array_unique(array_column($data, 'corp_id'));
  729. $corpDataList = AuthorizeCorp::query()->whereIn('corpid', $corpIdList)->get();
  730. foreach ($data as $k => $item) {
  731. $corpName = $corpDataList->where('corpid', $item['corp_id'])->first();
  732. $data[$k]['corp_name'] = $corpName->corp_name ?? '';
  733. }
  734. return [$data, $count];
  735. }
  736. /**
  737. * 群发详情-客户详情列表
  738. **/
  739. public static function massSendCustListExport($rule_id, $sender, $name, $type, $senderCorpid)
  740. {
  741. try {
  742. $send_time = MassMsg::where('id', $rule_id)->value('send_time');
  743. if(empty($send_time)){
  744. return [[], 0];
  745. }
  746. $spreadsheet = new Spreadsheet();
  747. $sheet = $spreadsheet->getActiveSheet();
  748. $row_index = 1;
  749. $column_index = 1;
  750. /**设置标题**/
  751. $title = ['客户昵称', '企微主体', '发送成员', '送达时间', '状态描述'];
  752. foreach ($title as $title_val){
  753. $sheet->setCellValueByColumnAndRow($column_index++,$row_index,$title_val);
  754. }
  755. $page = 1;
  756. $pagesize = 1000;
  757. # 获取非重试情况下的msgId
  758. $msgIds = MassMsgRecord::where('rule_id', $rule_id)->where('msg_id', '>', '')->where('type', 1)
  759. ->where(function($query) use($sender) {
  760. if($sender) $query->where('sender', $sender);
  761. })
  762. ->pluck('msg_id')->toArray();
  763. while (1){
  764. list($list,$total) = MassMsgSendDetailEs::massSendCustList($rule_id, $send_time, $sender, $name, $type
  765. , $page++, $pagesize, $msgIds, $senderCorpid);
  766. $corpIdList = array_unique(array_column($list, 'corp_id'));
  767. $corpDataList = AuthorizeCorp::query()->whereIn('corpid', $corpIdList)->get();
  768. /**列表内容**/
  769. foreach ($list as $row =>$item){
  770. $corpName = $corpDataList->where('corpid', $item['corp_id'])->first();
  771. $corpName = $corpName->corp_name ?? '';
  772. $row_index++;
  773. $column_index=1; //重置列索引
  774. $sheet->setCellValueByColumnAndRow($column_index++,$row_index,$item['external_username']);
  775. $sheet->setCellValueByColumnAndRow($column_index++,$row_index,$corpName);
  776. $sheet->setCellValueByColumnAndRow($column_index++,$row_index,$item['sender_name']);
  777. $sheet->setCellValueByColumnAndRow($column_index++,$row_index,$item['send_time']);
  778. $sheet->setCellValueByColumnAndRow($column_index++,$row_index,$item['status_description']);
  779. }
  780. if ($total<$pagesize) break; //停止游标获取完整列表
  781. }
  782. $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
  783. header('Content-Type:application/vnd.ms-excel');
  784. header('Content-Disposition:attachment;filename=客户群发详情导出.xlsx');
  785. header('Cache-Control:max-age=0');
  786. $writer->save('php://output');
  787. } catch(\Exception $e) {
  788. $log_content = [
  789. 'params' => " ruleId=$rule_id , sender=$sender , name=$name , type=$type",
  790. 'msg' => $e->getMessage(),
  791. 'line' => $e->getLine()
  792. ];
  793. Log::logError('导出客户群发详情出现错误', $log_content, 'MassSendCustomerListExport');
  794. return false;
  795. }
  796. }
  797. /*
  798. * 校验群发消息的附件信息合法性
  799. * */
  800. public static function attachmentsVerify($attachments, $corpid) {
  801. if(empty($attachments)) {
  802. return 0;
  803. }
  804. if(!is_array($attachments))
  805. $attachments = json_decode($attachments, true);
  806. foreach ($attachments as $attachment) {
  807. $msgType = $attachment['msgtype'] ?? '';
  808. switch($msgType) {
  809. case 'miniprogram':
  810. # 检出小程序路径中的appId信息
  811. $path = $attachment[$msgType]['page'] ?? '';
  812. $pathData = parse_url($path);
  813. if(isset($pathData['query']) && $pathData['query']) {
  814. parse_str($pathData['query'], $query);
  815. $appId = $query['wxAppId'] ?? '';
  816. if(!empty($appId)) { // 存在appId,校验与当前corpid是否关联
  817. $isExist = TencentAdConf::where('app_id', $appId)->where('enable', 1)
  818. ->where('corp_id', $corpid)->exists();
  819. if(!$isExist) return 2110;
  820. }
  821. }
  822. break;
  823. }
  824. }
  825. return 0;
  826. }
  827. public static function getCorpIdListBySenders($senders) {
  828. $corpIdList = array_column($senders, 'corpid');
  829. $corpIdList = array_unique($corpIdList);
  830. return $corpIdList;
  831. }
  832. public static function getLicenseExpirationUserList($params) {
  833. $corpid = $params['corpid'];
  834. # 查询企微名称
  835. $corpName = AuthorizeCorp::query()->where('corpid', $corpid)->where('enable', 1)->value('corp_name');
  836. if(!is_array($params['senders'])) {
  837. $senders = !empty($params['senders']) ? explode(',', $params['senders']) : [];
  838. } else {
  839. $senders = $params['senders'];
  840. }
  841. $userList = DjUser::query()->where('corpid', $corpid)->where(function($query) use ($senders){
  842. if(!empty($senders)) $query->whereIn('user_id', $senders);
  843. })->whereRaw('(is_active = 0 or expire_time < "'.date('Y-m-d 00:00:00').'")')
  844. ->where('enable', 1)->where('status', 1)->pluck('name')->toArray();
  845. $item = [
  846. 'corpId' => $corpid,
  847. 'corpName' => $corpName,
  848. 'senders' => $userList
  849. ];
  850. return $item;
  851. }
  852. public static function getMultipleCorpLicenseExpirationUserList($params) {
  853. try {
  854. $data = $params;
  855. if(1 == $params['is_operation']) {
  856. $senders = self::getSenderListByOperatorGroupId($params['operator_group_id']);//从运营组中查询客服以及企微信息
  857. } else {
  858. $senders = json_decode($params['multiple_senders'], 1);
  859. }
  860. $res = [];
  861. foreach ($senders as $item) {
  862. $data['corpid'] = $item['corpid'];
  863. $data['senders'] = implode(',', $item['user_list']);
  864. $userInfo = self::getLicenseExpirationUserList($data);
  865. if(!empty($userInfo['senders'])) {
  866. $res[] = $userInfo;
  867. }
  868. }
  869. return $res;
  870. } catch (\Exception $exception) {
  871. Log::logError('多企微获取群发条件匹配的外部联系人数过程发生异常', [
  872. 'params' => $params,
  873. 'line' => $exception->getLine(),
  874. 'msg' => $exception->getMessage()
  875. ], 'GetCustomerTotal-Exception');
  876. EmailQueue::rPush('多企微获取群发条件匹配的外部联系人数过程发生异常', '', ['song.shen@kuxuan-inc.com'], '猎羽');
  877. return [];
  878. }
  879. }
  880. }