WorkGroupTemplateServices.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  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\WorkGroupTemplateDao;
  13. use app\jobs\work\WorkGroupMsgJob;
  14. use app\services\BaseServices;
  15. use crmeb\services\wechat\WechatResponse;
  16. use crmeb\traits\service\ContactWayQrCode;
  17. use crmeb\traits\ServicesTrait;
  18. use think\exception\ValidateException;
  19. use think\facade\Event;
  20. use think\facade\Log;
  21. /**
  22. * 企业微信群发模板
  23. * Class WorkGroupTemplateServices
  24. * @package app\services\work
  25. * @mixin WorkGroupTemplateDao
  26. */
  27. class WorkGroupTemplateServices extends BaseServices
  28. {
  29. use ContactWayQrCode, ServicesTrait;
  30. /**
  31. * WorkGroupTemplateServices constructor.
  32. * @param WorkGroupTemplateDao $dao
  33. */
  34. public function __construct(WorkGroupTemplateDao $dao)
  35. {
  36. $this->dao = $dao;
  37. }
  38. /**
  39. * 获取群聊模板列表
  40. * @param array $where
  41. * @return array
  42. */
  43. public function getGroupTemplate(array $where)
  44. {
  45. [$page, $limit] = $this->getPageValue();
  46. $list = $this->dao->getDataList($where, ['*'], $page, $limit, 'create_time', ['msgIds']);
  47. /** @var WorkGroupMsgTaskServices $taskService */
  48. $taskService = app()->make(WorkGroupMsgTaskServices::class);
  49. foreach ($list as &$item) {
  50. $item['user_count'] = $item['unuser_count'] = $item['external_user_count'] = $item['external_unuser_count'] = 0;
  51. if (!empty($item['msgIds'])) {
  52. $msgIds = [];
  53. foreach ($item['msgIds'] as $value) {
  54. $msgIds[] = $value['msg_id'];
  55. if ($value['msg_id']) {
  56. WorkGroupMsgJob::dispatchDo('getTaks', [(int)$item['type'], $value['msg_id'], null]);
  57. }
  58. }
  59. $item = array_merge($item, $taskService->getSendMsgStatistics($msgIds, (int)$item['type']));
  60. }
  61. }
  62. $count = $this->dao->count($where);
  63. return compact('list', 'count');
  64. }
  65. /**
  66. * 创建或者修改
  67. * @param array $data
  68. */
  69. public function saveGroupTemplate(array $data)
  70. {
  71. $this->checkWelcome($data['welcome_words'], 0);
  72. $this->transaction(function () use ($data) {
  73. $res = $this->dao->save($data);
  74. //立即发送或者选择的时间小于当前时间
  75. if (!$data['template_type'] || ($data['template_type'] == 1 && $data['send_time'] < time())) {
  76. if ($data['type']) {
  77. WorkGroupMsgJob::dispatchDo('batch', [$res->id, '', 0]);
  78. } else {
  79. foreach ($data['userids'] as $key => $userid) {
  80. WorkGroupMsgJob::dispatchDo('batch', [$res->id, $userid, $key + 1]);
  81. }
  82. }
  83. }
  84. });
  85. }
  86. /**
  87. * 批量发送
  88. * @param int $id
  89. * @param string $userId
  90. * @param int $count
  91. * @return bool
  92. */
  93. public function batch(int $id, string $userId, int $count)
  94. {
  95. try {
  96. $groupTempInfo = $this->dao->get($id);
  97. if (!$groupTempInfo) {
  98. return true;
  99. }
  100. if ($groupTempInfo->send_type == 1) {
  101. return true;
  102. }
  103. $groupTempInfo = $groupTempInfo->toArray();
  104. $externalUserid = [];
  105. if (!$groupTempInfo['type']) {
  106. /** @var WorkClientServices $service */
  107. $service = app()->make(WorkClientServices::class);
  108. $where = ['userid' => $userId];
  109. if ($groupTempInfo['client_type']) {
  110. //条件筛选
  111. if ($groupTempInfo['where_time']) {
  112. $where['time'] = $groupTempInfo['where_time'];
  113. $where['timeKey'] = 'create_time';
  114. }
  115. if ($groupTempInfo['where_label']) {
  116. $where['label'] = $groupTempInfo['where_label'];
  117. }
  118. if ($groupTempInfo['notLabel']) {
  119. $where['notLabel'] = $groupTempInfo['where_not_label'];
  120. }
  121. }
  122. $externalUserid = $service->getClientUserIds($where);
  123. }
  124. $this->sendTask($id, $externalUserid, $groupTempInfo, $count);
  125. return true;
  126. } catch (\Throwable $e) {
  127. Log::error([
  128. 'message' => '创建群发任务失败:' . $e->getMessage(),
  129. 'file' => $e->getFile(),
  130. 'line' => $e->getLine()
  131. ]);
  132. //记录发送失败原因
  133. try {
  134. $this->dao->update($id, ['send_type' => -1, 'fail_message' => '创建群发任务失败:' . $e->getMessage()]);
  135. } catch (\Throwable $e) {
  136. }
  137. return true;
  138. }
  139. }
  140. /**
  141. * @param int $id
  142. * @param array $externalUserid
  143. * @param array $data
  144. * @param int $count
  145. */
  146. public function sendTask(int $id, array $externalUserid, array $data, int $count)
  147. {
  148. $update = [];
  149. /** @var WorkGroupMsgRelationServices $msgRelationService */
  150. $msgRelationService = app()->make(WorkGroupMsgRelationServices::class);
  151. if ($data['type']) {
  152. //群主群发
  153. $failList = [];
  154. $msgIdData = [];
  155. foreach ($data['userids'] as $item) {
  156. $res = $this->sendMsgTemplate([], $data['welcome_words'], 'group', $item);
  157. $failList = array_merge($failList, $res['fail_list'] ?? []);
  158. if (isset($res['msgid'])) {
  159. $msgIdData[] = $res['msgid'];
  160. }
  161. }
  162. $update['send_type'] = 1;
  163. if ($failList) {
  164. $update['fail_external_userid'] = json_encode($failList);
  165. }
  166. if ($update) {
  167. $this->dao->update($id, $update);
  168. }
  169. $msgRelation = [];
  170. foreach ($msgIdData as $msgId) {
  171. $msgRelation[] = ['template_id' => $id, 'msg_id' => $msgId];
  172. WorkGroupMsgJob::dispatchSece(60, 'getTaks', [(int)$data['type'], $msgId, null]);
  173. }
  174. $msgRelationService->saveAll($msgRelation);
  175. } else {
  176. //成员群发
  177. $sendTemplateWelcome = $this->sendMsgTemplate($externalUserid, $data['welcome_words']);
  178. $failList = $sendTemplateWelcome['fail_list'] ?? [];
  179. if ($failList) {
  180. $update['fail_external_userid'] = json_encode($failList);
  181. }
  182. if ($count == count($data['userids'])) {
  183. $update['send_type'] = 1;
  184. } else {
  185. $update['send_type'] = 2;
  186. }
  187. if ($update) {
  188. $this->dao->update($id, $update);
  189. }
  190. $msgRelationService->save(['template_id' => $id, 'msg_id' => $sendTemplateWelcome['msgid']]);
  191. WorkGroupMsgJob::dispatchSece(60, 'getTaks', [(int)$data['type'], $sendTemplateWelcome['msgid'], null]);
  192. }
  193. }
  194. /**
  195. * 群发任务详情
  196. * @param int $id
  197. * @return array
  198. * @throws \think\db\exception\DataNotFoundException
  199. * @throws \think\db\exception\DbException
  200. * @throws \think\db\exception\ModelNotFoundException
  201. */
  202. public function getGroupTemplateInfo(int $id)
  203. {
  204. $info = $this->dao->get($id, ['*'], ['msgIds']);
  205. if (!$info) {
  206. throw new ValidateException('没有查到此数据');
  207. }
  208. $msgIds = [];
  209. $info = $info->toArray();
  210. if (!empty($info['msgIds'])) {
  211. $msgIds = array_column($info['msgIds'], 'msg_id');
  212. }
  213. $info['user_count'] = $info['unuser_count'] = $info['external_user_count'] = $info['external_unuser_count'] = 0;
  214. $info['user_list'] = [];
  215. if ($info['userids']) {
  216. /** @var WorkMemberServices $service */
  217. $service = app()->make(WorkMemberServices::class);
  218. $info['user_list'] = $service->getColumn([
  219. ['userid', 'in', $info['userids']]
  220. ], 'userid,name');
  221. }
  222. if ($msgIds) {
  223. /** @var WorkGroupMsgTaskServices $taskService */
  224. $taskService = app()->make(WorkGroupMsgTaskServices::class);
  225. $info = array_merge($info, $taskService->getSendMsgStatistics($msgIds, (int)$info['type']));
  226. }
  227. return $info;
  228. }
  229. /**
  230. * 删除群发模板
  231. * @param int $id
  232. * @return bool
  233. */
  234. public function deleteGroupTemplate(int $id)
  235. {
  236. /** @var WorkGroupMsgTaskServices $taskService */
  237. $taskService = app()->make(WorkGroupMsgTaskServices::class);
  238. /** @var WorkGroupMsgRelationServices $msgRelationService */
  239. $msgRelationService = app()->make(WorkGroupMsgRelationServices::class);
  240. /** @var WorkGroupMsgSendResultServices $sendResultService */
  241. $sendResultService = app()->make(WorkGroupMsgSendResultServices::class);
  242. $msgIds = $msgRelationService->getColumn(['template_id' => $id], 'msg_id');
  243. $this->transaction(function () use ($id, $msgRelationService, $msgIds, $taskService, $sendResultService) {
  244. $this->dao->delete($id);
  245. if ($msgIds) {
  246. $taskService->delete(['msg_id' => $msgIds]);
  247. $sendResultService->delete(['msg_id' => $msgIds]);
  248. }
  249. });
  250. return true;
  251. }
  252. /**
  253. * 执行定时发送群发内容
  254. */
  255. public function cornHandle()
  256. {
  257. $time = time();
  258. $list = $this->dao->getDataList(['send_time' => $time, 'send_type' => 0], ['*'], 0, 0, null, ['msgIds']);
  259. foreach ($list as $item) {
  260. if ($item['type']) {
  261. WorkGroupMsgJob::dispatchDo('batch', [$item['id'], '', 0]);
  262. } else {
  263. foreach ($item['userids'] as $count => $userid) {
  264. WorkGroupMsgJob::dispatchDo('batch', [$item['id'], $userid, $count + 1]);
  265. }
  266. }
  267. }
  268. return true;
  269. }
  270. /**
  271. * 发送应用消息
  272. * @param int $id
  273. * @param string $userid
  274. * @param string $sendTime
  275. * @return WechatResponse
  276. * @throws \think\db\exception\DataNotFoundException
  277. * @throws \think\db\exception\DbException
  278. * @throws \think\db\exception\ModelNotFoundException
  279. */
  280. public function sendMessage(int $id, string $userid, string $sendTime)
  281. {
  282. if ($id) {
  283. $template = $this->dao->get(['id' => $id], ['userids', 'type', 'name', 'create_time']);
  284. if (!$template) {
  285. throw new ValidateException('没有查到群发模板');
  286. }
  287. $template = $template->toArray();
  288. if (isset($template[0]['userid'])) {
  289. $template['userids'] = array_column($template['userids'], 'userid');
  290. }
  291. $userids = $template['userids'];
  292. $task = $template['type'] ? '客户群群发任务' : '客户群发任务';
  293. $text = "【任务提醒】有新的任务啦!\n" .
  294. "任务类型:{$task}\n" .
  295. "任务名称:{$template['name']}\n" .
  296. "创建时间:{$template['create_time']}\n" .
  297. "可前往【群发助手】中确认发送,记得及时完成哦\n";
  298. } else {
  299. $userids = [$userid];
  300. $text = "【任务提醒】有新的任务啦!\n" .
  301. "任务类型:客户群发任务\n" .
  302. "创建时间:{$sendTime}\n" .
  303. "可前往【群发助手】中确认发送,记得及时完成哦\n";
  304. }
  305. $res = Event::until('work.message', [
  306. 'text', $text, ['toUser' => $userids], []
  307. ]);
  308. if ($res === false) {
  309. throw new ValidateException('发送消息失败');
  310. }
  311. }
  312. }