企微短剧业务系统

ExternalContactService.php 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. <?php
  2. namespace App\Service;
  3. use App\Log;
  4. use App\Models\AuthorizeCorp;
  5. use App\Models\Customer;
  6. use App\Models\CustomerDetails;
  7. use App\Models\CustomerDynamic;
  8. use App\Models\Tag;
  9. use App\Models\WarnRecordLog;
  10. use App\Models\WelcomeMsg;
  11. use App\RedisModel;
  12. use App\Support\EmailQueue;
  13. use Illuminate\Support\Facades\DB;
  14. class ExternalContactService
  15. {
  16. /**
  17. * 外部联系人变更事件回调处理
  18. * */
  19. public static function changeExternalContactEvent($msgObj, $isTemplate=0)
  20. {
  21. try {
  22. $changeType = trim($msgObj->ChangeType);
  23. $corpid = trim($msgObj->AuthCorpId);
  24. switch($changeType) {
  25. case 'add_external_contact': // 添加企业客户事件
  26. case 'edit_external_contact': // 编辑客户事件
  27. case 'add_half_external_contact': // 外部联系人免验证添加成员事件
  28. $data['corpid'] = $corpid;
  29. $data['user_id'] = trim($msgObj->UserID);
  30. $data['external_userid'] = trim($msgObj->ExternalUserID);
  31. $data['state'] = trim($msgObj->State);
  32. $welcomeCode = trim($msgObj->WelcomeCode);
  33. # 新添加客户入队列发送欢迎语
  34. if(!empty($welcomeCode)) {
  35. $newUserInfo = array(
  36. 'corpid' => $corpid, 'user_id' => $data['user_id'], 'welcome_code' => $welcomeCode
  37. );
  38. RedisModel::lPush(WelcomeMsg::WELCOME_USER_RDS, json_encode($newUserInfo));
  39. }
  40. if($isTemplate) {
  41. $rdsKey = Customer::CUSTOMER_UPDATE_WAITING_TEMPLATE_RDS;
  42. } else {
  43. $rdsKey = Customer::CUSTOMER_UPDATE_WAITING_RDS;
  44. }
  45. // Log::loginfo('客户信息',[
  46. // 'welcomeCode' => $welcomeCode,
  47. // 'user_id' => $data['user_id'],
  48. // 'is_template' => $isTemplate,
  49. // 'rds_key' => $rdsKey
  50. // ], 'TemplateWelcomeMsg');
  51. $result = RedisModel::lPush($rdsKey, json_encode($data));
  52. if(!$result) {
  53. // Todo::增加报警
  54. Log::logError('新添加客户信息入队列失败', $data, 'UpdateExternalContact');
  55. return false;
  56. }
  57. if($changeType != 'edit_external_contact') {
  58. # 添加成员动态信息
  59. $result = CustomerDynamic::customerDynamicSave($corpid, $data['external_userid'], $data['user_id'], 1);
  60. if(!$result) {
  61. Log::logError('(添加客户)客户动态信息存储失败', $data, 'CustomerDynamicSave');
  62. }
  63. }
  64. break;
  65. case 'del_external_contact': // 删除企业客户事件
  66. $data['corpid'] = $corpid;
  67. $data['user_id'] = trim($msgObj->UserID);
  68. $data['external_userid'] = trim($msgObj->ExternalUserID);
  69. $data['source'] = trim($msgObj->Source);
  70. $result = RedisModel::lPush(Customer::DEL_CUSTOMER_RDS, json_encode($data));
  71. if(!$result) {
  72. // Todo::增加报警
  73. Log::logError('新添加客户信息入队列失败', $data, 'DeleteExternalContact');
  74. return false;
  75. }
  76. # 添加成员动态信息
  77. $result = CustomerDynamic::customerDynamicSave($corpid, $data['external_userid'], $data['user_id'], 2);
  78. if(!$result) {
  79. Log::logError('(删除企业客户)客户动态信息存储失败', $data, 'CustomerDynamicSave');
  80. }
  81. break;
  82. case 'del_follow_user': // 删除跟进成员事件
  83. $data['corpid'] = $corpid;
  84. $data['user_id'] = trim($msgObj->UserID);
  85. $data['external_userid'] = trim($msgObj->ExternalUserID);
  86. $result = RedisModel::lPush(Customer::DEL_FOLLOW_USER_RDS, json_encode($data));
  87. if(!$result) {
  88. // Todo::增加报警
  89. Log::logError('新添加客户信息入队列失败', $data, 'DeleteFollowUser');
  90. return false;
  91. }
  92. # 添加成员动态信息
  93. $result = CustomerDynamic::customerDynamicSave($corpid, $data['external_userid'], $data['user_id'], 3);
  94. if(!$result) {
  95. Log::logError('(删除跟进成员)客户动态信息存储失败', $data, 'CustomerDynamicSave');
  96. }
  97. break;
  98. case 'customer_refused': // 客户接替失败事件
  99. $data['corpid'] = $corpid;
  100. $data['user_id'] = trim($msgObj->UserID);
  101. $data['external_userid'] = trim($msgObj->ExternalUserID);
  102. $data['fail_reason'] = trim($msgObj->FailReason);
  103. $result = RedisModel::lPush(Customer::CUSTOMER_REFUSED_RDS, json_encode($data));
  104. if(!$result) {
  105. // Todo::增加报警
  106. Log::logError('新添加客户信息入队列失败', $data, 'CustomerRefused');
  107. return false;
  108. }
  109. break;
  110. }
  111. } catch (\Exception $e) {
  112. Log::logError('外部联系人变更事件回调处理发生异常', [
  113. 'line' => $e->getLine(),
  114. 'msg' => $e->getMessage(),
  115. 'data' => $msgObj
  116. ], 'ChangeExternalContactEvent-Exception');
  117. return false;
  118. }
  119. return true;
  120. }
  121. /**
  122. * 获取外部联系人信息详情
  123. * @param $corpid string 授权方企业微信id
  124. * @param $externalUserid string 外部联系人的userid
  125. * @param $cursor string 上次请求返回的next_cursor,用于分页请求(见官方文档)
  126. * @param $retry integer 重试次数,默认为0
  127. * */
  128. public static function getExternalContactDetail($corpid, $externalUserid, $cursor=null, $retry=0)
  129. {
  130. # 获取SuiteAccessToken
  131. $accessToken = AuthorizeCorp::getAccessToken($corpid, '获取外部联系人信息详情');
  132. if(empty($accessToken)) { // 令牌获取失败,发送报警
  133. Log::logError('【获取外部联系人信息详情】令牌获取失败', [
  134. 'corpid' => $corpid,
  135. 'externalUserId' => $externalUserid,
  136. 'cursor' => $cursor,
  137. 'retry' => $retry,
  138. 'access_token' => $accessToken
  139. ], 'GetExternalContactDetail');
  140. return false;
  141. }
  142. # 获取外部联系人详情信息API
  143. $getExternalContactUri = config('qyWechat.external_contact_detail');
  144. $getExternalContactUri .= $accessToken . '&external_userid=' . $externalUserid;
  145. if($cursor) {
  146. $getExternalContactUri .= '&cursor=' . $cursor;
  147. }
  148. $response = HttpService::httpGet($getExternalContactUri);
  149. $responseData = json_decode($response, true);
  150. if(isset($responseData['errcode']) && $responseData['errcode']) {
  151. if($retry <=5 && $responseData['errcode'] == -1) {
  152. $retry++;
  153. return ExternalContactService::getExternalContactDetail($corpid, $externalUserid, $cursor, $retry);
  154. }
  155. if($responseData['errcode'] == '84061') {
  156. # 获取客户在系统中已有的客户关系
  157. $userIdList = CustomerDetails::suffix($corpid)->where('corpid', $corpid)->where('loss_status', 1)
  158. ->where('external_userid', $externalUserid)->pluck('user_id');
  159. if(!empty($userIdList)) {
  160. foreach ($userIdList as $userId) {
  161. RedisModel::lPush(CustomerDetails::CUSTOMER_LOSS_INFO_RDS, json_encode([
  162. 'corpid' => $corpid,
  163. 'user_id' => $userId,
  164. 'external_userid' => $externalUserid,
  165. ]));
  166. }
  167. } else {
  168. Log::logError('外部联系人详情信息获取异常,提示无好友关系,且系统未查到对应用户数据', [
  169. 'response' => $responseData,
  170. 'corpid' => $corpid,
  171. 'externalUserId' => $externalUserid,
  172. 'cursor' => $cursor
  173. ], 'GetExternalContactDetail');
  174. }
  175. } else {
  176. if($responseData['errcode'] != '701008') {
  177. Log::logError('外部联系人详情信息获取失败', [
  178. 'response' => $responseData,
  179. 'corpid' => $corpid,
  180. 'externalUserId' => $externalUserid,
  181. 'cursor' => $cursor
  182. ], 'GetExternalContactDetail');
  183. }
  184. }
  185. return [];
  186. }
  187. return $responseData;
  188. }
  189. /**
  190. * 内部员工删除企业客户
  191. * */
  192. public static function deleteExternalContact($corpid, $userId, $externalUserid, $source)
  193. {
  194. try {
  195. // 开启事务
  196. DB::beginTransaction();
  197. # 判断该企业主体是否有其它员工负责该客户
  198. $followedCount = CustomerDetails::followUsersCount($corpid, $externalUserid);
  199. if($followedCount == 1) {
  200. # 更新客户状态为被内部员工删除
  201. $result = Customer::changeCustomerStatus($corpid, $externalUserid, 0);
  202. if(!$result) {
  203. DB::rollBack();
  204. return false;
  205. }
  206. }
  207. # 员工客户关系表对应状态更新
  208. $result = CustomerDetails::changeRelationStatus($corpid, $userId, $externalUserid, 1, $source, false, date("Y-m-d H:i:s"));
  209. if(!$result) {
  210. DB::rollBack();
  211. return false;
  212. }
  213. # 删除客户数据缓存
  214. RedisModel::sAdd('Playlet::deleteFollowUser##'.$corpid.'##'.$userId, $externalUserid);
  215. DB::commit();
  216. } catch (\Exception $e) {
  217. DB::rollBack();
  218. Log::logError('内部员工删除企业客户处理逻辑发生异常', [
  219. 'line' => $e->getLine(),
  220. 'msg' => $e->getMessage()
  221. ], 'DeleteExternalContact');
  222. return false;
  223. }
  224. return true;
  225. }
  226. /**
  227. * 客户删除跟进成员事件
  228. * */
  229. public static function deleteFollowUser($corpid, $userId, $externalUserid)
  230. {
  231. try {
  232. DB::beginTransaction();
  233. # 判断该企业主体是否有其它员工负责该客户
  234. $followedCount = CustomerDetails::followUsersCount($corpid, $externalUserid);
  235. if($followedCount == 1) {
  236. # 更新客户状态为客户删除跟进成员
  237. $result = Customer::changeCustomerStatus($corpid, $externalUserid, 0);
  238. if(!$result) {
  239. DB::rollBack();
  240. return false;
  241. }
  242. }
  243. # 员工客户关系表对应状态更新
  244. $result = CustomerDetails::changeRelationStatus($corpid, $userId, $externalUserid, 2, '', false, date("Y-m-d H:i:s"));
  245. if(!$result) {
  246. DB::rollBack();
  247. return false;
  248. }
  249. # 更新客户首次关注时间
  250. $customerInfo = Customer::suffix($corpid)
  251. ->select('name', 'avatar')
  252. ->where('corpid', $corpid)->where('external_userid', $externalUserid)
  253. ->first();
  254. $createtime = CustomerDetails::suffix($corpid)->where('corpid', $corpid)->where('user_id', $userId)->where('external_userid', $externalUserid)->value('createtime');
  255. Log::logInfo('更新用户留存信息中最早关注时间日志', [
  256. 'user' => $customerInfo->toArray(),
  257. 'createtime' => $createtime,
  258. 'corpid' => $corpid,
  259. 'user_id' => $userId,
  260. 'external_userid' => $externalUserid
  261. ], 'UpdateCustomerFirstAddTime');
  262. CustomerService::updateCustomerFirstAddTime($customerInfo->name, $customerInfo->avatar, $createtime);
  263. # 删除客户数据缓存
  264. RedisModel::sAdd('Playlet::deleteFollowUser##'.$corpid.'##'.$userId, $externalUserid);
  265. DB::commit();
  266. } catch (\Exception $e) {
  267. DB::rollBack();
  268. Log::logError('客户删除跟进成员处理逻辑发生异常', [
  269. 'line' => $e->getLine(),
  270. 'msg' => $e->getMessage()
  271. ], 'DeleteFollowUser');
  272. }
  273. return true;
  274. }
  275. /**
  276. * 更新外部联系人信息至数据库
  277. * */
  278. public static function updateExternalContacts($externalUserData, $corpid, $userId, $state, $changeType, &$customerId)
  279. {
  280. try{
  281. # 客户基本信息
  282. $externalContact = $externalUserData['external_contact'];
  283. # 客户跟进信息
  284. $followData = $externalUserData['follow_user'];
  285. DB::beginTransaction();
  286. # 更新客户信息到Customer表
  287. $customerId = Customer::externalContactDataSave($corpid, $externalContact);
  288. if(!$customerId) { // 更新客户信息失败,事务回滚
  289. Log::logError('更新客户信息失败', [
  290. 'corpid' => $corpid,
  291. 'user_id' => $userId,
  292. 'state' => $state,
  293. 'externalUserData' => $externalUserData
  294. ], 'UpdateExternalContact');
  295. DB::rollBack();
  296. return false;
  297. }
  298. # 更新客户关系到CustomerDetails表
  299. foreach ($followData as $followInfo) {
  300. if($followInfo['userid'] != $userId) {
  301. continue;
  302. }
  303. # 处理标签信息
  304. $tagList = '';
  305. if(isset($followInfo['tags'])) {
  306. foreach ($followInfo['tags'] as $tagInfo) {
  307. $tagId = isset($tagInfo['tag_id']) ? $tagInfo['tag_id'] : '';
  308. if(!$tagId) {
  309. Log::logError('标签信息异常', $tagInfo, 'ExternalContactUpService');
  310. continue;
  311. }
  312. $type = isset($tagInfo['type']) ? $tagInfo['type'] : 1;
  313. $groupName = isset($tagInfo['group_name']) ? $tagInfo['group_name'] : '';
  314. $tagName = isset($tagInfo['tag_name']) ? $tagInfo['tag_name'] : '';
  315. # 判断tag_id是否存在于tag表中
  316. $tagMd5 = Tag::where('corpid', $corpid)->where('tag_id', $tagId)->value('tag_md5');
  317. if(empty($tagMd5)) { // 标签数据不存在
  318. $tagMd5 = md5($tagId);
  319. $tagInsertData = [
  320. 'corpid' => $corpid,
  321. 'tag_id' => $tagId,
  322. 'tag_name' => $tagName,
  323. 'tag_md5' => $tagMd5
  324. ];
  325. $result = Tag::updateOrCreate(['corpid' => $corpid, 'tag_id' => $tagId], $tagInsertData);
  326. if(!$result) {
  327. Log::logError('标签信息插入到标签表失败', $tagInsertData, 'TagInfoInsert');
  328. DB::rollBack();
  329. return false;
  330. }
  331. }
  332. # 构造tagList
  333. $tagList .= $tagMd5 . ',';
  334. }
  335. }
  336. # 更新客户关系到CustomerDetails表
  337. $tagList = trim($tagList, ',');
  338. $followInfo['tag_list'] = $tagList;
  339. $followInfoId = CustomerDetails::followInfoDataSaveV2($corpid, $followInfo, $customerId, $externalContact);
  340. if(!$followInfoId) {
  341. Log::logError('客户关系表中更新数据失败', $followInfo, 'ExternalContactUpService');
  342. DB::rollBack();
  343. return false;
  344. }
  345. // 预警检测
  346. if ($followInfo['userid'] == $userId) {
  347. WarnRecordLog::warnRecordIntoQueue($changeType, $corpid, $followInfo, $externalContact);
  348. }
  349. // 批量加好友
  350. BatchAddCustomerService::addTagForCustomer($changeType, $corpid, $followInfo, $externalContact);
  351. }
  352. DB::commit();
  353. } catch (\Exception $e) {
  354. EmailQueue::rPush('更新外部联系人信息至数据库过程发生异常', $e->getTraceAsString(), ['xiaohua.hou@kuxuan-inc.com'], '更新外部联系人信息至数据库过程发生异常');
  355. Log::logError('更新外部联系人信息至数据库过程发生异常', [
  356. 'line' => $e->getLine(),
  357. 'msg' => $e->getMessage(),
  358. 'data' => $externalUserData,
  359. 'corpid' => $corpid,
  360. 'state' => $state
  361. ], 'ExternalContactUpService');
  362. DB::rollBack();
  363. return false;
  364. }
  365. return true;
  366. }
  367. // /**
  368. // * 更新外部联系人信息至数据库
  369. // * */
  370. // public static function updateExternalContact($externalUserData, $corpid, $userId, $state, &$customerId)
  371. // {
  372. // try{
  373. // # 客户基本信息
  374. // $externalContact = $externalUserData['external_contact'];
  375. // # 客户跟进信息
  376. // $followData = $externalUserData['follow_user'];
  377. //
  378. // DB::beginTransaction();
  379. // # 更新客户信息到Customer表
  380. // $customerId = Customer::externalContactDataSave($corpid, $externalContact);
  381. //
  382. // if(!$customerId) { // 更新客户信息失败,事务回滚
  383. // Log::logError('更新客户信息失败', [
  384. // 'corpid' => $corpid,
  385. // 'user_id' => $userId,
  386. // 'state' => $state,
  387. // 'externalUserData' => $externalUserData
  388. // ], 'UpdateExternalContact');
  389. // DB::rollBack();
  390. // return false;
  391. // }
  392. //
  393. // # 更新客户关系到CustomerDetails表
  394. // foreach ($followData as $followInfo) {
  395. // $followInfoId = CustomerRelation::followInfoDataSave($corpid, $followInfo, $customerId, $externalContact['external_userid']);
  396. //
  397. // if(!$followInfoId) {
  398. // Log::logError('客户关系表中更新数据失败', $followInfo, 'ExternalContactUpService');
  399. // continue;
  400. // }
  401. //
  402. // if(isset($followInfo['tags'])) {
  403. // foreach ($followInfo['tags'] as $tagInfo) {
  404. // $tagId = isset($tagInfo['tag_id']) ? $tagInfo['tag_id'] : '';
  405. // if(!$tagId) {
  406. // Log::logError('标签信息异常', $tagInfo, 'ExternalContactUpService');
  407. // continue;
  408. // }
  409. // $type = isset($tagInfo['type']) ? $tagInfo['type'] : 1;
  410. // $groupName = isset($tagInfo['group_name']) ? $tagInfo['group_name'] : '';
  411. // $tagName = isset($tagInfo['tag_name']) ? $tagInfo['tag_name'] : '';
  412. // $result = CustomerTag::suffix($corpid)->updateOrCreate([
  413. // 'corpid' => $corpid,
  414. // 'user_id' => $followInfo['userid'],
  415. // 'customer_id' => $customerId,
  416. // 'tag_id' => $tagId,
  417. // ], [
  418. // 'external_userid' => $externalContact['external_userid'],
  419. // 'group_name' => $groupName,
  420. // 'tag_name' => $tagName,
  421. // 'type' => $type
  422. // ]);
  423. //
  424. // if(!$result) {
  425. // Log::logError('标签信息异常', $tagInfo, 'ExternalContactUpService');
  426. // }
  427. // }
  428. // }
  429. // }
  430. //
  431. // DB::commit();
  432. // } catch (\Exception $e) {
  433. // Log::logError('更新外部联系人信息至数据库过程发生异常', [
  434. // 'line' => $e->getLine(),
  435. // 'msg' => $e->getMessage(),
  436. // 'data' => $externalUserData,
  437. // 'corpid' => $corpid,
  438. // 'state' => $state
  439. // ], 'ExternalContactUpService');
  440. //
  441. // return false;
  442. // }
  443. //
  444. // return true;
  445. // }
  446. }