WorkGroupChatServices.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2020 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. namespace app\services\work;
  12. use app\dao\work\WorkGroupChatDao;
  13. use app\jobs\work\WorkClientJob;
  14. use app\jobs\work\WorkGroupChatJob;
  15. use app\services\BaseServices;
  16. use crmeb\services\wechat\config\WorkConfig;
  17. use crmeb\services\wechat\Work;
  18. use crmeb\traits\ServicesTrait;
  19. use think\db\exception\DataNotFoundException;
  20. use think\db\exception\DbException;
  21. use think\db\exception\ModelNotFoundException;
  22. use think\exception\ValidateException;
  23. /**
  24. * Class WorkGroupChatServices
  25. * @package app\services\work
  26. * @mixin WorkGroupChatDao
  27. */
  28. class WorkGroupChatServices extends BaseServices
  29. {
  30. use ServicesTrait;
  31. /**
  32. * WorkGroupChatServices constructor.
  33. * @param WorkGroupChatDao $dao
  34. */
  35. public function __construct(WorkGroupChatDao $dao)
  36. {
  37. $this->dao = $dao;
  38. }
  39. /**
  40. * 获取客户群群发列表
  41. * @param array $where
  42. * @param array $with
  43. * @return array
  44. * @throws DataNotFoundException
  45. * @throws DbException
  46. * @throws ModelNotFoundException
  47. */
  48. public function groupChatList(array $where, array $with = [])
  49. {
  50. [$page, $limit] = $this->getPageValue();
  51. $list = $this->dao->groupChat($where)->with($with)->page($page, $limit)->select()->toArray();
  52. $count = $this->dao->groupChat($where)->count();
  53. return compact('list', 'count');
  54. }
  55. /**
  56. * 群发详情列表
  57. * @param array $chatIds
  58. * @return array
  59. * @throws DataNotFoundException
  60. * @throws DbException
  61. * @throws ModelNotFoundException
  62. */
  63. public function getOwnerChatList(array $chatIds, $status = null)
  64. {
  65. return $this->groupChatList(['chat_id' => $chatIds, 'status' => $status], [
  66. 'sendResult' => function ($query) {
  67. $query->field(['chat_id', 'status']);
  68. }, 'chatMember' => function ($query) {
  69. $query->field(['count(*) as sun', 'group_id']);
  70. }
  71. ]);
  72. }
  73. /**
  74. * 同步企业微信客户群
  75. * @param string|null $nextCursor
  76. * @return bool
  77. * @author 等风来
  78. * @email 136327134@qq.com
  79. * @date 2022/10/10
  80. */
  81. public function authGroupChat(string $nextCursor = null)
  82. {
  83. /** @var WorkConfig $confg */
  84. $config = app()->make(WorkConfig::class);
  85. $corpId = $config->get('corpId');
  86. if (!$corpId) {
  87. throw new ValidateException('请先配置企业微信ID');
  88. }
  89. $res = Work::getGroupChats([], 100, $nextCursor);
  90. if (0 !== ($res['errcode'] ?? 0)) {
  91. throw new ValidateException($res['errmsg']);
  92. }
  93. $groupChatList = $res['group_chat_list'] ?? [];
  94. if ($groupChatList) {
  95. $groupChat = [];
  96. foreach ($groupChatList as $item) {
  97. $item['corp_id'] = $corpId;
  98. if (($id = $this->dao->value(['chat_id' => $item['chat_id'], 'corp_id' => $corpId], 'id'))) {
  99. $this->dao->update($id, $item);
  100. } else {
  101. $item['create_time'] = time();
  102. $groupChat[] = $item;
  103. }
  104. }
  105. if ($groupChat) {
  106. $this->dao->saveAll($groupChat);
  107. }
  108. foreach ($groupChatList as $item) {
  109. WorkGroupChatJob::dispatchDo('authChat', [$corpId, $item['chat_id']]);
  110. }
  111. //如果有下一页继续执行
  112. if (!empty($res['next_cursor'])) {
  113. WorkGroupChatJob::dispatchDo('authGroupChat', [$res['next_cursor']]);
  114. }
  115. }
  116. return true;
  117. }
  118. /**
  119. * 保存群详情
  120. * @param string $corpId
  121. * @param string $chatId
  122. * @return bool
  123. * @throws DataNotFoundException
  124. * @throws DbException
  125. * @throws ModelNotFoundException
  126. */
  127. public function saveWorkGroupChat(string $corpId, string $chatId)
  128. {
  129. $response = Work::getGroupChat($chatId);
  130. if (0 !== $response['errcode']) {
  131. throw new ValidateException($response['errmsg']);
  132. }
  133. $groupInfo = $response['group_chat'] ?? [];
  134. $groupInfo['admin_list'] = json_encode(array_column($groupInfo['admin_list'], 'userid'));
  135. $memberList = $groupInfo['member_list'] ?? [];
  136. unset($groupInfo['member_list']);
  137. $group = $this->dao->get(['corp_id' => $corpId, 'chat_id' => $chatId]);
  138. return $this->transaction(function () use ($chatId, $corpId, $group, $groupInfo, $memberList) {
  139. if ($group) {
  140. $group->name = $groupInfo['name'];
  141. $group->owner = $groupInfo['owner'];
  142. $group->notice = $groupInfo['notice'] ?? '';
  143. $group->group_create_time = $groupInfo['create_time'];
  144. $group->member_num = count($memberList);
  145. $group->save();
  146. } else {
  147. $group = $this->dao->save([
  148. 'corp_id' => $corpId,
  149. 'chat_id' => $chatId,
  150. 'name' => $groupInfo['name'],
  151. 'owner' => $groupInfo['owner'],
  152. 'notice' => $groupInfo['notice'] ?? '',
  153. 'member_num' => count($memberList),
  154. 'group_create_time' => $groupInfo['create_time'],
  155. 'status' => $groupInfo['status'] ?? 0,
  156. ]);
  157. }
  158. $this->saveMember($memberList, $group->id, $group->member_num);
  159. return $group->id;
  160. });
  161. }
  162. /**
  163. * @param array $where
  164. * @return array
  165. */
  166. public function getList(array $where)
  167. {
  168. [$page, $limit] = $this->getPageValue();
  169. $where['timeKey'] = 'group_create_time';
  170. $where['status'] = [0, 2, 3];
  171. $list = $this->dao->getDataList($where, ['*'], $page, $limit, 'group_create_time', [
  172. 'ownerInfo' => function ($query) {
  173. $query->field(['userid', 'name']);
  174. },
  175. ]);
  176. //提取管理员数据
  177. $adminUserId = [];
  178. foreach ($list as $item) {
  179. $adminUserId = array_merge($adminUserId, $item['admin_list'] ?? []);
  180. }
  181. $adminUserId = array_merge(array_unique(array_filter($adminUserId)));
  182. if ($adminUserId) {
  183. /** @var WorkMemberServices $memberService */
  184. $memberService = app()->make(WorkMemberServices::class);
  185. $adminUserList = $memberService->getColumn([
  186. ['userid', 'in', $adminUserId],
  187. ], 'name', 'userid');
  188. foreach ($list as &$item) {
  189. $newAdminUser = [];
  190. if (!empty($item['admin_list'])) {
  191. foreach ($adminUserList as $key => $value) {
  192. if (in_array($key, $item['admin_list'])) {
  193. $newAdminUser[] = ['name' => $value, 'userid' => $key];
  194. }
  195. }
  196. }
  197. $item['admin_user_list'] = $newAdminUser;
  198. }
  199. }
  200. $count = $this->dao->count();
  201. return compact('list', 'count');
  202. }
  203. /**
  204. * 企业微信客户群变动
  205. * @param array $payload
  206. * @throws DataNotFoundException
  207. * @throws DbException
  208. * @throws ModelNotFoundException
  209. */
  210. public function updateGroupChat(array $payload)
  211. {
  212. $corpId = $payload['ToUserName'];
  213. $chatId = $payload['ChatId'];
  214. $groupInfo = $this->dao->get(['corp_id' => $corpId, 'chat_id' => $chatId]);
  215. if (!$groupInfo) {
  216. $groupId = $this->saveWorkGroupChat($corpId, $chatId);
  217. $groupInfo = $this->dao->get($groupId);
  218. }
  219. $response = Work::getGroupChat($chatId);
  220. if (0 !== $response['errcode']) {
  221. throw new ValidateException($response['errmsg'] ?? '企业微信查询群详情失败');
  222. }
  223. $groupChat = $response['group_chat'] ?? [];
  224. $memberList = $groupChat['member_list'];
  225. /** @var WorkGroupChatStatisticServices $statisticService */
  226. $statisticService = app()->make(WorkGroupChatStatisticServices::class);
  227. $this->transaction(function () use ($payload, $groupInfo, $groupChat, $memberList, $statisticService) {
  228. switch ($payload['UpdateDetail']) {
  229. case 'add_member':
  230. $groupInfo->member_num++;
  231. $this->saveMember($memberList, $groupInfo->id, $groupInfo->member_num, true);
  232. $statisticService->saveOrUpdate($groupInfo->id, true, false, $groupInfo->member_num, $groupInfo->retreat_group_num);
  233. break;
  234. case 'del_member':
  235. $groupInfo->member_num--;
  236. $groupInfo->retreat_group_num++;
  237. $this->saveMember($memberList, $groupInfo->id, $groupInfo->member_num, false);
  238. $statisticService->saveOrUpdate($groupInfo->id, false, true, $groupInfo->member_num, $groupInfo->retreat_group_num);
  239. break;
  240. case 'change_owner':
  241. $groupInfo->owner = $groupChat['owner'];
  242. break;
  243. case 'change_name':
  244. $groupInfo->name = $groupChat['name'];
  245. break;
  246. case 'change_notice':
  247. $groupInfo->notice = $groupChat['notice'];
  248. break;
  249. }
  250. if (!empty($groupChat['admin_list'])) {
  251. $groupInfo->admin_list = json_encode(array_column($groupChat['admin_list'], 'userid'));
  252. }
  253. $groupInfo->save();
  254. });
  255. }
  256. /**
  257. * 保存群成员
  258. * @param array $memberList
  259. * @param int $groupId
  260. * @param int $sum
  261. * @param bool $plus
  262. * @return mixed
  263. */
  264. public function saveMember(array $memberList, int $groupId, int $sum = 0, bool $plus = false)
  265. {
  266. $data = [];
  267. /** @var WorkGroupChatMemberServices $chatMemberService */
  268. $chatMemberService = app()->make(WorkGroupChatMemberServices::class);
  269. $newUserIds = array_column($memberList, 'userid');
  270. $userids = $chatMemberService->getColumn(['group_id' => $groupId], 'userid');
  271. $unUserIds = array_diff($userids, $newUserIds);
  272. $labelList = [];
  273. foreach ($memberList as $item) {
  274. $item['group_id'] = $groupId;
  275. $state = $item['state'] ?? '';
  276. if (isset($item['state'])) {
  277. unset($item['state']);
  278. }
  279. $item['invitor_userid'] = $item['invitor']['userid'] ?? '';
  280. $unionid = $item['unionid'] ?? '';
  281. unset($item['invitor'], $item['unionid']);
  282. if ($chatMemberService->count(['group_id' => $groupId, 'userid' => $item['userid']])) {
  283. $chatMemberService->update(['group_id' => $groupId, 'userid' => $item['userid']], [
  284. 'type' => $item['type'],
  285. 'unionid' => $unionid,
  286. 'chat_sum' => $sum,
  287. 'status' => 1,
  288. 'join_time' => $item['join_time'],
  289. 'join_scene' => $item['join_scene'],
  290. 'invitor_userid' => $item['invitor_userid'],
  291. 'group_nickname' => $item['group_nickname'],
  292. ]);
  293. } else {
  294. if ($state) {
  295. $labelList[] = ['userid' => $item['userid'], 'type' => $item['type'], 'state' => $state];
  296. }
  297. $item['unionid'] = $unionid;
  298. $item['chat_sum'] = $sum;
  299. $item['state'] = $state;
  300. $item['create_time'] = time();
  301. $data[] = $item;
  302. }
  303. }
  304. if ($data) {
  305. $chatMemberService->saveAll($data);
  306. //如果没有客户信息同步客户信息
  307. /** @var WorkClientServices $clientService */
  308. $clientService = app()->make(WorkClientServices::class);
  309. $corpId = app()->make(WorkConfig::class)->get('corpId');
  310. foreach ($data as $item) {
  311. if (2 == $item['type'] && $clientService->count(['external_userid' => $item['userid']])) {
  312. WorkClientJob::dispatchDo('saveClientInfo', [$corpId, $item['userid'], '']);
  313. }
  314. }
  315. }
  316. if ($unUserIds) {
  317. $chatMemberService->update([
  318. ['userid', 'in', $unUserIds]
  319. ], ['status' => 0]);
  320. }
  321. return true;
  322. }
  323. /**
  324. * 解散客户群
  325. * @param string $corpId
  326. * @param string $chatId
  327. * @return mixed
  328. * @throws DataNotFoundException
  329. * @throws DbException
  330. * @throws ModelNotFoundException
  331. */
  332. public function dismissGroupChat(string $corpId, string $chatId)
  333. {
  334. $groupChat = $this->dao->get(['corp_id' => $corpId, 'chat_id' => $chatId], ['id']);
  335. if (!$groupChat) {
  336. throw new ValidateException('没有查询到群');
  337. }
  338. return $this->transaction(function () use ($groupChat) {
  339. /** @var WorkGroupChatMemberServices $chatMemberService */
  340. $chatMemberService = app()->make(WorkGroupChatMemberServices::class);
  341. $chatMemberService->delete(['group_id' => $groupChat->id]);
  342. return $groupChat->delete();
  343. });
  344. }
  345. /**
  346. * 群统计
  347. * @param int $id
  348. * @param string $time
  349. * @return array
  350. */
  351. public function getChatStatistics(int $id, string $time)
  352. {
  353. /** @var WorkGroupChatMemberServices $chatMemberService */
  354. $chatMemberService = app()->make(WorkGroupChatMemberServices::class);
  355. $data = [
  356. 'toDaySum' => $chatMemberService->getToDaySum($id),
  357. 'toDayReturn' => $chatMemberService->getToDayReturn($id),
  358. 'groupChatSum' => $this->dao->value(['id' => $id], 'member_num'),
  359. 'groupChatReturnSum' => $this->dao->value(['id' => $id], 'retreat_group_num')
  360. ];
  361. $data['groupChatList'] = $chatMemberService->getChatMemberStatistics($id, 'join_time', ['count(*) as sum'], 1, $time);
  362. $data['groupChatReturnList'] = $chatMemberService->getChatMemberStatistics($id, 'join_time', ['count(*) as sum'], 0, $time);
  363. return $data;
  364. }
  365. /**
  366. * 群成员统计
  367. * @param int $id
  368. * @param string $time
  369. * @return array
  370. */
  371. public function getChatStatisticsList(int $id, string $time)
  372. {
  373. [$page, $limit] = $this->getPageValue();
  374. /** @var WorkGroupChatMemberServices $chatMemberService */
  375. $chatMemberService = app()->make(WorkGroupChatMemberServices::class);
  376. $newCount = $chatMemberService->getChatMemberStatisticsCount($id, 1, $time);
  377. $returnCount = $chatMemberService->getChatMemberStatisticsCount($id, 0, $time);
  378. $SumCount = $newCount > $returnCount ? $newCount : $returnCount;
  379. $groupChatList = $chatMemberService->getChatMemberStatistics($id, 'join_time', ['count(*) as sum', 'chat_sum', 'retreat_chat_num'], 1, $time, $page, $limit);
  380. $groupChatReturnList = $chatMemberService->getChatMemberStatistics($id, 'join_time', ['count(*) as retreat_sum'], 0, $time, $page, $limit);
  381. $count = ($rCount = count($groupChatReturnList)) > ($count = count($groupChatList)) ? $rCount : $count;
  382. $data = [];
  383. for ($i = 0; $i < $count; $i++) {
  384. $data[] = [
  385. 'time' => $groupChatList[$i]['time'] ?? $groupChatReturnList[$i]['time'] ?? '',
  386. 'sum' => $groupChatList[$i]['sum'] ?? 0,
  387. 'retreat_chat_num' => $groupChatList[$i]['retreat_chat_num'] ?? 0,
  388. 'chat_sum' => $groupChatList[$i]['chat_sum'] ?? 0,
  389. 'retreat_sum' => $groupChatReturnList[$i]['retreat_sum'] ?? 0,
  390. ];
  391. }
  392. return ['list' => $data, 'count' => $SumCount];
  393. }
  394. /**
  395. * @param string $chatId
  396. * @param string $corpId
  397. * @return array
  398. * @throws DataNotFoundException
  399. * @throws DbException
  400. * @throws ModelNotFoundException
  401. */
  402. public function getGroupInfo(string $chatId, string $corpId)
  403. {
  404. $groupInfo = $this->dao->get(['chat_id' => $chatId, 'corp_id' => $corpId]);
  405. if (!$groupInfo) {
  406. throw new ValidateException('客户群未查到');
  407. }
  408. /** @var WorkGroupChatMemberServices $service */
  409. $service = app()->make(WorkGroupChatMemberServices::class);
  410. $groupInfo['todaySum'] = $service->getToDaySum($groupInfo->id);
  411. $groupInfo['todayReturnSum'] = $service->getToDayReturn($groupInfo->id);
  412. return $groupInfo->toArray();
  413. }
  414. }