WorkChannelCodeServices.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  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\WorkChannelCodeDao;
  13. use app\services\BaseServices;
  14. use app\services\user\label\UserLabelServices;
  15. use crmeb\services\wechat\Work;
  16. use crmeb\traits\service\ContactWayQrCode;
  17. use crmeb\traits\ServicesTrait;
  18. use think\exception\ValidateException;
  19. /**
  20. * 渠道码
  21. * Class WorkChannelCodeServices
  22. * @package app\services\work
  23. * @mixin WorkChannelCodeDao
  24. */
  25. class WorkChannelCodeServices extends BaseServices
  26. {
  27. use ServicesTrait, ContactWayQrCode;
  28. /**
  29. * WorkChannelCodeServices constructor.
  30. * @param WorkChannelCodeDao $dao
  31. */
  32. public function __construct(WorkChannelCodeDao $dao)
  33. {
  34. $this->dao = $dao;
  35. }
  36. /**
  37. * 获取渠道二维码列表
  38. * @param array $where
  39. * @return array
  40. */
  41. public function getList(array $where)
  42. {
  43. [$page, $limit] = $this->getPageValue();
  44. $list = $this->dao->getDataList($where, ['*'], $page, $limit, 'create_time', ['category' => function ($query) {
  45. $query->field(['id', 'name']);
  46. }]);
  47. //获取标签
  48. $labelIds = [];
  49. foreach ($list as $item) {
  50. $labelIds = array_merge($labelIds, $item['label_id']);
  51. }
  52. $labelIds = array_merge(array_unique(array_filter($labelIds)));
  53. $labelList = [];
  54. if ($labelIds) {
  55. /** @var UserLabelServices $labelServices */
  56. $labelServices = app()->make(UserLabelServices::class);
  57. $labelList = $labelServices->getColumn([
  58. ['tag_id', 'in', $labelIds]
  59. ], 'label_name', 'tag_id');
  60. }
  61. foreach ($list as &$item) {
  62. foreach ($labelList as $key => $value) {
  63. if (in_array($key, $item['label_id'])) {
  64. $item['label_name'][] = $value;
  65. }
  66. }
  67. }
  68. $count = $this->dao->count($where);
  69. return compact('list', 'count');
  70. }
  71. /**
  72. * 获取渠道码详情
  73. * @param int $id
  74. * @return array
  75. * @throws \think\db\exception\DataNotFoundException
  76. * @throws \think\db\exception\DbException
  77. * @throws \think\db\exception\ModelNotFoundException
  78. */
  79. public function getChannelInfo(int $id)
  80. {
  81. $channelInfo = $this->dao->get($id, ['*'], ['cycle', 'useridLimit' => function ($query) {
  82. $query->with(['member']);
  83. }]);
  84. if (!$channelInfo) {
  85. throw new ValidateException('没有查询到渠道二维码信息');
  86. }
  87. /** @var WorkMemberServices $make */
  88. $make = app()->make(WorkMemberServices::class);
  89. //重组周期数据
  90. if (!empty($channelInfo['cycle'])) {
  91. $userIds = [];
  92. foreach ($channelInfo['cycle'] as $item) {
  93. $userIds = array_merge($userIds, $item['userids']);
  94. }
  95. if ($userIds) {
  96. $userIds = array_merge(array_unique(array_filter($userIds)));
  97. $userList = $make->getColumn([['userid', 'in', $userIds]], 'name', 'userid');
  98. foreach ($channelInfo['cycle'] as &$item) {
  99. $userItem = [];
  100. foreach ($userList as $key => $value) {
  101. if (in_array($key, $item['userids'])) {
  102. $userItem[] = ['name' => $value, 'userid' => $key];
  103. }
  104. }
  105. $item['userItem'] = $userItem;
  106. }
  107. }
  108. }
  109. $channelInfo = $channelInfo->toArray();
  110. $channelInfo['presentUseUserIds'] = $this->getPresentUseUserIds((int)$channelInfo['type'], $channelInfo['cycle'] ?? [], $channelInfo['useridLimit'] ?? [], [
  111. 'reserve_userid' => $channelInfo['reserve_userid'],
  112. 'userids' => $channelInfo['userids'],
  113. 'code_id' => $channelInfo['id'],
  114. 'add_upper_limit' => $channelInfo['add_upper_limit'],
  115. ]);
  116. $channelInfo['presentUseUserList'] = $make->getColumn([['userid', 'in', $channelInfo['presentUseUserIds']]], 'name');
  117. $channelInfo['reserve_user_list'] = $make->getColumn([['userid', 'in', $channelInfo['reserve_userid']]], 'name');
  118. $channelInfo['user_list'] = $make->getColumn([['userid', 'in', $channelInfo['userids']]], 'name,userid,avatar');
  119. return $channelInfo;
  120. }
  121. /**
  122. * 保存和修改渠道二维码
  123. * @param array $data
  124. * @param int $id
  125. * @return mixed
  126. */
  127. public function saveChanne(array $data, int $id = 0)
  128. {
  129. $data['type'] = (int)$data['type'];
  130. $cycle = $data['cycle'];
  131. $useridLimit = $data['useridLimit'];
  132. unset($data['cycle'], $data['useridLimit']);
  133. //验证数据
  134. if (1 == $data['type']) {
  135. if (!count($cycle)) {
  136. throw new ValidateException('至少设置一个周期');
  137. }
  138. $this->checkEmployee($cycle);
  139. } else {
  140. if (!$data['userids']) {
  141. throw new ValidateException('必须选择一个成员');
  142. }
  143. if (count($data['userids']) > 100) {
  144. throw new ValidateException('选择的成员不能大于100个');
  145. }
  146. }
  147. //验证欢迎语
  148. $this->checkWelcome($data['welcome_words'], (int)$data['welcome_type']);
  149. /** @var WorkChannelCycleServices $cycleService */
  150. $cycleService = app()->make(WorkChannelCycleServices::class);
  151. /** @var WorkChannelLimitServices $limitService */
  152. $limitService = app()->make(WorkChannelLimitServices::class);
  153. //保存数据
  154. $id = $this->transaction(function () use ($cycle, $id, $data, $cycleService, $useridLimit, $limitService) {
  155. $configId = null;
  156. if ($id) {
  157. $this->dao->update($id, $data);
  158. $configId = $this->dao->value(['id' => $id], 'config_id');
  159. } else {
  160. $data['create_time'] = time();
  161. $res = $this->dao->save($data);
  162. if (!$res) {
  163. throw new ValidateException('保存失败');
  164. }
  165. $id = $res->id;
  166. }
  167. $newCycle = $cycle;
  168. foreach ($cycle as &$item) {
  169. $item['channel_id'] = $id;
  170. $item['userids'] = json_encode($item['userids']);
  171. $item['wokr_time'] = json_encode($item['wokr_time']);
  172. }
  173. foreach ($useridLimit as &$item) {
  174. $item['channel_id'] = $id;
  175. }
  176. $cycleService->delete(['channel_id' => $id]);
  177. $cycleService->saveAll($cycle);
  178. $limitService->delete(['channel_id' => $id]);
  179. $limitService->saveAll($useridLimit);
  180. //提取在线成员生成二维码
  181. $userIds = $this->getPresentUseUserIds((int)$data['type'], $newCycle, $useridLimit, [
  182. 'reserve_userid' => $data['reserve_userid'],
  183. 'userids' => $data['userids'],
  184. 'code_id' => $id,
  185. 'add_upper_limit' => $data['add_upper_limit'],
  186. ]);
  187. $this->handleQrCode((int)$id, $userIds, !!$data['skip_verify'], $configId);
  188. return $id;
  189. });
  190. return $id;
  191. }
  192. /**
  193. * 获取当前时间内值班的成员userid
  194. * @param int $type
  195. * @param array $cycle
  196. * @param array $useridLimit
  197. * @param array $option
  198. * @return array|mixed
  199. */
  200. public function getPresentUseUserIds(int $type, array $cycle, array $useridLimit, array $option = [])
  201. {
  202. $addUpperLimit = (int)($option['add_upper_limit'] ?? 0);
  203. if (0 !== $type) {
  204. $newUserIdLimit = [];
  205. foreach ($useridLimit as $item) {
  206. $newUserIdLimit[$item['userid']] = $item;
  207. }
  208. $userIds = $this->cycleByUserid($cycle, $newUserIdLimit, $option['code_id'], $addUpperLimit);
  209. } else {
  210. //限制添加成员
  211. if (1 === $addUpperLimit) {
  212. $userIds = $this->addClientLimit($option['userids'] ?? [], $useridLimit, $option['code_id']);
  213. } else {
  214. $userIds = $option['userids'] ?? [];
  215. }
  216. }
  217. if (!$userIds) {
  218. $userIds = $option['reserve_userid'] ?? [];
  219. }
  220. return $userIds;
  221. }
  222. /**
  223. * 检测成员限制
  224. * @param array $cycle
  225. */
  226. public function checkEmployee(array $cycle)
  227. {
  228. $week = [];
  229. foreach ($cycle as $item) {
  230. foreach ($item['wokr_time'] as $num) {
  231. $week[$num] = array_merge(($week[$num] ?? []), $item['userids']);
  232. }
  233. }
  234. foreach ($week as $item) {
  235. if (count($item) > 100) {
  236. throw new ValidateException('周期内每个联系方式最多配置100个使用成员');
  237. }
  238. }
  239. }
  240. /**
  241. * 根据周期规则获取当前值班成员userid
  242. * @param array $cycle
  243. * @param array $useridLimit
  244. * @param int $codeId
  245. * @param int $addUpperLimit
  246. * @return array
  247. */
  248. public function cycleByUserid(array $cycle, array $useridLimit = [], int $codeId = 0, int $addUpperLimit = 0)
  249. {
  250. $userids = [];
  251. $weekNam = date('w');
  252. $nowHon = date('H:i');
  253. foreach ($cycle as $item) {
  254. //查询出当前周期内工作的值班人员
  255. if (in_array($weekNam, $item['wokr_time']) && $item['start_time'] <= $nowHon && $nowHon <= $item['end_time']) {
  256. $userids = array_merge($userids, $item['userids']);
  257. }
  258. //24小时值班人员
  259. if (in_array($weekNam, $item['wokr_time']) && $item['start_time'] == $item['end_time']) {
  260. $userids = array_merge($userids, $item['userids']);
  261. }
  262. }
  263. //每日值班人数大于100的需要使用备用人员
  264. $userids = array_merge(array_unique($userids));
  265. if ($userids && count($userids) > 100) {
  266. return [];
  267. }
  268. //不限制添加客户上限
  269. if (0 === $addUpperLimit) {
  270. return $userids;
  271. }
  272. return $this->addClientLimit($userids, $useridLimit, $codeId);
  273. }
  274. /**
  275. * 获取添加员工上限
  276. * @param array $userids
  277. * @param array $useridLimit
  278. * @param int $codeId
  279. * @return array
  280. */
  281. protected function addClientLimit(array $userids, array $useridLimit, int $codeId)
  282. {
  283. /** @var WorkClientFollowServices $followService */
  284. $followService = app()->make(WorkClientFollowServices::class);
  285. $userCount = $followService->userIdByCilentCount($userids, $codeId);
  286. if (!$userCount) {
  287. return $userids;
  288. }
  289. $useridLimitNew = [];
  290. foreach ($useridLimit as $value) {
  291. $useridLimitNew[$value['userid']] = $value;
  292. }
  293. $returnUserId = [];
  294. //限制员工每日添加客户个数
  295. foreach ($userids as $userid) {
  296. foreach ($userCount as $item) {
  297. if ($userid == $item['userid'] && $useridLimitNew[$userid]['max'] > $item['sum']) {
  298. $returnUserId[] = $userid;
  299. }
  300. }
  301. }
  302. return $returnUserId;
  303. }
  304. /**
  305. * @param int $id
  306. * @return mixed
  307. */
  308. public function deleteChannel(int $id)
  309. {
  310. $configId = $this->dao->value(['id' => $id], 'config_id');
  311. return $this->transaction(function () use ($id, $configId) {
  312. if ($configId) {
  313. Work::deleteQrCode($configId);
  314. }
  315. return $this->dao->destroy($id);
  316. });
  317. }
  318. /**
  319. * 执行生成渠道二维码
  320. * @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
  321. * @throws \GuzzleHttp\Exception\GuzzleException
  322. */
  323. public function cronHandle()
  324. {
  325. $channelList = $this->dao->getDataList(['status' => 1, 'type' => 1], ['*'], 0, 0, null, ['cycle', 'useridLimit']);
  326. foreach ($channelList as $item) {
  327. //提取在线成员生成二维码
  328. $userIds = $this->getPresentUseUserIds(
  329. (int)$item['type'],
  330. $item['cycle'] ?? [],
  331. $item['useridLimit'] ?? [],
  332. [
  333. 'reserve_userid' => $item['reserve_userid'],
  334. 'userids' => $item['userids'],
  335. 'code_id' => $item['id'],
  336. 'add_upper_limit' => $item['add_upper_limit'],
  337. ]
  338. );
  339. if ($userIds) {
  340. $this->handleQrCode((int)$item['id'], $userIds, !!$item['skip_verify'], $item['config_id']);
  341. }
  342. }
  343. return true;
  344. }
  345. }