企微短剧业务系统

CustomerService.php 132KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: shensong
  5. * Date: 2022/3/23
  6. * Time: 18:05
  7. */
  8. namespace App\Service;
  9. use App\Log;
  10. use App\Models\AndroidBindCorp;
  11. use App\Models\AndroidToolRequestLog;
  12. use App\Models\AuthorizeCorp;
  13. use App\Models\BatchTransferCustomer;
  14. use App\Models\BlackListRecord;
  15. use App\Models\ChatGroup;
  16. use App\Models\Customer;
  17. use App\Models\CustomerAssignmentRecord;
  18. use App\Models\CustomerAssignmentTotal;
  19. use App\Models\CustomerDetails;
  20. use App\Models\CustomerDynamic;
  21. use App\Models\CustomerShareRelation;
  22. use App\Models\CustomerTag;
  23. use App\Models\CustomerUnique;
  24. use App\Models\DjDepartment;
  25. use App\Models\DjOrder;
  26. use App\Models\DjUser;
  27. use App\Models\Es\ChatGroupMember;
  28. use App\Models\InviteChatGroupDetail;
  29. use App\Models\InviteChatGroupRule;
  30. use App\Models\OfficialAccount;
  31. use App\Models\RadarCustomerBehavior;
  32. use App\Models\RadarCustomerDetail;
  33. use App\Models\System\AdminManageCorp;
  34. use App\Models\System\Users;
  35. use App\Models\Tag;
  36. use App\Models\TagGroup;
  37. use App\Models\CustomerHourRecord;
  38. use App\Models\WaitInviteRequest;
  39. use App\Models\WarnRecordLog;
  40. use App\Models\WelcomeMsgRelation;
  41. use App\RedisModel;
  42. use App\Support\EmailQueue;
  43. use Illuminate\Support\Facades\Auth;
  44. class CustomerService
  45. {
  46. /**
  47. * 分配客户
  48. * @param $corpid string 企业id
  49. * @param $handoverUserId string 原跟进成员的userid
  50. * @param $takeoverUserId string 接替成员的userid
  51. * @param $externalUserIdList array 客户的external_userid列表,每次最多分配100个客户
  52. * @param $transferSuccessMsg string 转移成功后发给客户的消息,最多200个字符,不填则使用默认文案
  53. * @param $type int 1在职迁移 2离职迁移
  54. * @param $selectAll int 是否全选 0否 1是
  55. * @return mixed
  56. * 注意:
  57. * 1.external_userid必须是handover_userid的客户(即配置了客户联系功能的成员所添加的联系人)。
  58. * 2.为保障客户服务体验,90个自然日内,在职成员的每位客户仅可被转接2次。
  59. */
  60. public static function addCustomerAssignment($corpid, $handoverUserId, $takeoverUserId, $externalUserIdList,
  61. $transferSuccessMsg, $type, $selectAll)
  62. {
  63. if(2 == $type) {
  64. // 验证handover_userid 是否为离职员工
  65. $user = DjUser::query()
  66. ->where('user_id', $handoverUserId)
  67. ->where('corpid', $corpid)
  68. ->where('enable', 1)
  69. ->first();
  70. if(empty($user) || (isset($user->status) && $user->status != 5)) {
  71. return ['原跟进员工需为离职状态', 3003];
  72. }
  73. }
  74. $record = CustomerAssignmentTotal::query()
  75. ->create([
  76. 'corpid' => $corpid,
  77. 'handover_userid' => $handoverUserId,
  78. 'takeover_userid' => $takeoverUserId,
  79. 'transfer_success_msg' => $transferSuccessMsg,
  80. 'external_userid' => json_encode($externalUserIdList),
  81. 'type' => $type,
  82. 'select_all' => $selectAll,
  83. 'status' => 1,
  84. ]);
  85. return isset($record->id) ? ['申请成功', 0] : ['申请失败', 400];
  86. }
  87. /**
  88. * 返回客户来源列表
  89. * @return array
  90. */
  91. public static function addWayList()
  92. {
  93. $arr = [
  94. ['key' => 0 , 'val' => '未知来源'],
  95. ['key' => 1 , 'val' => '扫描二维码'],
  96. ['key' => 2 , 'val' => '搜索手机号'],
  97. ['key' => 3 , 'val' => '名片分享'],
  98. ['key' => 4 , 'val' => '群聊'],
  99. ['key' => 5 , 'val' => '手机通讯录'],
  100. ['key' => 6 , 'val' => '微信联系人'],
  101. ['key' => 8 , 'val' => '安装第三方应用时自动添加的客服人员'],
  102. ['key' => 9 , 'val' => '搜索邮箱'],
  103. ['key' => 10 , 'val' => '视频号添加'],
  104. ['key' => 11 , 'val' => '通过日程参与人添加'],
  105. ['key' => 12 , 'val' => '通过会议参与人添加'],
  106. ['key' => 13 , 'val' => '添加微信好友对应的企业微信'],
  107. ['key' => 14 , 'val' => '通过智慧硬件专属客服添加'],
  108. ['key' => 15 , 'val' => '通过上门服务客服添加'],
  109. ['key' => 16 , 'val' => '通过获客链接添加'],
  110. ['key' => 17 , 'val' => '通过定制开发添加'],
  111. ['key' => 18 , 'val' => '通过需求回复添加'],
  112. ['key' => 201 , 'val' => '内部成员共享'],
  113. ['key' => 202 , 'val' => '管理员/负责人分配'],
  114. ];
  115. return $arr;
  116. }
  117. // 客户管理列表
  118. public static function customerList($search, $page, $pageSize, $source)
  119. {
  120. $extra = ''; // 当用户筛选了客户关注起止时间时,返回列表中客户对应的客服列表
  121. $customerQuery = CustomerDetails::getCustomerBySearchNew($search, $source);
  122. $corpid = $search['corpid'];
  123. $customerRelationCount = clone $customerQuery;
  124. $followCount = $customerRelationCount->selectRaw('count(distinct(`con_user_cus`)) as count')->first();
  125. $followCount = $followCount->count;
  126. $customerQueryCount = clone $customerQuery;
  127. $count = $customerQueryCount->selectRaw('count(distinct(`external_userid`)) as count')->first();
  128. $count = $count->count;
  129. $customerLossQ = clone $customerQuery;
  130. $lossCountInfo = $customerLossQ->where('loss_status', 0)->selectRaw('count(distinct(`external_userid`)) as count')->first();
  131. $loss_count = $lossCountInfo->count ?? 0;
  132. if($search['add_date_start'] && $search['add_date_end']) {
  133. $userListQuery = clone $customerQuery;
  134. $userIdList = $userListQuery->selectRaw('distinct(user_id) as user_id')->pluck('user_id');
  135. if($userIdList->isNotEmpty()) {
  136. $userNameList = DjUser::select('name')->whereIn('user_id', $userIdList)->where('corpid', $corpid)->pluck('name');
  137. $extra = implode(',', $userNameList->toArray());
  138. }
  139. }
  140. if($pageSize == 20000) $pageSize = 100000; // 临时修改,增加业务导出量
  141. $offset = ($page - 1) * $pageSize;
  142. $data = $customerQuery->selectRaw("customer_id, name, gender, loss_time, external_userid, user_id, remark,
  143. createtime, add_way, corpid, pay_num, loss_status as relation_enable, blacklist_status, con_user_cus, tag_list,
  144. loss_time, is_new_customer_no_loss, retained_status")
  145. ->groupBy('con_user_cus')
  146. ->orderBy('createtime', 'desc')
  147. ->offset($offset)
  148. ->limit($pageSize)
  149. ->get()
  150. ->toArray();
  151. // 提取客户id列表
  152. $customerIdList = array_column($data, 'customer_id');
  153. // 获取客户基础信息
  154. $customerDataList = Customer::suffix($corpid)
  155. ->where('corpid', $corpid)
  156. ->whereIn('id', $customerIdList)
  157. ->get()
  158. ->keyBy('id')
  159. ->toArray();
  160. $insideUserIdList = array_unique(array_column($data, 'user_id'));
  161. // 查询客服基本信息
  162. $insideUserDataList = DjUser::query()
  163. ->select(['user_id', 'name', 'open_user_id', 'avatar', 'avatar', 'department'])
  164. ->whereIn('user_id', $insideUserIdList)
  165. // ->where('enable', 1)
  166. ->where('corpid', $corpid)
  167. ->get()
  168. ->keyBy('user_id')
  169. ->toArray();
  170. $tagList = Tag::query()
  171. ->where('corpid', $corpid)
  172. ->where('enable', 1)
  173. ->get()
  174. ->keyBy('tag_md5')
  175. ->toArray();
  176. // 查询部门列表
  177. $departmentList = DjDepartment::query()
  178. ->selectRaw('department_id, name as department_name')
  179. ->where('enable', 1)
  180. ->where('corpid', $corpid)
  181. ->get()
  182. ->keyBy('department_id')
  183. ->toArray();
  184. // 获取添加渠道数据
  185. $addWayData = self::addWayList();
  186. $addWayData = array_column($addWayData, null, 'key');
  187. // 循环将数据补充
  188. foreach($data as $key => $value) {
  189. $customerInfo = isset($customerDataList[$value['customer_id']]) ? $customerDataList[$value['customer_id']] : [];
  190. $customerTag = [];
  191. $customerTagList = empty($value['tag_list']) ? [] : explode(',', $value['tag_list']);
  192. foreach($customerTagList as $tagId) {
  193. $tagName = isset($tagList[$tagId]['tag_name']) ? $tagList[$tagId]['tag_name'] : '';
  194. if(!empty($tagName)) {
  195. $customerTag[] = $tagName;
  196. }
  197. }
  198. $data[$key]['type'] = isset($customerInfo['type']) ? $customerInfo['type'] : 0;
  199. $data[$key]['avatar'] = isset($customerInfo['avatar']) ? $customerInfo['avatar'] : 0;
  200. $customerServiceData = [];
  201. $departmentData = null;
  202. $userId = $value['user_id'];
  203. if(isset($insideUserDataList[$userId])) {
  204. $customerServiceData = $insideUserDataList[$userId];
  205. $departmentIdData = explode(',', $insideUserDataList[$userId]['department']);
  206. foreach ($departmentIdData as $item) {
  207. $departmentData = isset($departmentList[$item]['department_name']) ?
  208. $departmentList[$item]['department_name'] : '';
  209. }
  210. }
  211. $data[$key]['user_list'] = $customerServiceData;
  212. $data[$key]['department_list'] = [$departmentData];
  213. $data[$key]['tag_list'] = $customerTag;
  214. $data[$key]['createtime'] = date('Y-m-d H:i', $value['createtime']);
  215. $data[$key]['add_way'] = isset($addWayData[$value['add_way']]['val']) ? $addWayData[$value['add_way']]['val'] : '';
  216. }
  217. $excludeCount = $followCount - $count;
  218. return [['list' => $data, 'count' => $count, 'exclude_count' => $excludeCount, 'cust_loss_uc' => $loss_count], $followCount, $extra];
  219. }
  220. // 客户订单列表
  221. public static function customerOrderList($corpid, $customerId, $page, $pageSize)
  222. {
  223. $offset = ($page - 1) * $pageSize;
  224. // 获取客户的外部联系人id
  225. $externalUserId = Customer::suffix($corpid)
  226. ->where('corpid', $corpid)
  227. ->where('id', $customerId)
  228. ->first();
  229. $externalUserId = isset($externalUserId->external_userid) ? $externalUserId->external_userid : null;
  230. if(empty($externalUserId)) {
  231. return [[], 0];
  232. }
  233. $query = DjOrder::query()
  234. ->select(['order_id', 'pay_money', 'created_ts', 'pay_status', 'playlet_name', 'order_type', 'bind_app_id'
  235. , 'adq_account_id', 'origin_pay_money', 'pay_type'])
  236. ->where('system_corpid', $corpid)
  237. ->where('pay_status', 1)
  238. ->where('external_userid', $externalUserId);
  239. $count = $query->count();
  240. $data = $query->orderBy('created_ts', 'desc')
  241. ->offset($offset)
  242. ->limit($pageSize)
  243. ->get()
  244. ->toArray();
  245. # 提取公众号APPID
  246. $appIdList = array_column($data, 'bind_app_id');
  247. $appList = OfficialAccount::query()->select(['mp_name', 'mp_app_id'])->whereIn('mp_app_id', $appIdList)->get();
  248. // 循环将数据格式化
  249. foreach($data as $key => $value) {
  250. $data[$key]['pay_money'] = round($value['pay_money']/10000, 2);
  251. $data[$key]['origin_pay_money'] = round($value['origin_pay_money']/10000, 2);
  252. $data[$key]['created_ts'] = date('Y-m-d H:i', round($value['created_ts']/1000, 0));
  253. # 投放账号
  254. if(1 == $value['order_type']) {
  255. # 公众号
  256. $wxAccountInfo = $appList->where('mp_app_id', $value['bind_app_id'])->first();
  257. $data[$key]['launch_account'] = isset($wxAccountInfo->mp_name) ? $wxAccountInfo->mp_name : '-';
  258. } else {
  259. $data[$key]['launch_account'] = $value['adq_account_id'];
  260. }
  261. $payType = $value['pay_type'] == 1 ? '小程序虚拟支付' : '其他类型支付';
  262. $data[$key]['pay_type'] = $payType;
  263. }
  264. return [$data, $count];
  265. }
  266. // 客户概览(废弃)
  267. public static function customerDetail($corpid, $customerId, $userId)
  268. {
  269. // 查询客户基础信息
  270. $customerInfo = Customer::suffix($corpid)
  271. ->select(['external_userid', 'name', 'avatar', 'type', 'gender', 'e_weibo', 'e_company', 'e_source',
  272. 'e_qq', 'e_position', 'e_phone', 'e_email', 'e_desc', 'e_birthday', 'e_age', 'e_address'])
  273. ->selectRaw('id as customer_id')
  274. ->where('id', $customerId)
  275. ->where('corpid', $corpid)
  276. // ->where('enable', 1)
  277. ->first();
  278. if(empty($customerInfo)) {
  279. $requestData = [
  280. 'corpid' => $corpid,
  281. 'customer_id' => $customerId,
  282. 'user_id' => $userId,
  283. ];
  284. Log::logInfo('customerDetail 数据为空 request:'.json_encode($requestData, JSON_UNESCAPED_UNICODE), [], 'interface');
  285. return ['数据为空', 3002];
  286. }
  287. $customerInfo = json_decode(json_encode($customerInfo), 1);
  288. $customerInfo['e_birthday'] = !empty($customerInfo['e_birthday']) ? date('Y-m-d', strtotime($customerInfo['e_birthday'])) : null;
  289. // 查询客户跟进信息
  290. $followList = CustomerDetails::suffix($corpid)
  291. ->select(['user_id', 'add_way', 'createtime', 'remark', 'tag_list'])
  292. ->where('corpid', $corpid)
  293. ->where('external_userid', $customerInfo['external_userid'])
  294. ->where('user_id', $userId)
  295. ->orderBy('loss_status', 'desc')
  296. ->first();
  297. $tagIdList = empty($followList->tag_list) ? [] : explode(',', $followList->tag_list);
  298. if(!empty($tagIdList)) {
  299. $tagList = Tag::query()
  300. ->whereIn('tag_md5', $tagIdList)
  301. ->where('corpid', $corpid)
  302. ->where('enable', 1)
  303. ->get()
  304. ->toArray();
  305. $customerInfo['tag_list'] = array_column($tagList, 'tag_name');
  306. } else {
  307. $customerInfo['tag_list'] = [];
  308. }
  309. // 提取跟进信息中的客服id
  310. $userDataList = DjUser::query()
  311. ->select(['user_id', 'name', 'avatar'])
  312. // ->where('enable', 1)
  313. ->where('user_id', $userId)
  314. ->where('corpid', $corpid)
  315. ->first();
  316. $userDataList = !empty($userDataList) ? $userDataList->toArray() : [];
  317. // 获取添加渠道数据
  318. $addWayData = self::addWayList();
  319. $addWayData = array_column($addWayData, null, 'key');
  320. $followList = empty($followList) ? [] : json_decode(json_encode($followList), 1);
  321. $followList['user_name'] = isset($userDataList['name']) ? $userDataList['name'] : '';
  322. $followList['avatar'] = isset($userDataList['avatar']) ? $userDataList['avatar'] : '';
  323. $followList['createtime'] = isset($followList['createtime']) ? date('Y-m-d H:i', $followList['createtime']) : null;
  324. $followList['add_way'] = (isset($followList['add_way']) && isset($addWayData[$followList['add_way']]['val'])) ?
  325. $addWayData[$followList['add_way']]['val'] : '';
  326. $customerInfo['follow_list'] = $followList;
  327. return [$customerInfo, 0];
  328. }
  329. // 客户概览
  330. public static function customerDetails($corpid, $customerId)
  331. {
  332. // 查询客户基础信息
  333. $customerInfo = Customer::suffix($corpid)
  334. ->select(['external_userid', 'name', 'avatar', 'type', 'gender', 'e_weibo', 'e_company', 'e_source',
  335. 'e_qq', 'e_position', 'e_phone', 'e_email', 'e_desc', 'e_birthday', 'e_age', 'e_address'])
  336. ->selectRaw('id as customer_id')
  337. ->where('id', $customerId)
  338. ->where('corpid', $corpid)
  339. // ->where('enable', 1)
  340. ->first();
  341. if(empty($customerInfo)) {
  342. $requestData = [
  343. 'corpid' => $corpid,
  344. 'customer_id' => $customerId,
  345. ];
  346. Log::logInfo('customerDetails 数据为空 request:'.json_encode($requestData, JSON_UNESCAPED_UNICODE), [], 'interface');
  347. return ['数据为空', 3002];
  348. }
  349. $customerInfo = json_decode(json_encode($customerInfo), 1);
  350. $customerInfo['e_birthday'] = !empty($customerInfo['e_birthday']) ? date('Y-m-d', strtotime($customerInfo['e_birthday'])) : null;
  351. // 查询客户跟进信息
  352. $followList = CustomerDetails::suffix($corpid)
  353. ->select(['user_id', 'add_way', 'createtime', 'remark', 'tag_list'])
  354. ->where('corpid', $corpid)
  355. ->where('external_userid', $customerInfo['external_userid'])
  356. ->where('loss_status', 1)
  357. ->orderBy('loss_status', 'desc')
  358. ->get();
  359. $userTagIdList = $allTagIdList = $userList = [];
  360. if($followList->isNotEmpty()) {
  361. foreach($followList as $followInfo) {
  362. $tagIdList = empty($followInfo->tag_list) ? [] : explode(',', $followInfo->tag_list);
  363. $userTagIdList[$followInfo['user_id']] = $tagIdList;
  364. $allTagIdList = array_merge($allTagIdList, $tagIdList);
  365. $userList[] = $followInfo['user_id'];
  366. }
  367. $allTagIdList = array_unique($allTagIdList);
  368. }
  369. $userTagList = [];
  370. if(!empty($allTagIdList)) {
  371. $tagList = Tag::query()
  372. ->whereIn('tag_md5', $allTagIdList)
  373. ->where('corpid', $corpid)
  374. ->where('enable', 1)
  375. ->get();
  376. foreach($userTagIdList as $userId => $userTag) {
  377. $userTagList[$userId] = $tagList->whereIn('tag_md5', $userTag)->pluck('tag_name');
  378. }
  379. }
  380. // 提取跟进信息中的客服id
  381. $userDataList = DjUser::query()
  382. ->select(['user_id', 'name', 'avatar'])
  383. // ->where('enable', 1)
  384. ->whereIn('user_id', $userList)
  385. ->where('corpid', $corpid)
  386. ->get()
  387. ->toArray();
  388. // 获取添加渠道数据
  389. $addWayData = self::addWayList();
  390. $addWayData = array_column($addWayData, null, 'key');
  391. foreach($userDataList as $userData) {
  392. $item['user_name'] = isset($userData['name']) ? $userData['name'] : '';
  393. $item['avatar'] = isset($userData['avatar']) ? $userData['avatar'] : '';
  394. $followData = $followList->where('user_id', $userData['user_id'])->first();
  395. $item['createtime'] = date('Y-m-d H:i', $followData->createtime);
  396. $item['add_way'] = isset($addWayData[$followData->add_way]['val']) ?
  397. $addWayData[$followData->add_way]['val'] : '';
  398. $item['remark'] = isset($followData->remark) ? $followData->remark : '';
  399. $item['user_id'] = $userData['user_id'];
  400. $item['tag_list'] = isset($userTagList[$userData['user_id']]) ? $userTagList[$userData['user_id']] : [];
  401. $customerInfo['follow_list'][] = $item;
  402. }
  403. return [$customerInfo, 0];
  404. }
  405. // 客户详情信息编辑
  406. public static function customerInfoUpdate($corpid, $customerId, $params)
  407. {
  408. $res = Customer::suffix($corpid)
  409. ->where('corpid', $corpid)
  410. ->where('id', $customerId)
  411. ->update($params);
  412. $requestData = [
  413. 'corpid' => $corpid,
  414. 'customer_id' => $customerId,
  415. 'params' => $params,
  416. ];
  417. if($res > 0) {
  418. // Log::logInfo('customerInfoUpdate 编辑成功, request:'.json_encode($requestData, JSON_UNESCAPED_UNICODE), [], 'interface');
  419. return ['编辑成功', 0];
  420. } else {
  421. Log::logInfo('customerInfoUpdate 编辑失败, request:'.json_encode($requestData, JSON_UNESCAPED_UNICODE), [], 'interface');
  422. return ['编辑失败', 400];
  423. }
  424. }
  425. public static function moveCustomerToBlackList($corpid, $customerId, $userId, $reason)
  426. {
  427. // 获取客户的外部联系人id
  428. $customerInfo = Customer::getCustomerInfoById($corpid, $customerId);
  429. $customerInfo = json_decode(json_encode($customerInfo), 1);
  430. $externalUserId = $customerInfo['external_userid'];
  431. $operUserID = Auth::id();
  432. // 开启事务
  433. \DB::begintransaction();
  434. $recordRes = BlackListRecord::query()
  435. ->updateOrCreate([
  436. 'corpid' => $corpid,
  437. 'user_id' => $userId,
  438. 'customer_id' => $customerId
  439. ], [
  440. 'external_userid' => $externalUserId,
  441. 'oper_user_id' => $operUserID,
  442. 'enable' => 1,
  443. 'reason' => $reason,
  444. ]);
  445. $relationRes = CustomerDetails::suffix($corpid)
  446. ->where('user_id', $userId)
  447. ->where('corpid', $corpid)
  448. ->where('external_userid', $externalUserId)
  449. ->update(['blacklist_status' => 1]);
  450. CustomerDynamic::customerDynamicSave($corpid, $externalUserId, $userId, 6);
  451. # 给客户添加黑名单用户标签
  452. RedisModel::lPush(CustomerTagService::CUSTOMER_TAG_MARK_RDS, json_encode([
  453. 'corpid' => $corpid, 'user_id' => $userId, 'external_userid' => $externalUserId,
  454. 'type' => 2, 'tag_group_name' => '猎羽', 'tag_name_list' => ['黑名单客户']
  455. ], 256));
  456. $requestData = [
  457. 'corpid' => $corpid,
  458. 'customer_id' => $customerId,
  459. 'user_id' => $userId,
  460. 'reason' => $reason
  461. ];
  462. if($relationRes > 0 && (isset($recordRes->id) && $relationRes > 0)) {
  463. \DB::commit();
  464. // Log::logInfo('moveCustomerToBlackList 操作成功 request:'.json_encode($requestData, JSON_UNESCAPED_UNICODE), [], 'interface');
  465. return ['操作成功', 0];
  466. } else {
  467. \DB::rollBack();
  468. Log::logInfo('moveCustomerToBlackList 操作失败 request:'.json_encode($requestData, JSON_UNESCAPED_UNICODE), [], 'interface');
  469. return ['操作失败', 400];
  470. }
  471. }
  472. public static function removeCustomerFromBlackList($corpid, $customerId, $userId)
  473. {
  474. // 获取客户的外部联系人id
  475. $customerInfo = Customer::getCustomerInfoById($corpid, $customerId);
  476. $customerInfo = json_decode(json_encode($customerInfo), 1);
  477. $externalUserId = $customerInfo['external_userid'];
  478. $operUserID = Auth::id();
  479. // 开启事务
  480. \DB::begintransaction();
  481. $recordRes = BlackListRecord::query()
  482. ->updateOrCreate([
  483. 'corpid' => $corpid,
  484. 'user_id' => $userId,
  485. 'customer_id' => $customerId
  486. ], [
  487. 'external_userid' => $externalUserId,
  488. 'oper_user_id' => $operUserID,
  489. 'enable' => 0
  490. ]);
  491. $relationRes = CustomerDetails::suffix($corpid)
  492. ->where('user_id', $userId)
  493. ->where('corpid', $corpid)
  494. ->where('external_userid', $externalUserId)
  495. ->update(['blacklist_status' => 0]);
  496. CustomerDynamic::customerDynamicSave($corpid, $externalUserId, $userId, 7);
  497. # 将客户黑名单标签取消(先查询黑名单标签ID,并且查看用户当前是否含有该标签)
  498. $tagGroupId = TagGroup::getGroupIdByGroupName($corpid, '猎羽');
  499. if(!empty($tagGroupId)) {
  500. $tagInfoList = Tag::getTagInfoByTagGroup($corpid, $tagGroupId);
  501. $tagInfo = $tagInfoList->where('tag_name', '黑名单客户')->first();
  502. $tagId = $tagInfo->tag_id ?? null;
  503. if (!empty($tagId)) {
  504. RedisModel::lPush(CustomerTagService::CUSTOMER_TAG_MARK_RDS, json_encode([
  505. 'corpid' => $corpid, 'user_id' => $userId, 'external_userid' => $externalUserId,
  506. 'type' => 1, 'tag_group_id' => $tagGroupId, 'remove_tag_id_list' => [$tagId]
  507. ], 256));
  508. }
  509. }
  510. $requestData = [
  511. 'corpid' => $corpid,
  512. 'customer_id' => $customerId,
  513. 'user_id' => $userId
  514. ];
  515. if($relationRes > 0 && (isset($recordRes->id) && $relationRes > 0)) {
  516. \DB::commit();
  517. // Log::logInfo('removeCustomerFromBlackList 操作成功 request:'.json_encode($requestData, JSON_UNESCAPED_UNICODE), [], 'interface');
  518. return ['操作成功', 0];
  519. } else {
  520. \DB::rollBack();
  521. Log::logInfo('removeCustomerFromBlackList 操作失败 request:'.json_encode($requestData, JSON_UNESCAPED_UNICODE), [], 'interface');
  522. return ['操作失败', 400];
  523. }
  524. }
  525. // 黑名单列表
  526. public static function blackList($corpid, $addWay, $customerName, $adminId, $page, $pageSize)
  527. {
  528. $customerQuery = CustomerDetails::suffix($corpid)
  529. ->where('blacklist_status', 1)
  530. ->whereIn('loss_status', [0, 1, 2]);
  531. if(!empty($customerName)) {
  532. $customerQuery = $customerQuery->where('name', 'like', '%'.$customerName.'%');}
  533. if(is_numeric($addWay)) {
  534. $customerQuery = $customerQuery->where('add_way', $addWay);}
  535. $customerQuery = $customerQuery->where('corpid', $corpid);
  536. $customerQueryCount = clone $customerQuery;
  537. $count = $customerQueryCount->selectRaw('count(distinct(`external_userid`)) as count')->first();
  538. $count = $count->count;
  539. $offset = ($page - 1) * $pageSize;
  540. $data = $customerQuery->selectRaw('customer_id, name, enable as relation_enable, loss_time, external_userid,
  541. user_id, remark, createtime, add_way, blacklist_status, con_user_cus')
  542. ->groupBy('con_user_cus')
  543. ->offset($offset)
  544. ->limit($pageSize)
  545. ->get()
  546. ->toArray();
  547. // 提取客户id列表
  548. $customerIdList = array_column($data, 'customer_id');
  549. // 获取客户基础信息
  550. $customerDataList = Customer::suffix($corpid)
  551. ->where('corpid', $corpid)
  552. ->whereIn('id', $customerIdList)
  553. ->get()
  554. ->keyBy('id')
  555. ->toArray();
  556. // 提取客服id列表
  557. $insideUserIdList = array_unique(array_column($data, 'user_id'));
  558. // 查询客户对应的操作记录
  559. $blackListRecord = BlackListRecord::query()
  560. ->whereIn('customer_id', $customerIdList)
  561. ->where('enable', 1)
  562. ->where('corpid', $corpid)
  563. ->get()
  564. ->toArray();
  565. $blackListData = [];
  566. foreach($blackListRecord as $item) {
  567. $key = $item['customer_id'].'-'.$item['user_id'];
  568. $blackListData[$key] = $item;
  569. }
  570. // 查询客服基本信息
  571. $insideUserDataList = DjUser::query()
  572. ->select(['user_id', 'name', 'open_user_id', 'avatar', 'avatar', 'department'])
  573. ->whereIn('user_id', $insideUserIdList)
  574. // ->where('enable', 1)
  575. ->where('corpid', $corpid)
  576. ->get()
  577. ->keyBy('user_id')
  578. ->toArray();
  579. // 获取添加渠道数据
  580. $addWayData = self::addWayList();
  581. $addWayData = array_column($addWayData, null, 'key');
  582. // 循环将数据补充
  583. foreach($data as $key => $value) {
  584. $k = $value['customer_id'].'-'.$value['user_id'];
  585. $blackList = isset($blackListData[$k]) ? $blackListData[$k] : [];
  586. $customerInfo = isset($customerDataList[$value['customer_id']]) ? $customerDataList[$value['customer_id']] : [];
  587. $customerServiceData = [];
  588. $userId = $value['user_id'];
  589. if(isset($insideUserDataList[$userId])) {
  590. $customerServiceData = $insideUserDataList[$userId];
  591. }
  592. $data[$key]['type'] = isset($customerInfo['type']) ? $customerInfo['type'] : 0;
  593. $data[$key]['avatar'] = isset($customerInfo['avatar']) ? $customerInfo['avatar'] : '';
  594. $data[$key]['reason'] = isset($blackList['reason']) ? $blackList['reason'] : '';
  595. $data[$key]['operate_time'] = isset($blackList['create_time']) ? $blackList['create_time'] : '';
  596. $data[$key]['user_list'] = $customerServiceData;
  597. $data[$key]['createtime'] = date('Y-m-d H:i', $value['createtime']);
  598. $data[$key]['add_way'] = isset($addWayData[$value['add_way']]['val']) ? $addWayData[$value['add_way']]['val'] :
  599. '';
  600. }
  601. return [$data, $count];
  602. }
  603. // 黑名单列表(新)
  604. public static function blackListNew($corpid, $addWay, $customerName, $adminId, $page, $pageSize)
  605. {
  606. $customerList = $customerIdList = null;
  607. if(!empty($addWay) || !empty($customerName)) {
  608. // 从客户关系表中查询符合条件的外部联系人id列表
  609. $customerList = CustomerDetails::suffix($corpid)
  610. ->where('corpid', $corpid)
  611. ->where('blacklist_status', 1)
  612. ->whereIn('loss_status', [0, 1, 2])
  613. ->where(function($query) use ($addWay, $customerName) {
  614. if($addWay) $query->where('add_way', $addWay);
  615. if($customerName) $query->where('customer_name', 'like', '%'.$customerName.'%');
  616. })->get();
  617. $customerIdList = $customerList->pluck('customer_id')->toArray();
  618. }
  619. // 查询客户对应的操作记录
  620. $blackListRecordQuery = BlackListRecord::query()
  621. ->where('enable', 1)
  622. ->where('corpid', $corpid);
  623. if(!is_null($customerIdList)) {
  624. $blackListRecordQuery->whereIn('customer_id', $customerIdList);
  625. }
  626. if($adminId) {
  627. $blackListRecordQuery->where('oper_user_id', $adminId);
  628. }
  629. $blackListRecordCountQuery = clone $blackListRecordQuery;
  630. $countRres = $blackListRecordCountQuery->selectRaw('count(distinct(customer_id)) as count')->first();
  631. $count = !empty($countRres->count) ? $countRres->count : 0;
  632. $offset = ($page - 1) * $pageSize;
  633. $blackListRecordList = $blackListRecordQuery
  634. ->select(['user_id', 'customer_id', 'external_userid', 'oper_user_id', 'reason', 'create_time'])
  635. ->offset($offset)
  636. ->limit($pageSize)
  637. ->orderBy('id', 'desc')
  638. ->get();
  639. $insideUserIdList = array_unique(array_column($blackListRecordList->toArray(), 'user_id'));
  640. $customerIds = array_unique(array_column($blackListRecordList->toArray(), 'customer_id'));
  641. $operUserIds = array_unique(array_column($blackListRecordList->toArray(), 'oper_user_id'));
  642. // 查询客服基本信息
  643. $insideUserDataList = DjUser::query()
  644. ->select(['user_id', 'name', 'open_user_id', 'avatar', 'avatar', 'department'])
  645. ->whereIn('user_id', $insideUserIdList)
  646. // ->where('enable', 1)
  647. ->where('corpid', $corpid)
  648. ->get();
  649. // 获取添加渠道数据
  650. $addWayData = self::addWayList();
  651. $addWayData = array_column($addWayData, null, 'key');
  652. // 获取客户基础信息
  653. $customerDataList = Customer::suffix($corpid)
  654. // ->where('corpid', $corpid)
  655. ->whereIn('id', $customerIds)
  656. ->get();
  657. // 获取操作用户信息
  658. $users = Users::query()
  659. ->whereIn('id', $operUserIds)
  660. ->get();
  661. if(empty($customerList)) {
  662. $customerList = CustomerDetails::suffix($corpid)
  663. ->where('blacklist_status', 1)
  664. ->whereIn('loss_status', [0, 1, 2])
  665. ->where('corpid', $corpid)
  666. ->whereIn('customer_id', $customerIds)
  667. ->get();
  668. }
  669. foreach($blackListRecordList as &$blackList) {
  670. # 客户详细信息 添加渠道,客户状态,添加时间
  671. $customerDetails = !empty($customerList) ? $customerList->where('user_id', $blackList->user_id)
  672. ->where('external_userid', $blackList->external_userid)->first() : null;
  673. # 客户信息 头像,昵称,来源
  674. $customer = $customerDataList->where('id', $blackList->customer_id)->first();
  675. # 客服信息 昵称
  676. $user = $insideUserDataList->where('user_id', $blackList->user_id)->first();
  677. # 操作人信息
  678. $operUser = $users->where('id', $blackList->oper_user_id)->first();
  679. $blackList->type = !empty($customer) ? $customer->type : 0;
  680. $blackList->avatar = !empty($customer) ? $customer->avatar : '';
  681. $blackList->name = !empty($customer) ? $customer->name : '';
  682. $blackList->operate_time = $blackList->create_time;
  683. $blackList->user_list = $user;
  684. $blackList->createtime = !empty($customerDetails) ? date('Y-m-d H:i', $customerDetails->createtime) : '';
  685. $blackList->add_way = !empty($addWayData[$customerDetails->add_way]) ? $addWayData[$customerDetails->add_way]['val'] : '';
  686. $blackList->oper_user = !empty($operUser) ? $operUser->name : '';
  687. $blackList->blacklist_status = 1;
  688. $blackList->remark = !empty($customerDetails) ? $customerDetails->remark : '';
  689. $blackList->relation_enable = !empty($customerDetails) ? $customerDetails->enable : 1;
  690. }
  691. return [$blackListRecordList, $count];
  692. }
  693. /**
  694. * 待分配客户列表
  695. * @param $customerName string 客户名称
  696. * @param $userIdList string 客服user_id列表
  697. * @param $addDateStart string 起始添加时间
  698. * @param $addDateEnd string 截止添加时间
  699. * @param $addWay int 添加渠道
  700. * @param $payStatus int 付款状态
  701. * @param $payNumMin int 最小付款次数
  702. * @param $payNumMax int 最大付款次数
  703. * @param $tagType int 标签筛选类型 0不筛选 1满足其一 2同时满足 3没有标签
  704. * @param $tagIdList array 标签id列表
  705. * @param $corpid string 企业id
  706. * @param $page
  707. * @param $pageSize
  708. * @return array
  709. */
  710. public static function unassignedCustomerList(
  711. $customerName, $userIdList, $addDateStart, $addDateEnd, $addWay, $payStatus, $payNumMin, $payNumMax, $tagType,
  712. $tagIdList, $corpid, $page, $pageSize
  713. )
  714. {
  715. $customerQuery = CustomerDetails::suffix($corpid)
  716. ->where('loss_status', 2)
  717. ->where('corpid', $corpid);
  718. if(!empty($customerName)) {
  719. $customerQuery = $customerQuery->where('name', 'like', '%'.$customerName.'%');}
  720. if(!empty($userIdList)) {
  721. $customerQuery = $customerQuery->whereIn('user_id', $userIdList);}
  722. if(!empty($addDateStart)) {
  723. $customerQuery = $customerQuery->where('createtime', '>=', strtotime($addDateStart . ' 00:00:00'));}
  724. if(!empty($addDateEnd)) {
  725. $customerQuery = $customerQuery->where('createtime', '<=', strtotime($addDateEnd . ' 23:59:59'));}
  726. if(is_numeric($addWay)) {
  727. $customerQuery = $customerQuery->where('add_way', $addWay);}
  728. // 付款状态,付款次数筛选
  729. if(is_numeric($payStatus)) {
  730. if(0 == $payStatus) {
  731. $payNumMax=0;
  732. $payNumMin=0;
  733. } else {
  734. if(empty($payNumMin)) {
  735. $payNumMin = 1;
  736. }
  737. }
  738. if(is_numeric($payNumMax)) {
  739. $customerQuery = $customerQuery->where('pay_num', '<=', $payNumMax);
  740. }
  741. if(is_numeric($payNumMin)) {
  742. $customerQuery = $customerQuery->where('pay_num', '>=', $payNumMin);
  743. }
  744. }
  745. if(1 == $tagType) {
  746. $tagRaw = '';
  747. foreach($tagIdList as $k=>$tagId) {
  748. $tagRaw .= $tagId;
  749. if($k < count($tagIdList) -1){
  750. $tagRaw = $tagRaw.' ';
  751. }
  752. }
  753. $customerQuery = $customerQuery->whereRaw('match(`tag_list`) against ("'.$tagRaw.'" in boolean mode)');
  754. }
  755. if(2 == $tagType) {
  756. $tagRaw = '';
  757. foreach($tagIdList as $k=>$tagId) {
  758. $tagRaw .= '+'.$tagId;
  759. if($k < count($tagIdList) -1){
  760. $tagRaw = $tagRaw.' ';
  761. }
  762. }
  763. $customerQuery = $customerQuery->whereRaw('match(`tag_list`) against ("'.$tagRaw.'" in boolean mode)');
  764. }
  765. if(3 == $tagType) {
  766. $customerQuery = $customerQuery->whereNull('tag_list');
  767. }
  768. $customerQueryCount = clone $customerQuery;
  769. $count = $customerQueryCount->selectRaw('count(distinct(`external_userid`)) as count')->first();
  770. $count = $count->count;
  771. $offset = ($page - 1) * $pageSize;
  772. $data = $customerQuery->selectRaw('customer_id, name, gender, enable as relation_enable, external_userid,
  773. user_id, remark, createtime, add_way, corpid, pay_num, loss_time, blacklist_status, con_user_cus, tag_list')
  774. ->groupBy('con_user_cus')
  775. ->offset($offset)
  776. ->limit($pageSize)
  777. ->get()
  778. ->toArray();
  779. // 提取客户id列表
  780. $customerIdList = array_column($data, 'customer_id');
  781. // 获取客户基础信息
  782. $customerDataList = Customer::suffix($corpid)
  783. ->where('corpid', $corpid)
  784. ->whereIn('id', $customerIdList)
  785. ->get()
  786. ->keyBy('id')
  787. ->toArray();
  788. $insideUserIdList = array_unique(array_column($data, 'user_id'));
  789. $tagList = Tag::query()
  790. ->where('corpid', $corpid)
  791. ->where('enable', 1)
  792. ->get()
  793. ->keyBy('tag_md5')
  794. ->toArray();
  795. // 查询客服基本信息
  796. $insideUserDataList = DjUser::query()
  797. ->select(['user_id', 'name', 'open_user_id', 'avatar', 'avatar', 'department'])
  798. ->whereIn('user_id', $insideUserIdList)
  799. // ->where('enable', 1)
  800. ->where('corpid', $corpid)
  801. ->get()
  802. ->keyBy('user_id')
  803. ->toArray();
  804. // 获取添加渠道数据
  805. $addWayData = self::addWayList();
  806. $addWayData = array_column($addWayData, null, 'key');
  807. // 查询部门列表
  808. $departmentList = DjDepartment::query()
  809. ->selectRaw('department_id, name as department_name')
  810. ->where('enable', 1)
  811. ->where('corpid', $corpid)
  812. ->get()
  813. ->keyBy('department_id')
  814. ->toArray();
  815. // 循环将数据补充
  816. foreach($data as $key => $value) {
  817. $customerInfo = isset($customerDataList[$value['customer_id']]) ? $customerDataList[$value['customer_id']] : [];
  818. $customerServiceData = [];
  819. $departmentData = null;
  820. $userId = $value['user_id'];
  821. $customerTag = [];
  822. $customerTagList = empty($value['tag_list']) ? [] : explode(',', $value['tag_list']);
  823. foreach($customerTagList as $tagId) {
  824. $tagName = isset($tagList[$tagId]['tag_name']) ? $tagList[$tagId]['tag_name'] : '';
  825. if(!empty($tagName)) {
  826. $customerTag[] = $tagName;
  827. }
  828. }
  829. $data[$key]['type'] = isset($customerInfo['type']) ? $customerInfo['type'] : 0;
  830. $data[$key]['avatar'] = isset($customerInfo['avatar']) ? $customerInfo['avatar'] : 0;
  831. if(isset($insideUserDataList[$userId])) {
  832. $customerServiceData = $insideUserDataList[$userId];
  833. $departmentIdData = explode(',', $insideUserDataList[$userId]['department']);
  834. foreach($departmentIdData as $item) {
  835. $departmentData = isset($departmentList[$item]['department_name']) ? $departmentList[$item]['department_name'] : '';
  836. }
  837. }
  838. $data[$key]['user_list'] = $customerServiceData;
  839. $data[$key]['department_list'] = [$departmentData];
  840. $data[$key]['tag_list'] = $customerTag;
  841. $data[$key]['createtime'] = date('Y-m-d H:i', $value['createtime']);
  842. $data[$key]['add_way'] = isset($addWayData[$value['add_way']]['val']) ? $addWayData[$value['add_way']]['val'] : '';
  843. }
  844. return [$data, $count];
  845. }
  846. /**
  847. * 流失客户列表
  848. * @param $userIdList array 客服user_id
  849. * @param $addDateStart string 起始添加时间
  850. * @param $addDateEnd string 截止添加时间
  851. * @param $lossDateStart string 起始流失时间
  852. * @param $lossDateEnd string 截止流失时间
  853. * @param $addDurationMin int 最小添加天数
  854. * @param $addDurationMax int 最大添加天数
  855. * @param $corpid string 企业id
  856. * @param $page
  857. * @param $pageSize
  858. * @return array
  859. */
  860. public static function lossCustomerList(
  861. $userIdList, $addDateStart, $addDateEnd, $lossDateStart, $lossDateEnd, $addDurationMin, $addDurationMax,
  862. $corpid, $page, $pageSize
  863. )
  864. {
  865. $customerQuery = CustomerDetails::suffix($corpid)
  866. ->where('loss_status', 0)
  867. ->where('corpid', $corpid);
  868. // 统一处理添加天数和添加时间
  869. if(empty($addDateStart) && empty($addDateEnd) && !empty($addDurationMin) && !empty($addDurationMax)) {
  870. // 没有添加时间筛选,只有添加时长筛选
  871. $addDateStart = date('Y-m-d', strtotime('-'.$addDurationMax.' days'));
  872. $addDateEnd = date('Y-m-d', strtotime('-'.$addDurationMin.' days'));
  873. } else if(!empty($addDateStart) && !empty($addDateEnd) && !empty($addDurationMin) && !empty($addDurationMax)) {
  874. // 既有添加时间筛选,又有添加时长筛选
  875. $addDurationStatDate = date('Y-m-d', strtotime('-'.$addDurationMax.' days'));
  876. $addDurationEndDate = date('Y-m-d', strtotime('-'.$addDurationMin.' days'));
  877. if($addDurationStatDate > $addDateStart) $addDateStart = $addDurationStatDate;
  878. if($addDurationEndDate < $addDateEnd) $addDateEnd = $addDurationEndDate;
  879. if($addDateStart > $addDateEnd) return [[], 0];
  880. }
  881. if(!empty($customerName)) {
  882. $customerQuery = $customerQuery->where('name', 'like', '%'.$customerName.'%');}
  883. if(!empty($userIdList)) {
  884. $customerQuery = $customerQuery->whereIn('user_id', $userIdList);}
  885. if(!empty($addDateStart)) {
  886. $customerQuery = $customerQuery->where('createtime', '>=', strtotime($addDateStart . ' 00:00:00'));}
  887. if(!empty($addDateEnd)) {
  888. $customerQuery = $customerQuery->where('createtime', '<=', strtotime($addDateEnd . ' 23:59:59'));}
  889. if(!empty($lossDateStart)) {
  890. $customerQuery = $customerQuery->where('loss_time', '>=', $lossDateStart.' 00:00:00');}
  891. if(!empty($lossDateEnd)) {
  892. $customerQuery = $customerQuery->where('loss_time', '<=', $lossDateEnd.' 23:59:59');}
  893. $customerQueryCount = clone $customerQuery;
  894. $count = $customerQueryCount->selectRaw('count(distinct(`external_userid`)) as count')->first();
  895. $count = $count->count;
  896. $offset = ($page - 1) * $pageSize;
  897. $data = $customerQuery->selectRaw('customer_id, name, gender, loss_time, external_userid, user_id, remark,
  898. createtime, add_way, corpid, blacklist_status, loss_status as relation_enable, con_user_cus, tag_list')
  899. ->groupBy('con_user_cus')
  900. ->offset($offset)
  901. ->limit($pageSize)
  902. ->get()
  903. ->toArray();
  904. // 提取客户id列表
  905. $customerIdList = array_column($data, 'customer_id');
  906. // 获取客户基础信息
  907. $customerDataList = Customer::suffix($corpid)
  908. ->where('corpid', $corpid)
  909. ->whereIn('id', $customerIdList)
  910. ->get()
  911. ->keyBy('id')
  912. ->toArray();
  913. $insideUserIdList = array_unique(array_column($data, 'user_id'));
  914. $tagList = Tag::query()
  915. ->where('corpid', $corpid)
  916. ->where('enable', 1)
  917. ->get()
  918. ->keyBy('tag_md5')
  919. ->toArray();
  920. // 查询客服基本信息
  921. $insideUserDataList = DjUser::query()
  922. ->select(['user_id', 'name', 'open_user_id', 'avatar', 'avatar', 'department'])
  923. ->whereIn('user_id', $insideUserIdList)
  924. // ->where('enable', 1)
  925. ->where('corpid', $corpid)
  926. ->get()
  927. ->keyBy('user_id')
  928. ->toArray();
  929. // 获取添加渠道数据
  930. $addWayData = self::addWayList();
  931. $addWayData = array_column($addWayData, null, 'key');
  932. // 查询部门列表
  933. $departmentList = DjDepartment::query()
  934. ->selectRaw('department_id, name as department_name')
  935. ->where('enable', 1)
  936. ->where('corpid', $corpid)
  937. ->get()
  938. ->keyBy('department_id')
  939. ->toArray();
  940. // 循环将数据补充
  941. foreach($data as $key => $value) {
  942. $customerInfo = isset($customerDataList[$value['customer_id']]) ? $customerDataList[$value['customer_id']] : [];
  943. $customerTag = [];
  944. $customerTagList = empty($value['tag_list']) ? [] : explode(',', $value['tag_list']);
  945. foreach($customerTagList as $tagId) {
  946. $tagName = isset($tagList[$tagId]['tag_name']) ? $tagList[$tagId]['tag_name'] : '';
  947. if(!empty($tagName)) {
  948. $customerTag[] = $tagName;
  949. }
  950. }
  951. $data[$key]['type'] = isset($customerInfo['type']) ? $customerInfo['type'] : 0;
  952. $data[$key]['avatar'] = isset($customerInfo['avatar']) ? $customerInfo['avatar'] : 0;
  953. $customerServiceData = [];
  954. $departmentData = null;
  955. $userId = $value['user_id'];
  956. if(isset($insideUserDataList[$userId])) {
  957. $customerServiceData = $insideUserDataList[$userId];
  958. $departmentIdData = explode(',', $insideUserDataList[$userId]['department']);
  959. foreach($departmentIdData as $departmentId) {
  960. $departmentData = isset($departmentList[$departmentId]['department_name']) ?
  961. $departmentList[$departmentId]['department_name'] : '';
  962. }
  963. }
  964. $data[$key]['user_list'] = $customerServiceData;
  965. $data[$key]['department_list'] = [$departmentData];
  966. $data[$key]['tag_list'] = $customerTag;
  967. $data[$key]['createtime'] = date('Y-m-d H:i', $value['createtime']);
  968. $data[$key]['add_way'] = isset($addWayData[$value['add_way']]['val']) ? $addWayData[$value['add_way']]['val'] :
  969. '';
  970. // 计算添加时长
  971. $data[$key]['add_duration_days'] = floor((time() - $value['createtime']) / 86400);
  972. }
  973. return [$data, $count];
  974. }
  975. /**
  976. * 客户分配记录
  977. * @param $corpid string 企业id
  978. * @param $handoverUserId string 转接员工userId
  979. * @param $takeoverUserId string 接替员工userid
  980. * @param $assignmentDateStart string 起始接替时间
  981. * @param $assignmentDateEnd string 截止接替时间
  982. * @param $assignmentStatus int 接替状态
  983. * @param $type int 接替类型 1在职 2离职
  984. * @param $page
  985. * @param $pageSize
  986. * @return array
  987. */
  988. public static function customerAssignmentRecordList(
  989. $corpid, $handoverUserId, $operatorId, $takeoverUserId, $assignmentDateStart, $assignmentDateEnd, $assignmentStatus,
  990. $type, $page, $pageSize
  991. )
  992. {
  993. $customerAssignmentQuery = CustomerAssignmentTotal::query()->where('corpid', $corpid)
  994. ->where('enable', 1);
  995. if(!empty($assignmentDateStart)) {
  996. $customerAssignmentQuery = $customerAssignmentQuery->where('create_time', '>=',
  997. $assignmentDateStart.' 00:00:00');
  998. }
  999. if(!empty($assignmentDateEnd)) {
  1000. $customerAssignmentQuery = $customerAssignmentQuery->where('create_time', '<=',
  1001. $assignmentDateEnd.' 23:59:59');
  1002. }
  1003. if(!empty($type)) {
  1004. $customerAssignmentQuery = $customerAssignmentQuery->where('type', $type);
  1005. }
  1006. if(!empty($handoverUserId)) {
  1007. $customerAssignmentQuery = $customerAssignmentQuery->where('handover_userid', $handoverUserId);
  1008. }
  1009. if(!empty($takeoverUserId)) {
  1010. $customerAssignmentQuery = $customerAssignmentQuery->where('takeover_userid', $takeoverUserId);
  1011. }
  1012. if(!empty($assignmentStatus)) {
  1013. $customerAssignmentQuery = $customerAssignmentQuery->where('status', $assignmentStatus);
  1014. }
  1015. if(!empty($operatorId)) {
  1016. $customerAssignmentQuery = $customerAssignmentQuery->where('operator_id', $operatorId);
  1017. }
  1018. $customerAssignmentCountQuery = clone $customerAssignmentQuery;
  1019. $count = $customerAssignmentCountQuery->count();
  1020. $offset = ($page - 1) * $pageSize;
  1021. $data = $customerAssignmentQuery->selectRaw('create_time, takeover_userid, status, type, corpid'
  1022. . ',handover_userid, id as record_id, customer_number, operator_id')->orderBy('id', 'desc')
  1023. ->offset($offset)
  1024. ->limit($pageSize)
  1025. ->get()
  1026. ->toArray();
  1027. $takeOverUserIdList = array_unique(array_column($data, 'takeover_userid'));
  1028. $handOverUserIdList = array_unique(array_column($data, 'handover_userid'));
  1029. $userIdList = array_unique(array_merge($takeOverUserIdList, $handOverUserIdList));
  1030. $userInfoList = DjUser::query()->where('corpid', $corpid)
  1031. ->whereIn('user_id', $userIdList)->get()->keyBy('user_id')->toArray();
  1032. $operatorIdList = array_unique(array_column($data, 'operator_id'));
  1033. $operatorInfoList = Users::query()->whereIn('id', $operatorIdList)
  1034. ->get()->keyBy('id')->toArray();
  1035. $errcodeConf = config('qyWechat.errcode');
  1036. foreach($data as $key => $value) {
  1037. // 接替客服昵称
  1038. $data[$key]['takeover_username'] = isset($userInfoList[$value['takeover_userid']]['name']) ?
  1039. $userInfoList[$value['takeover_userid']]['name'] : '';
  1040. // 迁出客服昵称
  1041. $data[$key]['handover_username'] = isset($userInfoList[$value['handover_userid']]['name']) ?
  1042. $userInfoList[$value['handover_userid']]['name'] : '';
  1043. // 迁移失败原因展示
  1044. $data[$key]['err_msg'] = [];
  1045. $errCodeList = CustomerAssignmentRecord::suffix($value['corpid'])->where('record_id', $value['record_id'])
  1046. ->where('errcode', '!=', 0)->select('errcode')->get();
  1047. if($errCodeList->isNotEmpty()) {
  1048. $errCodeList = $errCodeList->toArray();
  1049. foreach ($errCodeList as $errCode) {
  1050. $errCode = $errCode['errcode'];
  1051. $errMsg = $errcodeConf[$errCode] ?? null;
  1052. $errMsg = $value['status'] == '-1' ? $errMsg : ('部分迁移失败由于'.$errMsg);
  1053. if($errMsg && !in_array($errMsg, $data[$key]['err_msg'])) {
  1054. $data[$key]['err_msg'][] = $errMsg;
  1055. }
  1056. }
  1057. }
  1058. // 创建人
  1059. $data[$key]['operator_name'] = isset($operatorInfoList[$value['operator_id']]['name']) ?
  1060. $operatorInfoList[$value['operator_id']]['name'] : '';
  1061. }
  1062. return [$data, $count];
  1063. }
  1064. /**
  1065. * 客户分配记录
  1066. * @param $corpid string 企业id
  1067. * @param $customerName string 客户名称
  1068. * @param $recordId int 分配记录id
  1069. * @param $assignmentDateStart string 起始接替时间
  1070. * @param $assignmentDateEnd string 截止接替时间
  1071. * @param $assignmentStatus int 接替状态
  1072. * @param $page
  1073. * @param $pageSize
  1074. * @return array
  1075. */
  1076. public static function customerAssignmentList(
  1077. $corpid, $customerName, $recordId, $assignmentDateStart, $assignmentDateEnd, $assignmentStatus, $page, $pageSize
  1078. )
  1079. {
  1080. $customerAssignmentQuery = CustomerAssignmentRecord::suffix($corpid)
  1081. ->where('corpid', $corpid)->where('enable', 1)->where('record_id', $recordId);
  1082. if(!empty($customerName)){
  1083. $customerAssignmentQuery = $customerAssignmentQuery->where('customer_name', 'like', '%'.$customerName.'%');
  1084. }
  1085. if(!empty($assignmentDateStart)) {
  1086. $customerAssignmentQuery = $customerAssignmentQuery->where('takeover_time', '>=',
  1087. $assignmentDateStart.' 00:00:00');
  1088. }
  1089. if(!empty($assignmentDateEnd)) {
  1090. $customerAssignmentQuery = $customerAssignmentQuery->where('takeover_time', '<=',
  1091. $assignmentDateEnd.' 23:59:59');
  1092. }
  1093. if(!empty($assignmentStatus)) {
  1094. if(2 == $assignmentStatus) {
  1095. $customerAssignmentQuery = $customerAssignmentQuery->whereIn('status', [0, 2]);
  1096. } else if(-1 == $assignmentStatus) {
  1097. $customerAssignmentQuery = $customerAssignmentQuery->where('errcode', '>', 0);
  1098. }else {
  1099. $customerAssignmentQuery = $customerAssignmentQuery->where('status', $assignmentStatus);
  1100. }
  1101. }
  1102. $customerAssignmentCountQuery = clone $customerAssignmentQuery;
  1103. $count = $customerAssignmentCountQuery->count();
  1104. $offset = ($page - 1) * $pageSize;
  1105. $data = $customerAssignmentQuery->select(['customer_name', 'takeover_userid', 'status', 'takeover_time',
  1106. 'customer_id', 'corpid', 'errcode', 'handover_userid'])->orderBy('id', 'desc')
  1107. ->offset($offset)
  1108. ->limit($pageSize)
  1109. ->get()
  1110. ->toArray();
  1111. $takeOverUserIdList = array_unique(array_column($data, 'takeover_userid'));
  1112. $handOverUserIdList = array_unique(array_column($data, 'handover_userid'));
  1113. $userIdList = array_unique(array_merge($takeOverUserIdList, $handOverUserIdList));
  1114. $userInfoList = DjUser::query()
  1115. ->where('corpid', $corpid)
  1116. ->whereIn('user_id', $userIdList)
  1117. ->get()
  1118. ->keyBy('user_id')
  1119. ->toArray();
  1120. $errcodeConf = config('qyWechat.errcode');
  1121. foreach($data as $key => $value) {
  1122. if( 0 == $value['status']) {
  1123. $data[$key]['status'] = 2;
  1124. }
  1125. $data[$key]['takeover_username'] = isset($userInfoList[$value['takeover_userid']]['name']) ?
  1126. $userInfoList[$value['takeover_userid']]['name'] : '';
  1127. $data[$key]['handover_username'] = isset($userInfoList[$value['handover_userid']]['name']) ?
  1128. $userInfoList[$value['handover_userid']]['name'] : '';
  1129. $data[$key]['err_msg'] = null;
  1130. if(!empty($value['errcode'])) {
  1131. $data[$key]['err_msg'] = $errcodeConf[$value['errcode']] ?? null;
  1132. }
  1133. }
  1134. return [$data, $count];
  1135. }
  1136. /**
  1137. * 客户动态列表
  1138. * @param $corpid string 企业id
  1139. * @param $customerId integer 客户id
  1140. * @return array
  1141. */
  1142. public static function customerDynamicList($corpid, $customerId)
  1143. {
  1144. // 根据客户id查询客户信息
  1145. $customerInfo = Customer::suffix($corpid)
  1146. ->where('id', $customerId)
  1147. ->first();
  1148. if(empty($customerInfo)) {
  1149. return [];
  1150. }
  1151. $externalUserid = $customerInfo->external_userid;
  1152. $data = CustomerDynamic::suffix($corpid)
  1153. ->select(['type', 'handover_userid', 'takeover_userid', 'create_time', 'radar_name', 'request_id'])
  1154. ->where('corpid', $corpid)
  1155. ->where('external_userid', $externalUserid)
  1156. ->orderBy('create_time', 'desc')
  1157. ->get()
  1158. ->toArray();
  1159. // 提取客服信息
  1160. $handOverUserIdList = array_column($data, 'handover_userid');
  1161. $takeOverUserIdList = array_column($data, 'takeover_userid');
  1162. $userIdList = array_unique(array_merge($handOverUserIdList, $takeOverUserIdList));
  1163. $userDataList = DjUser::query()
  1164. ->select(['user_id', 'name'])
  1165. ->whereIn('user_id', $userIdList)
  1166. ->where('corpid', $corpid)
  1167. ->get()
  1168. ->keyBy('user_id')
  1169. ->toArray();
  1170. // 提取雷达request_id
  1171. $requestIdList = array_column($data, 'request_id');
  1172. $radarCustomerBehaviorList = RadarCustomerBehavior::query()
  1173. ->whereIn('request_id', $requestIdList)
  1174. ->get();
  1175. $weekName = array('星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六');
  1176. $result = [];
  1177. foreach($data as $key => $item) {
  1178. $date = date('Y-m-d', strtotime($item['create_time']));
  1179. $item['customer_id'] = $customerInfo->id;
  1180. $item['customer_name'] = $customerInfo->name;
  1181. $value = $item;
  1182. if(!isset($result[$date])) {
  1183. $week = $weekName[date('w', strtotime($item['create_time']))];
  1184. $result[$date]['date'] = $date;
  1185. $result[$date]['week'] = $week;
  1186. }
  1187. $value['time'] = date('H:i:s', strtotime($item['create_time']));
  1188. if(!empty($item['handover_userid'])) {
  1189. $item['handover_username'] = isset($userDataList[$item['handover_userid']]['name']) ?
  1190. $userDataList[$item['handover_userid']]['name'] : '';
  1191. } else {
  1192. $item['handover_username'] = '';
  1193. }
  1194. if(!empty($item['takeover_userid'])) {
  1195. $item['takeover_username'] = isset($userDataList[$item['takeover_userid']]['name']) ?
  1196. $userDataList[$item['takeover_userid']]['name'] : '';
  1197. } else {
  1198. $item['takeover_username'] = '';
  1199. }
  1200. switch($item['type']) {
  1201. case 1:
  1202. $value['type_desc'] = '添加好友';
  1203. $value['content'] = $item['customer_name'].'添加了'.$item['handover_username'].'为好友';
  1204. break;
  1205. case 2:
  1206. $value['type_desc'] = '被员工删除';
  1207. $value['content'] = $item['handover_username'].'将'.$item['customer_name'].'删除了';
  1208. break;
  1209. case 3:
  1210. $value['type_desc'] = '删除跟进客户';
  1211. $value['content'] = $item['customer_name'].'将'.$item['handover_username'].'删除了';
  1212. break;
  1213. case 4:
  1214. $value['type_desc'] = '客户转移';
  1215. $value['content'] = $item['customer_name'].'从'.$item['handover_username'].'迁移到了'.
  1216. $item['takeover_username'];
  1217. break;
  1218. case 5:
  1219. $value['type_desc'] = '客户下单';
  1220. $value['content'] = '客户'.$item['customer_name'].'下单了';
  1221. break;
  1222. case 6:
  1223. $value['type_desc'] = '加入黑名单';
  1224. $value['content'] = '将'.$item['customer_name'].'加入黑名单';
  1225. break;
  1226. case 7:
  1227. $value['type_desc'] = '移出黑名单';
  1228. $value['content'] = '将'.$item['customer_name'].'从黑名单中移除';
  1229. break;
  1230. case 8:
  1231. $value['type_desc'] = '聊天';
  1232. $value['content'] = $item['customer_name'].'与'.$item['handover_username'].'聊天了';
  1233. break;
  1234. case 9:
  1235. $value['type_desc'] = '查看智能雷达';
  1236. $radarCustomerBehaviorInfo = $radarCustomerBehaviorList->where('request_id', $item['request_id'])->first();
  1237. $second = !empty($radarCustomerBehaviorInfo->view_time) ? $radarCustomerBehaviorInfo->view_time : 0;
  1238. $value['content'] = $item['customer_name'].'点击了【'.$item['radar_name'].'】雷达,浏览时长'.$second.'秒';
  1239. break;
  1240. default:
  1241. $value['type_desc'] = '';
  1242. $value['content'] = '';
  1243. break;
  1244. }
  1245. $result[$date]['sub'][] = $value;
  1246. }
  1247. return array_values($result);
  1248. }
  1249. /**
  1250. * 客户详情-编辑标签时标签展示
  1251. * @param $corpid string 企业id
  1252. * @param $customerId int 客户id
  1253. * @param $userId string 客服user_id
  1254. * @return array
  1255. */
  1256. public static function customerDetailTagList($corpid, $customerId, $userId)
  1257. {
  1258. $followList = CustomerDetails::suffix($corpid)
  1259. ->where('corpid', $corpid)
  1260. ->where('customer_id', $customerId)
  1261. ->where('user_id', $userId)
  1262. ->where('loss_status', 1)
  1263. ->first();
  1264. $tagIdList = empty($followList->tag_list) ? [] : explode(',', $followList->tag_list);
  1265. // 获取企业下所有标签数据
  1266. $tagGroupList = TagGroup::query()
  1267. ->where('corpid', $corpid)
  1268. ->where('enable', 1)
  1269. ->orderBy('order', 'asc')
  1270. ->get()
  1271. ->toArray();
  1272. $data = [];
  1273. foreach($tagGroupList as $tagGroup) {
  1274. $item = [];
  1275. $item['group_id'] = $tagGroup['group_id'];
  1276. $item['group_name'] = $tagGroup['group_name'];
  1277. $tagList = Tag::query()
  1278. ->select(['tag_name', 'tag_md5', 'order'])
  1279. ->where('corpid', $corpid)
  1280. ->where('enable', 1)
  1281. ->where('group_id', $item['group_id'])
  1282. ->get()
  1283. ->toArray();
  1284. foreach($tagList as $k=>$v) {
  1285. $tagList[$k]['tag_id'] = $v['tag_md5'];
  1286. if(in_array($v['tag_md5'], $tagIdList)){
  1287. $tagList[$k]['selected'] = 1;
  1288. } else {
  1289. $tagList[$k]['selected'] = 0;
  1290. }
  1291. }
  1292. $item['tag_list'] = $tagList;
  1293. $item['tag_num'] = count($tagList);
  1294. if($item['tag_num'] > 0) {
  1295. $data[] = $item;
  1296. }
  1297. }
  1298. return $data;
  1299. }
  1300. // 更新客户标签
  1301. public static function customerDetailTagUpdate($corpid, $externalUserId, $userId, $selectedTagIdList)
  1302. {
  1303. // 判断客户跟进关系状态是否支持打标签
  1304. $customerRelation = CustomerDetails::suffix($corpid)
  1305. ->where('corpid', $corpid)
  1306. ->where('external_userid', $externalUserId)
  1307. ->where('user_id', $userId)
  1308. ->where('enable', 1)
  1309. ->where('loss_status', 1)
  1310. ->first();
  1311. if(empty($customerRelation)) {
  1312. // 已流失或者是待分配状态 因为没有客服跟进关系,所以不能调用企业微信打标签功能
  1313. return ['客户状态不可用,不可编辑标签', 2008];
  1314. }
  1315. $tagIdList = Tag::query()
  1316. ->where('corpid', $corpid)
  1317. ->where('enable', 1)
  1318. ->get()
  1319. ->keyBy('tag_md5')
  1320. ->toArray();
  1321. $customerTagIdList = empty($customerRelation->tag_list) ? [] : explode(',', $customerRelation->tag_list);
  1322. $removeTagIdList = array_values(array_diff($customerTagIdList, $selectedTagIdList));
  1323. $addTagIdList = array_values(array_diff($selectedTagIdList, $customerTagIdList));
  1324. $newRemoveTagIdList = [];
  1325. $newAddTagIdList = [];
  1326. foreach ($removeTagIdList as $tagId) {
  1327. if(!isset($tagIdList[$tagId]['tag_id'])) continue;
  1328. $newRemoveTagIdList[] = $tagIdList[$tagId]['tag_id'];
  1329. }
  1330. foreach($addTagIdList as $tagId) {
  1331. if(!isset($tagIdList[$tagId]['tag_id'])) continue;
  1332. $newAddTagIdList[] = $tagIdList[$tagId]['tag_id'];
  1333. }
  1334. $accessToken = AuthorizeCorp::getAccessToken($corpid, '更新客户标签');
  1335. $url = 'https://qyapi.weixin.qq.com/cgi-bin/externalcontact/mark_tag?access_token='.$accessToken;
  1336. $params['userid'] = $userId;
  1337. $params['external_userid'] = $externalUserId;
  1338. $params['add_tag'] = $newAddTagIdList;
  1339. $params['remove_tag'] = $newRemoveTagIdList;
  1340. $result = HttpService::httpPost($url, json_encode($params), true);
  1341. $result= json_decode($result, 1);
  1342. // Log::logInfo('编辑客户标签结果', [$result], 'mark_tag');
  1343. if(isset($result['errcode']) && $result['errcode'] == 0) {
  1344. // 处理系统数据
  1345. // $res = self::updateCustomerTag($corpid, $externalUserId, $userId, $addTagIdList, $removeTagIdList);
  1346. $res = TagService::updateLocalCustomerTagSecond($corpid, $userId, $externalUserId, $newAddTagIdList,
  1347. $newRemoveTagIdList);
  1348. if($res) {
  1349. return ['编辑成功', 0];
  1350. } else {
  1351. return ['编辑失败', 400];
  1352. }
  1353. } else {
  1354. if('84061' == $result['errcode']) {
  1355. RedisModel::lPush(CustomerDetails::CUSTOMER_LOSS_INFO_RDS, json_encode([
  1356. 'corpid' => $corpid,
  1357. 'user_id' => $userId,
  1358. 'external_userid' => $externalUserId
  1359. ], 256));
  1360. return ['编辑失败,客户关系不存在', 2011];
  1361. }
  1362. if('40068' == $result['errcode']) {
  1363. // 编辑标签ID不存在,报警
  1364. EmailQueue::rPush('编辑客户标签时,客户标签不存在', json_encode([
  1365. 'corpid' => $corpid,'user_id' => $userId, 'external_userid' => $externalUserId,
  1366. 'add_tag' => $newAddTagIdList, 'remove_tag' => $newRemoveTagIdList
  1367. ], 256), ['song.shen@kuxuan-inc.com'], '猎羽');
  1368. }
  1369. Log::logError('单独编辑客户标签失败', [
  1370. 'params' => ['user_id' => $userId, 'corpid' => $corpid, 'external_userid' => $externalUserId
  1371. , 'add_tag' => $newAddTagIdList, 'remove_tag' => $newRemoveTagIdList],
  1372. 'result' => $result
  1373. ], 'mark_tag');
  1374. // 记录
  1375. return ['编辑失败', 400];
  1376. }
  1377. }
  1378. public static function updateCustomerTag($corpid, $externalUserId, $userId, $addTagIdList, $removeTagIdList)
  1379. {
  1380. \DB::begintransaction();
  1381. if(!empty($removeTagIdList)) {
  1382. $res1 = CustomerTag::suffix($corpid)
  1383. ->where('corpid', $corpid)
  1384. ->where('external_userid', $externalUserId)
  1385. ->where('user_id', $userId)
  1386. ->whereIn('tag_id', $removeTagIdList)
  1387. ->update(['enable' => 0]);
  1388. } else {
  1389. $res1 = true;
  1390. }
  1391. if(!empty($addTagIdList)) {
  1392. // 查询标签对应的标签名以及标签组名
  1393. $tagList = Tag::query()
  1394. ->where('corpid', $corpid)
  1395. ->whereIn('tag_id', $addTagIdList)
  1396. ->get()
  1397. ->keyBy('tag_id')
  1398. ->toArray();
  1399. $groupIdList = array_unique(array_column($tagList, 'group_id'));
  1400. $tagGroupList = TagGroup::query()
  1401. ->where('corpid', $corpid)
  1402. ->whereIn('group_id', $groupIdList)
  1403. ->get()
  1404. ->keyBy('group_id')
  1405. ->toArray();
  1406. // 获取员工id
  1407. $customeInfo = Customer::suffix($corpid)
  1408. ->where('corpid', $corpid)
  1409. ->where('external_userid', $externalUserId)
  1410. ->first()
  1411. ->toArray();
  1412. $createData = [];
  1413. foreach($tagList as $tag) {
  1414. $item['corpid'] = $corpid;
  1415. $item['user_id'] = $userId;
  1416. $item['customer_id'] = $customeInfo['id'];
  1417. $item['external_userid'] = $externalUserId;
  1418. $item['tag_id'] = $tag['tag_id'];
  1419. $item['tag_name'] = $tag['tag_name'];
  1420. $item['type'] = 1;
  1421. $item['group_name'] = isset($tagGroupList[$tag['group_id']]['group_name']) ?
  1422. $tagGroupList[$tag['group_id']]['group_name'] : '';
  1423. $createData[] = $item;
  1424. }
  1425. $res2 = CustomerTag::suffix($corpid)
  1426. ->insert($createData);
  1427. } else {
  1428. $res2 = true;
  1429. }
  1430. if($res1 > 0 && $res2 > 0) {
  1431. \DB::commit();
  1432. return true;
  1433. } else {
  1434. \DB::rollback();
  1435. return false;
  1436. }
  1437. }
  1438. // 客户迁移
  1439. public static function addCustomerAssignmentNew(
  1440. $corpid, $takeoverUserId, $externalUserList, $transferSuccessMsg, $type, $portraitInheritance, $selectAll,
  1441. $filterCustomerList, $params
  1442. )
  1443. {
  1444. $requestData = [
  1445. 'corpid' => $corpid,
  1446. 'takeover_userid' => $takeoverUserId,
  1447. 'external_user_list' => $externalUserList,
  1448. 'transfer_success_msg' => $transferSuccessMsg,
  1449. 'type' => $type,
  1450. 'portrait_inheritance' => $portraitInheritance,
  1451. 'select_all' => $selectAll,
  1452. 'filter_customer_list' => $filterCustomerList,
  1453. 'params' => $params,
  1454. ];
  1455. // 检测接替员工是否为在职状态
  1456. $takeoverUserInfo = DjUser::query()->where('user_id', $takeoverUserId)->where('corpid', $corpid)
  1457. ->where('status', 1)->first();
  1458. if(empty($takeoverUserInfo)){
  1459. return ['接替员工状态不合法', 2012];
  1460. }
  1461. if(1 == $selectAll) {
  1462. $res = BatchTransferCustomer::query()
  1463. ->insert([
  1464. 'corpid' => $corpid,
  1465. 'params' => json_encode($params, JSON_UNESCAPED_UNICODE),
  1466. 'takeover_userid' => $takeoverUserId,
  1467. 'filter_customer_list' => json_encode($filterCustomerList, JSON_UNESCAPED_UNICODE),
  1468. 'transfer_success_msg' => $transferSuccessMsg,
  1469. 'portrait_inheritance' => $portraitInheritance,
  1470. 'type' => $type,
  1471. 'operator_id' => \Auth::id(),
  1472. ]);
  1473. if(isset($res)) {
  1474. return ['申请成功', 0];
  1475. } else {
  1476. Log::logInfo('addCustomerAssignmentNew 申请失败, params:'.json_encode($requestData, JSON_UNESCAPED_UNICODE), [], 'interface');
  1477. return ['申请失败', 400];
  1478. }
  1479. } else {
  1480. $handoverUserList = [];
  1481. foreach($externalUserList as $data) {
  1482. $handoverUserList[$data['handover_userid']][] = $data['external_userid'];
  1483. }
  1484. \DB::begintransaction();
  1485. foreach($handoverUserList as $handoverUserId => $externalUserIdList) {
  1486. $enableExternalUserIdList = [];
  1487. if(1 == $type) {
  1488. // 检测接替员工是否为在职状态
  1489. $handoverUserInfo = DjUser::query()->where('user_id', $handoverUserId)->where('corpid', $corpid)
  1490. ->where('status', 1)->first();
  1491. if(empty($handoverUserInfo)){
  1492. return ['迁出员工状态不合法', 2013];
  1493. }
  1494. $enableExternalUserIdList = CustomerDetails::suffix($corpid)
  1495. ->where('corpid', $corpid)
  1496. ->where('user_id', $handoverUserId)
  1497. ->whereIn('external_userid', $externalUserIdList)
  1498. // ->where('loss_status', 1)
  1499. ->get();
  1500. $enableExternalUserIdList = $enableExternalUserIdList->isNotEmpty() ?
  1501. $enableExternalUserIdList->toArray() : [];
  1502. $enableExternalUserIdList = array_column($enableExternalUserIdList, 'external_userid');
  1503. } else if(2 == $type) {
  1504. // 检测接替员工是否为在职状态
  1505. $handoverUserInfo = DjUser::query()->where('user_id', $handoverUserId)->where('corpid', $corpid)
  1506. ->where('status', 5)->first();
  1507. if(empty($handoverUserInfo)){
  1508. return ['迁出员工状态不合法', 2013];
  1509. }
  1510. $enableExternalUserIdList = CustomerDetails::suffix($corpid)
  1511. ->where('corpid', $corpid)
  1512. ->where('user_id', $handoverUserId)
  1513. ->whereIn('external_userid', $externalUserIdList)
  1514. // ->where('loss_status', 2)
  1515. ->get();
  1516. $enableExternalUserIdList = $enableExternalUserIdList->isNotEmpty() ?
  1517. $enableExternalUserIdList->toArray() : [];
  1518. $enableExternalUserIdList = array_column($enableExternalUserIdList, 'external_userid');
  1519. }
  1520. if(count($enableExternalUserIdList) > 0) {
  1521. $res = CustomerAssignmentTotal::query()
  1522. ->insert([
  1523. 'corpid' => $corpid,
  1524. 'handover_userid' => $handoverUserId,
  1525. 'takeover_userid' => $takeoverUserId,
  1526. 'transfer_success_msg' => $transferSuccessMsg,
  1527. 'external_userid' => json_encode($enableExternalUserIdList),
  1528. 'type' => $type,
  1529. 'status' => 1,
  1530. 'portrait_inheritance' => $portraitInheritance,
  1531. 'operator_id' => \Auth::id(),
  1532. 'customer_number' => count($enableExternalUserIdList)
  1533. ]);
  1534. if(!$res) {
  1535. \DB::rollBack();
  1536. Log::logInfo('addCustomerAssignmentNew 申请失败, params:'.json_encode($requestData, JSON_UNESCAPED_UNICODE), [], 'interface');
  1537. return ['申请失败', 400];
  1538. }
  1539. }
  1540. }
  1541. \DB::commit();
  1542. return ['申请成功', 0];
  1543. }
  1544. }
  1545. /**
  1546. * 获取外部联系人信息
  1547. * */
  1548. public static function updateExternalInfo($corpid, $userId, $externalUserid, $state, $changeType)
  1549. {
  1550. $flag = true;
  1551. $nextCursor = null;
  1552. try {
  1553. while($flag) {
  1554. # 获取外部联系人详情
  1555. $externalUserData = ExternalContactService::getExternalContactDetail($corpid, $externalUserid, $nextCursor);
  1556. if(empty($externalUserData)) {
  1557. if($externalUserData === false) {
  1558. Log::logError('外部联系人详情获取失败', [
  1559. 'corpid' => $corpid,
  1560. 'user_id' => $userId,
  1561. 'external_userid' => $externalUserid,
  1562. 'state' => $state,
  1563. 'change_type' => $changeType,
  1564. 'nextCursor' => $nextCursor
  1565. ], 'UpdateExternalContact');
  1566. }
  1567. $flag = false;
  1568. continue;
  1569. }
  1570. # 更新外部联系人信息至数据库
  1571. $customerId = 0;
  1572. ExternalContactService::updateExternalContacts($externalUserData, $corpid, $userId, $state, $changeType, $customerId);
  1573. if($customerId) {
  1574. // $followInfo = [
  1575. // 'createtime' => time(),
  1576. // 'userid' => $userId,
  1577. // ];
  1578. // TagService::autoMarkCustomerAddDateTag($corpid, $customerId, $followInfo);
  1579. if('add_external_contact' == $changeType) {
  1580. # 设置自动标签
  1581. $tagNameList = [date('Y年m月')];
  1582. $redisArr = [
  1583. 'corpid' => $corpid, 'user_id' => $userId, 'external_userid' => $externalUserid,
  1584. 'tag_group_name' => '自动标签', 'tag_name_list' => $tagNameList, 'type' => 2
  1585. ];
  1586. RedisModel::lPush(CustomerTagService::CUSTOMER_TAG_MARK_RDS, json_encode($redisArr, 1));
  1587. }
  1588. }
  1589. # 翻页查询外部联系人所有的关联员工
  1590. if(isset($externalUserData['next_cursor']) && !empty($externalUserData['next_cursor'])) {
  1591. $nextCursor = $externalUserData['next_cursor'];
  1592. } else {
  1593. $flag = false;
  1594. }
  1595. }
  1596. } catch (\Exception $e) {
  1597. Log::logError('获取外部联系人详情', [
  1598. 'line' => $e->getLine(),
  1599. 'msg' => $e->getMessage(),
  1600. 'corpid' => $corpid,
  1601. 'user_id' => $userId,
  1602. 'external_userid' => $externalUserid,
  1603. 'state' => $state,
  1604. 'change_type' => $changeType,
  1605. 'nextCursor' => $nextCursor
  1606. ], 'UpdateExternalContact-Exception');
  1607. $flag = false;
  1608. return false;
  1609. }
  1610. return true;
  1611. }
  1612. /**
  1613. * 获取外部联系人信息
  1614. * */
  1615. public static function getCustomerInfo($corpid, $externalUserid, $userid)
  1616. {
  1617. return CustomerDetails::suffix($corpid)
  1618. ->select(['name', 'remark'])
  1619. ->where('user_id', $userid)
  1620. ->where('external_userid', $externalUserid)
  1621. ->where('loss_status', 1)
  1622. ->first();
  1623. }
  1624. /**
  1625. * 获取待邀请入群的新用户
  1626. * */
  1627. public static function waitInvite($deviceId, $beginTime, $endTime, $retry, $requestCorpId, $customerNumber)
  1628. {
  1629. # 校验开始时间和结束时间,最大时间差不超过十分钟
  1630. $endTimestamps = strtotime($endTime);
  1631. $beginTimestamps = strtotime($beginTime);
  1632. if($endTimestamps - $beginTimestamps > 600) {
  1633. $beginTimestamps = strtotime('-10 minute', $endTimestamps);
  1634. }
  1635. # 持续邀请客户入群任务单次最大请求次数配置
  1636. $maxRequestNum = 5;
  1637. # 查询设备对应的企微及客服信息
  1638. $userList = AndroidBindCorp::select('corpid', 'user_id')->where('device_id', $deviceId)
  1639. ->where('enable', 1)->where(function($query) use ($requestCorpId) {
  1640. if($requestCorpId) $query->where('corpid', $requestCorpId);
  1641. })->get();
  1642. if(empty($userList)) {
  1643. Log::logError('此设备号未绑定任何企微信息', [
  1644. 'device_id' => $deviceId
  1645. ], 'WaitInvite');
  1646. return [];
  1647. }
  1648. # 查询支持无障碍模式的企微列表
  1649. $corpIds = $userList->pluck('corpid');
  1650. $corpList = AuthorizeCorp::select('corpid', 'corp_name')->whereIn('corpid', $corpIds)->where('enable', 1)->get();
  1651. $data = [];
  1652. # 查询企微和客服是否配置了邀请入群功能
  1653. foreach ($userList as $userInfo) {
  1654. $corpid = $userInfo->corpid ?? null;
  1655. $userId = $userInfo->user_id ?? null;
  1656. if(!$corpid || !$userId) {
  1657. Log::logError('检索待邀请入群的客服时存在异常数据', [
  1658. 'user_info' => $userInfo->toArray(),
  1659. 'device_id' => $deviceId
  1660. ], 'WaitInvite');
  1661. continue;
  1662. }
  1663. # 使用更新时间最近的配置
  1664. $inviteConfig = InviteChatGroupRule::where('corpid', $corpid)
  1665. ->whereRaw("FIND_IN_SET('".$userId."', `users`)")
  1666. ->where('status', 1)->where('enable', 1)
  1667. ->orderBy('updated_at', 'desc')
  1668. ->first();
  1669. if(empty($inviteConfig)) {
  1670. $inviteConfig = InviteChatGroupRule::where('corpid', $corpid)
  1671. ->where('is_for_all', 1)
  1672. ->where('enable', 1)->where('status', 1)
  1673. ->orderBy('updated_at', 'desc')
  1674. ->first();
  1675. }
  1676. if(empty($inviteConfig)) {
  1677. Log::logError('客服暂未配置邀请入群规则', [
  1678. 'user_info' => $userInfo->toArray(),
  1679. 'device_id' => $deviceId
  1680. ], 'WaitInvite');
  1681. continue;
  1682. }
  1683. $datetimeNow = date('Y-m-d H:i:s');
  1684. if(1 == $inviteConfig->continuously_attract_groups) {// 客户续拉任务
  1685. if(1 == $inviteConfig->invite_type) {
  1686. Log::logError('含有重试标识,邀请类型不能是立即邀请', [
  1687. 'user_info' => $userInfo->toArray(),
  1688. 'device_id' => $deviceId,
  1689. 'config_info' => $inviteConfig->toArray()
  1690. ], 'WaitInvite');
  1691. continue;
  1692. }
  1693. # 重试
  1694. if(strtotime($inviteConfig->next_invite_time) > time()) {
  1695. # 查询当前请求次数
  1696. $requestNum = WaitInviteRequest::getNumber($inviteConfig->id, $corpid, $deviceId);
  1697. $requestNum = intval($requestNum);
  1698. if($requestNum >= $maxRequestNum) {
  1699. Log::logError('客户续拉任务重试次数超过限制', [
  1700. 'user_info' => $userInfo->toArray(),
  1701. 'device_id' => $deviceId,
  1702. 'config_info' => $inviteConfig->toArray(),
  1703. 'request_num' => $requestNum+1,
  1704. 'max_request_num' => $maxRequestNum,
  1705. 'retry' => $retry
  1706. ], 'WaitInvite');
  1707. continue;
  1708. } else {
  1709. WaitInviteRequest::saveData($inviteConfig->id, $corpid, $deviceId, $requestNum+1);
  1710. Log::logInfo('客户续拉任务', [
  1711. 'user_info' => $userInfo->toArray(),
  1712. 'device_id' => $deviceId,
  1713. 'config_info' => $inviteConfig->toArray(),
  1714. 'request_num' => $requestNum+1,
  1715. 'retry' => $retry
  1716. ], 'WaitInvite');
  1717. }
  1718. } else { # 首次
  1719. $customerNumber = null;
  1720. WaitInviteRequest::saveData($inviteConfig->id, $corpid, $deviceId, 1);
  1721. Log::logInfo('客户续拉任务首次请求',[
  1722. 'user_info' => $userInfo->toArray(),
  1723. 'device_id' => $deviceId,
  1724. 'config_info' => $inviteConfig->toArray(),
  1725. 'request_num' => 1,
  1726. 'retry' => $retry
  1727. ], 'WaitInvite');
  1728. $nextInviteTime = CircleMassMsgService::getNextSendTime(1, $inviteConfig->invite_time, 0, $datetimeNow);
  1729. $result = InviteChatGroupRule::where('id', $inviteConfig->id)->update(['next_invite_time' => $nextInviteTime]);
  1730. if(!$result) {
  1731. Log::logError('更新下次邀请时间失败', [
  1732. 'user_info' => $userInfo->toArray(),
  1733. 'device_id' => $deviceId,
  1734. 'config_info' => $inviteConfig->toArray(),
  1735. 'next_invite_time' => $nextInviteTime
  1736. ], 'WaitInvite');
  1737. }
  1738. }
  1739. } else {
  1740. $customerNumber = null;
  1741. if($inviteConfig->invite_type == 2 && strtotime($inviteConfig->next_invite_time) > time()) {
  1742. Log::logError('对应客服尚未到定时邀请入群时间', [
  1743. 'user_info' => $userInfo->toArray(),
  1744. 'device_id' => $deviceId,
  1745. 'config_info' => $inviteConfig->toArray()
  1746. ], 'WaitInvite');
  1747. continue;
  1748. }
  1749. # 满足邀请入群时间
  1750. # 更新下次邀请入群时间
  1751. if($inviteConfig->invite_type == 2) { // 定时邀请
  1752. $nextInviteTime = CircleMassMsgService::getNextSendTime(1, $inviteConfig->invite_time, 0, $datetimeNow);
  1753. $result = InviteChatGroupRule::where('id', $inviteConfig->id)->update(['next_invite_time' => $nextInviteTime]);
  1754. if(!$result) {
  1755. Log::logError('更新下次邀请时间失败', [
  1756. 'user_info' => $userInfo->toArray(),
  1757. 'device_id' => $deviceId,
  1758. 'config_info' => $inviteConfig->toArray(),
  1759. 'next_invite_time' => $nextInviteTime
  1760. ], 'WaitInvite');
  1761. }
  1762. }
  1763. }
  1764. # 规则ID
  1765. $ruleId = $inviteConfig->id;
  1766. $joinType = $inviteConfig->join_type;
  1767. $filterType = $inviteConfig->filter_type;
  1768. $addTimeLater = $inviteConfig->add_time_later;
  1769. $announcement = $inviteConfig->announcement;
  1770. # 单次创建群聊任务建群数量上限
  1771. $upperLimit = $inviteConfig->upper_limit;
  1772. # 建群类型 1当前客户直接建群 2其他客服建群
  1773. $ownerType = in_array($inviteConfig->owner_type, [1, 2]) ? $inviteConfig->owner_type : 1;
  1774. # 是否为持续拉取客户入群任务
  1775. $continuouslyAttractGroups = $inviteConfig->continuously_attract_groups;
  1776. $cursor = intval($inviteConfig->cursor);
  1777. # 获取邀请入群配置
  1778. $configList = InviteChatGroupDetail::select('corpid', 'chat_id', 'sort', 'user_limit')
  1779. ->where('enable', 1)->where('status', 1)->where('rule_id', $ruleId)
  1780. ->orderBy('sort')->get();
  1781. if(empty($configList)) {
  1782. Log::logError('该企业成员无可用的群配置', [
  1783. 'device_id' => $deviceId, 'join_type' => $joinType,
  1784. 'corpid' => $corpid, 'add_time_later' => $addTimeLater,
  1785. 'filter_type' => $filterType, 'add_time_st' => $inviteConfig->add_time_st, 'add_time_et' => $inviteConfig->add_time_et,
  1786. 'userId' => $userId, 'rule_id' => $ruleId,
  1787. ], 'WaitInvite');
  1788. continue;
  1789. }
  1790. # 获取群信息
  1791. $chatIdList = $configList->pluck('chat_id');
  1792. $chatGroupData = ChatGroup::select('chat_id', 'name', 'owner')->where('corpid', $corpid)
  1793. ->whereIn('chat_id', $chatIdList)->get();
  1794. # 拉取客户人数
  1795. $userLimit = 40;
  1796. foreach ($configList as $config) {
  1797. $groupInfo = $chatGroupData->where('chat_id', $config->chat_id)->first();
  1798. $config->groupName = $groupInfo->name;
  1799. $config->owner = $groupInfo->owner;
  1800. $config->announcement = $announcement;
  1801. $userLimit = min($userLimit, $config->user_limit);
  1802. }
  1803. $limit = $upperLimit * ($userLimit-1);
  1804. if(is_numeric($customerNumber)) {
  1805. $limit = $customerNumber;
  1806. }
  1807. $customerList = self::getInviteCustomerList($inviteConfig, $filterType, $deviceId, $joinType, $datetimeNow
  1808. , $corpid, $addTimeLater, $userId, $ruleId, $continuouslyAttractGroups, $cursor, $limit);
  1809. if(empty($customerList) || $customerList->isEmpty()) {
  1810. Log::logError('该企业成员无新添加的客户', [
  1811. 'device_id' => $deviceId, 'corpid' => $corpid,
  1812. 'userId' => $userId, 'begin_time' => $beginTimestamps,
  1813. 'end_time' => $endTimestamps, 'invite_config' => $inviteConfig,
  1814. 'filter_type' => $filterType, 'join_type' => $joinType, 'datetimenow' => $datetimeNow,
  1815. 'add_time_later' => $addTimeLater, 'rule_id' => $ruleId, 'continuouslyAttractGroups'=>$continuouslyAttractGroups,
  1816. 'cursor' =>$cursor, 'limit' => $limit
  1817. ], 'WaitInvite');
  1818. continue;
  1819. }
  1820. $corpInfo = $corpList->where('corpid', $corpid)->first();
  1821. $corpName = $corpInfo->corp_name ?? null;
  1822. if(is_null($corpName)) {
  1823. Log::logError('企微名称获取为空', [
  1824. 'device_id' => $deviceId,
  1825. 'corpid' => $corpid,
  1826. 'userId' => $userId,
  1827. ], 'WaitInvite');
  1828. continue;
  1829. }
  1830. # 持续拉取客户入群任务更新游标
  1831. if(1 == $continuouslyAttractGroups) {
  1832. $newCursor = $customerList->max('id');
  1833. InviteChatGroupRule::updateCursor($ruleId, $newCursor);
  1834. }
  1835. $data[] = [
  1836. 'companyName' => $corpName,
  1837. 'corpid' => $corpid,
  1838. 'joinType' => $joinType,
  1839. 'owner_type' => $ownerType,
  1840. 'groupBeans' => $configList,
  1841. 'peopleList' => $customerList,
  1842. 'upper_limit' => $upperLimit,
  1843. 'retry' => $retry
  1844. ];
  1845. }
  1846. return $data;
  1847. }
  1848. public static function getInviteCustomerList(
  1849. $inviteConfig, $filterType, $deviceId, $joinType, $datetimeNow, $corpid, $addTimeLater, $userId, $ruleId,
  1850. $continuouslyAttractGroups, $cursor, $limit
  1851. )
  1852. {
  1853. $search = [];
  1854. if($inviteConfig->customer_filter == 1) {
  1855. # 客户添加时间范围
  1856. if($filterType == 1) {
  1857. # 根据筛选条件重新定义$beginTimestamps和$endTimestamps
  1858. $endTimestamps = strtotime($datetimeNow);
  1859. $beginTimestamps = strtotime('- ' . $inviteConfig->add_time_later . ' hours', $endTimestamps);
  1860. } elseif ($filterType == 2) {
  1861. $endTimestamps = strtotime($inviteConfig->add_time_et);
  1862. $beginTimestamps = strtotime($inviteConfig->add_time_st);
  1863. }
  1864. # 性别
  1865. $gender = (!is_null($inviteConfig->gender) && $inviteConfig->gender != '') ? explode(',', $inviteConfig->gender) : [];
  1866. if(!empty($gender)) $search['gender'] = $gender;
  1867. # 标签
  1868. $tagScreenType = $inviteConfig->tag_screen_type ?? null;
  1869. $tagList = $inviteConfig->tag_list ?? null;
  1870. if($tagScreenType == 1 && empty($tagList)) {
  1871. Log::logError('筛选条件异常 - 选择标签为空', [
  1872. 'device_id' => $deviceId, 'join_type' => $joinType, 'gender' => $inviteConfig->gender,
  1873. 'corpid' => $corpid, 'add_time_later' => $addTimeLater, 'tag_list' => $inviteConfig->tag_list,
  1874. 'filter_type' => $filterType, 'add_time_st' => $inviteConfig->add_time_st,
  1875. 'add_time_et' => $inviteConfig->add_time_et, 'tag_screen_type' => $inviteConfig->tag_screen_type,
  1876. 'userId' => $userId, 'rule_id' => $ruleId,
  1877. ], 'WaitInvite');
  1878. return [];
  1879. }
  1880. if($tagScreenType) {
  1881. $search['tag_screen_type'] = $tagScreenType;
  1882. $search['tag_list'] = explode(',', $tagList);
  1883. }
  1884. // 付费情况
  1885. $payStatus = $inviteConfig->pay_status;
  1886. $payNumMin = $inviteConfig->pay_num_min;
  1887. $payNumMax = $inviteConfig->pay_num_max;
  1888. if (is_numeric($payStatus)) {
  1889. $search['pay_status'] = $payStatus;
  1890. }
  1891. if (is_numeric($payNumMin) && $payNumMin > 0) {
  1892. $search['pay_num_min'] = $payNumMin;
  1893. }
  1894. if (is_numeric($payNumMax) && $payNumMax > 0) {
  1895. $search['pay_num_max'] = $payNumMax;
  1896. }
  1897. }
  1898. # 获取新加入的客户信息
  1899. $customerListQuery = CustomerDetails::suffix($corpid)->where('corpid', $corpid)
  1900. ->select('external_userid', 'name', 'remark', 'id')
  1901. ->where('user_id', $userId)
  1902. ->where('loss_status', 1)
  1903. ->where('blacklist_status', 0);
  1904. if(!empty($beginTimestamps)) {
  1905. $customerListQuery->where('createtime', '>', $beginTimestamps);
  1906. }
  1907. if(!empty($endTimestamps)) {
  1908. $customerListQuery->where('createtime', '<=', $endTimestamps);
  1909. }
  1910. # 按性别搜索
  1911. if(isset($search['gender'])) {
  1912. $customerListQuery->whereIn('gender', $search['gender']);
  1913. }
  1914. # 按标签搜索
  1915. if(isset($search['tag_screen_type'])) {
  1916. $tagList = $search['tag_list'] ?? [];
  1917. $tagRaw = '';
  1918. switch($search['tag_screen_type']) {
  1919. case 1: // 所选标签满足其一
  1920. foreach($tagList as $tagId) {
  1921. $tagRaw .= $tagId. ' ';
  1922. }
  1923. if(isset($search['exclude_tag_list']) && !empty($search['exclude_tag_list'])) {
  1924. # 增加屏蔽标签
  1925. $tagRaw = CustomerDetails::excludeTagSqlDeal($search['exclude_tag_list'], $tagRaw);
  1926. }
  1927. if(!empty($tagRaw))
  1928. $customerListQuery->whereRaw('match(`tag_list`) against ("'.$tagRaw.'" in boolean mode)');
  1929. break;
  1930. case 2: // 所选标签同时满足
  1931. foreach($tagList as $tagId) {
  1932. $tagRaw .= '+' .$tagId. ' ';
  1933. }
  1934. if(isset($search['exclude_tag_list']) && !empty($search['exclude_tag_list'])) {
  1935. # 增加屏蔽标签
  1936. $tagRaw = CustomerDetails::excludeTagSqlDeal($search['exclude_tag_list'], $tagRaw);
  1937. }
  1938. if(!empty($tagRaw))
  1939. $customerListQuery->whereRaw('match(`tag_list`) against ("'.$tagRaw.'" in boolean mode)');
  1940. break;
  1941. case 3:
  1942. $customerListQuery->whereNull('tag_list');
  1943. break;
  1944. }
  1945. }
  1946. # 按付费状态
  1947. if(isset($search['pay_status'])) {
  1948. if($search['pay_status']) {
  1949. $customerListQuery->where('is_paid', 1);
  1950. } else {
  1951. $customerListQuery->where('is_paid', 0);
  1952. }
  1953. }
  1954. # 最小付费次数
  1955. if(isset($search['pay_num_min'])) {
  1956. $customerListQuery->where('pay_num', '>=', $search['pay_num_min']);
  1957. }
  1958. # 最大付费次数
  1959. if(isset($search['pay_num_max'])) {
  1960. $customerListQuery->where('pay_num', '<=', $search['pay_num_max']);
  1961. }
  1962. if(1 == $continuouslyAttractGroups) {
  1963. $customerList = $customerListQuery->where('id', '>', $cursor)->limit($limit)->orderby('createtime')->get();
  1964. } else {
  1965. $customerList = $customerListQuery->orderby('createtime')->get();
  1966. }
  1967. return $customerList;
  1968. }
  1969. /**
  1970. * 获取待邀请入群的新用户
  1971. * */
  1972. public static function waitShare($deviceId, $beginTime, $endTime)
  1973. {
  1974. $endTimestamps = strtotime($endTime);
  1975. $beginTimestamps = strtotime($beginTime);
  1976. # 查询设备对应的企微及客服信息
  1977. $userList = AndroidBindCorp::select('corpid', 'user_id')->where('device_id', $deviceId)
  1978. ->where('enable', 1)->get();
  1979. if(empty($userList)) {
  1980. Log::logError('此设备号未绑定任何企微信息', [
  1981. 'device_id' => $deviceId
  1982. ], 'waitShare');
  1983. return [];
  1984. }
  1985. # 查询支持无障碍模式的企微列表
  1986. $corpIds = $userList->pluck('corpid');
  1987. $corpList = AuthorizeCorp::select('corpid', 'corp_name')->whereIn('corpid', $corpIds)->where('enable', 1)->get();
  1988. $data = [];
  1989. # 查询企微和客服待共享的客户列表
  1990. foreach ($userList as $userInfo) {
  1991. $corpid = $userInfo->corpid ?? null;
  1992. $userId = $userInfo->user_id ?? null;
  1993. if(!$corpid || !$userId) {
  1994. Log::logError('检索待共享好友的客服时存在异常数据', [
  1995. 'user_info' => $userInfo->toArray(),
  1996. 'device_id' => $deviceId
  1997. ], 'waitShare');
  1998. continue;
  1999. }
  2000. # 检索共享好友配置
  2001. $takeoverUserIdList = CustomerShareRelation::where('status', 1)->where('enable', 1)->where('corpid', $corpid)
  2002. ->where('user_id', $userId)->value('takeover_users');
  2003. if(empty($takeoverUserIdList)) {
  2004. Log::logError('未检索到共享好友配置', [
  2005. 'corpid' => $corpid,
  2006. 'user_id' => $userId,
  2007. 'device_id' => $deviceId
  2008. ], 'waitShare');
  2009. continue;
  2010. }
  2011. $takeoverUserIds = explode(',', $takeoverUserIdList);
  2012. $takeoverUserList = DjUser::where('corpid', $corpid)->whereIn('user_id', $takeoverUserIds)->where('enable', 1)
  2013. ->where('status', 1)->pluck('name');
  2014. # 获取新加入的客户信息
  2015. $customerList = CustomerDetails::suffix($corpid)->where('corpid', $corpid)
  2016. ->select('name')
  2017. ->where('user_id', $userId)->where('createtime', '>', $beginTimestamps)
  2018. ->where('createtime', '<=', $endTimestamps)
  2019. ->where('loss_status', 1)
  2020. ->get()->pluck('name');
  2021. if($customerList->isEmpty()) {
  2022. Log::logError('该企业成员无新添加的客户', [
  2023. 'device_id' => $deviceId, 'corpid' => $corpid,
  2024. 'userId' => $userId, 'begin_time' => $beginTimestamps,
  2025. 'end_time' => $endTimestamps, ''
  2026. ], 'waitShare');
  2027. continue;
  2028. }
  2029. $corpInfo = $corpList->where('corpid', $corpid)->first();
  2030. $corpName = $corpInfo->corp_name ?? null;
  2031. if(is_null($corpName)) {
  2032. Log::logError('企微名称获取为空', [
  2033. 'device_id' => $deviceId,
  2034. 'corpid' => $corpid,
  2035. 'userId' => $userId,
  2036. ], 'waitShare');
  2037. continue;
  2038. }
  2039. $data[] = [
  2040. 'companyName' => $corpName,
  2041. 'colleagueNames' => $takeoverUserList,
  2042. 'clientNames' => $customerList
  2043. ];
  2044. }
  2045. return $data;
  2046. }
  2047. public static function waitSupplementaryInvite($deviceId, $beginTime, $endTime)
  2048. {
  2049. # 校验开始时间和结束时间,最大时间差不超过十分钟
  2050. $endTimestamps = strtotime($endTime);
  2051. $beginTimestamps = strtotime($beginTime);
  2052. if($endTimestamps - $beginTimestamps > 600) {
  2053. $beginTimestamps = strtotime('-10 minute', $endTimestamps);
  2054. }
  2055. # 查询设备对应的企微及客服信息
  2056. $userList = AndroidBindCorp::select('corpid', 'user_id')->where('device_id', $deviceId)
  2057. ->where('enable', 1)->get();
  2058. if(empty($userList)) {
  2059. Log::logError('此设备号未绑定任何企微信息', [
  2060. 'device_id' => $deviceId
  2061. ], 'waitSupplementaryInvite');
  2062. return [];
  2063. }
  2064. # 查询支持无障碍模式的企微列表
  2065. $corpIds = $userList->pluck('corpid');
  2066. $corpList = AuthorizeCorp::select('corpid', 'corp_name')->whereIn('corpid', $corpIds)->where('enable', 1)->get();
  2067. $data = [];
  2068. # 查询企微和客服是否配置了邀请入群功能
  2069. foreach ($userList as $userInfo) {
  2070. $corpid = $userInfo->corpid ?? null;
  2071. $userId = $userInfo->user_id ?? null;
  2072. if(!$corpid || !$userId) {
  2073. Log::logError('检索待邀请入群的客服时存在异常数据', [
  2074. 'user_info' => $userInfo->toArray(),
  2075. 'device_id' => $deviceId
  2076. ], 'waitSupplementaryInvite');
  2077. continue;
  2078. }
  2079. # 使用更新时间最近的配置
  2080. $inviteConfig = InviteChatGroupRule::where('corpid', $corpid)
  2081. ->whereRaw("FIND_IN_SET('".$userId."', `users`)")
  2082. ->where('status', 1)->where('enable', 1)->where('supplementary_invite', 1)
  2083. ->orderBy('updated_at', 'desc')
  2084. ->first();
  2085. if(empty($inviteConfig)) {
  2086. $inviteConfig = InviteChatGroupRule::where('corpid', $corpid)
  2087. ->where('is_for_all', 1)->where('supplementary_invite', 1)
  2088. ->where('enable', 1)->where('status', 1)
  2089. ->orderBy('updated_at', 'desc')
  2090. ->first();
  2091. }
  2092. if(empty($inviteConfig)) {
  2093. Log::logError('客服暂未配置邀请入群规则', [
  2094. 'user_info' => $userInfo->toArray(),
  2095. 'device_id' => $deviceId
  2096. ], 'waitSupplementaryInvite');
  2097. continue;
  2098. }
  2099. $datetimeNow = date('Y-m-d H:i:s');
  2100. if($inviteConfig->invite_type == 2 && strtotime($inviteConfig->next_supplementary_invite_time) > time()) {
  2101. Log::logError('对应客服尚未到定时补充邀请入群时间', [
  2102. 'user_info' => $userInfo->toArray(),
  2103. 'device_id' => $deviceId,
  2104. 'config_info' => $inviteConfig->toArray()
  2105. ], 'waitSupplementaryInvite');
  2106. continue;
  2107. }
  2108. $lastSupplementaryInviteTime = $inviteConfig->last_supplementary_invite_time;
  2109. # 满足邀请入群时间
  2110. # 更新下次邀请入群时间
  2111. if($inviteConfig->invite_type == 2) { // 定时邀请
  2112. $nextSupplementaryInviteTime = CircleMassMsgService::getNextSupplementaryInviteTime(1
  2113. , $inviteConfig->invite_time, $inviteConfig->supplementary_invite_cycle, $datetimeNow);
  2114. $result = InviteChatGroupRule::where('id', $inviteConfig->id)->update([
  2115. 'next_supplementary_invite_time' => $nextSupplementaryInviteTime,
  2116. 'last_supplementary_invite_time' => $datetimeNow
  2117. ]);
  2118. if(!$result) {
  2119. Log::logError('更新下次邀请时间失败', [
  2120. 'user_info' => $userInfo->toArray(),
  2121. 'device_id' => $deviceId,
  2122. 'config_info' => $inviteConfig->toArray(),
  2123. 'last_supplementary_invite_time' => $lastSupplementaryInviteTime,
  2124. 'next_supplementary_invite_time' => $nextSupplementaryInviteTime,
  2125. 'now_supplementary_invite_time' => $datetimeNow
  2126. ], 'waitSupplementaryInvite');
  2127. }
  2128. }
  2129. # 规则ID
  2130. $ruleId = $inviteConfig->id;
  2131. # 建群类型 1当前客户直接建群 2其他客服建群
  2132. $ownerType = in_array($inviteConfig->owner_type, [1, 2]) ? $inviteConfig->owner_type : 1;
  2133. # 获取邀请入群配置
  2134. $configList = InviteChatGroupDetail::select('corpid', 'chat_id', 'sort', 'user_limit')
  2135. ->where('enable', 1)->where('status', 1)->where('rule_id', $ruleId)
  2136. ->orderBy('sort')->get();
  2137. if(empty($configList)) {
  2138. Log::logError('该企业成员无可用的群配置', [
  2139. 'device_id' => $deviceId, 'add_time_et' => $datetimeNow,
  2140. 'corpid' => $corpid, 'add_time_st' => $lastSupplementaryInviteTime,
  2141. 'userId' => $userId, 'rule_id' => $ruleId,
  2142. ], 'waitSupplementaryInvite');
  2143. continue;
  2144. }
  2145. # 获取群信息
  2146. $chatIdList = $configList->pluck('chat_id');
  2147. $chatGroupData = ChatGroup::select('chat_id', 'name', 'owner')->where('corpid', $corpid)
  2148. ->whereIn('chat_id', $chatIdList)->get();
  2149. $userList = DjUser::getUserBySearch([
  2150. 'corp_list' => [$corpid]
  2151. ]);
  2152. # 拉取客户人数
  2153. foreach ($configList as $config) {
  2154. $groupInfo = $chatGroupData->where('chat_id', $config->chat_id)->first();
  2155. $config->groupName = $groupInfo->name ?? '';
  2156. $config->owner = $groupInfo->owner ?? '';
  2157. $ownerInfo = $userList->where('user_id', $config->owner)->first();
  2158. $config->owner_name = $ownerInfo->name ?? '';
  2159. # 找到所有群主为当前客服的模板群聊
  2160. $chatGroupModel = ChatGroup::getSearchModel([
  2161. 'status' => 1, 'corpid' => $corpid, 'owner' => $config->owner, 'name' => $config->groupName
  2162. ]);
  2163. $userChatList = $chatGroupModel->select(['chat_id'])->get();
  2164. if($userChatList->isEmpty()) {
  2165. Log::logError('该群模板无法找到当前客服所属的群聊', [
  2166. 'device_id' => $deviceId, 'corpid' => $corpid, 'user_id' => $userId,
  2167. 'name' => $config->groupName, 'rule_id' => $ruleId, 'invite_config' => $inviteConfig,
  2168. 'config' => $config
  2169. ], 'waitSupplementaryInvite');
  2170. $config->people_list = [];
  2171. continue;
  2172. }
  2173. $userChatIdList = array_column($userChatList->toArray(), 'chat_id');
  2174. Log::logInfo('根据群模板获取到的群聊结果', [
  2175. 'device_id' => $deviceId, 'corpid' => $corpid, 'user_id' => $userId, 'owner' => $config->owner,
  2176. 'name' => $config->groupName, 'rule_id' => $ruleId, 'invite_config' => $inviteConfig,
  2177. 'config' => $config, 'user_chat_id_list' => $userChatIdList
  2178. ], 'waitSupplementaryInvite');
  2179. if(2 == $ownerType) {# 群主与进粉客服不是同一人,再次筛选包含进粉客服的群聊ID
  2180. $newChatList = ChatGroupMember::getGroupMember([
  2181. 'corpid' => $corpid, 'chat_id_list' => $userChatIdList, 'user_id' => $userId, 'status' => 1
  2182. ]);
  2183. if(empty($newChatList)) {
  2184. Log::logError('查找进粉客服在群聊中的群聊信息', [
  2185. 'device_id' => $deviceId, 'corpid' => $corpid, 'user_id' => $userId, 'owner' => $config->owner,
  2186. 'name' => $config->groupName, 'rule_id' => $ruleId, 'invite_config' => $inviteConfig,
  2187. 'config' => $config, 'user_chat_id_list' => $userChatIdList, 'new_chat_list'=> $newChatList
  2188. ], 'waitSupplementaryInvite');
  2189. $config->people_list = [];
  2190. continue;
  2191. }
  2192. $newUserChatIdList = array_unique(array_column($newChatList, 'chat_id'));
  2193. Log::logInfo('查找进粉客服在群聊中的群聊信息结果', [
  2194. 'device_id' => $deviceId, 'corpid' => $corpid, 'user_id' => $userId, 'owner' => $config->owner,
  2195. 'name' => $config->groupName, 'rule_id' => $ruleId, 'invite_config' => $inviteConfig,
  2196. 'config' => $config, 'user_chat_id_list' => $userChatIdList, 'new_user_chat_id_list' => $newUserChatIdList
  2197. ], 'waitSupplementaryInvite');
  2198. $userChatIdList = $newUserChatIdList;
  2199. }
  2200. # 找到这些群聊在间隔时间范围内退群的客户外部联系人ID
  2201. $externalUserList = ChatGroupMember::getGroupMember([
  2202. 'corpid' => $corpid, 'chat_id_list' => $userChatIdList, 'status' => 2,
  2203. 'quit_time_st' => $lastSupplementaryInviteTime, 'quit_time_ed' => $datetimeNow
  2204. ]);
  2205. if(empty($externalUserList)) {
  2206. Log::logError('退群客户为空', [
  2207. 'device_id' => $deviceId, 'corpid' => $corpid, 'user_id' => $userId,
  2208. 'name' => $config->groupName, 'rule_id' => $ruleId, 'invite_config' => $inviteConfig,
  2209. 'config' => $config, 'chat_id_list' => $userChatIdList, 'quit_time_st' => $lastSupplementaryInviteTime,
  2210. 'quit_time_ed' => $datetimeNow
  2211. ], 'waitSupplementaryInvite');
  2212. $config->people_list = [];
  2213. continue;
  2214. }
  2215. # 通过外部联系人ID查询客户信息
  2216. $externalUserIdList = array_unique(array_column($externalUserList, 'user_id'));
  2217. Log::logInfo('客户ID列表', $externalUserIdList, 'waitSupplementaryInvite');
  2218. $customerList = CustomerDetails::suffix($corpid)->select(['external_userid', 'name', 'remark', 'id'])
  2219. ->where('corpid', $corpid)->whereIn('external_userid', $externalUserIdList)
  2220. ->where('loss_status', 1)->where('blacklist_status', 0)->groupBy('external_userid')->orderBy('id', 'desc')->get();
  2221. if($customerList->isEmpty()) {
  2222. Log::logError('查询客户列表结果为空', [
  2223. 'device_id' => $deviceId, 'corpid' => $corpid, 'user_id' => $userId,
  2224. 'name' => $config->groupName, 'rule_id' => $ruleId, 'invite_config' => $inviteConfig,
  2225. 'config' => $config, "external_userid_list" => $externalUserIdList
  2226. ], 'waitSupplementaryInvite');
  2227. continue;
  2228. }
  2229. $config->people_list = $customerList;
  2230. }
  2231. $corpInfo = $corpList->where('corpid', $corpid)->first();
  2232. $corpName = $corpInfo->corp_name ?? null;
  2233. if(is_null($corpName)) {
  2234. Log::logError('企微名称获取为空', [
  2235. 'device_id' => $deviceId,
  2236. 'corpid' => $corpid,
  2237. 'userId' => $userId,
  2238. ], 'waitSupplementaryInvite');
  2239. continue;
  2240. }
  2241. $data[] = [
  2242. 'companyName' => $corpName,
  2243. 'corpid' => $corpid,
  2244. 'owner_type' => $ownerType,
  2245. 'groupBeans' => $configList,
  2246. ];
  2247. }
  2248. return $data;
  2249. }
  2250. public static function waitInviteTargetGroup($deviceId, $beginTime, $endTime)
  2251. {
  2252. # 查询设备对应的企微及客服信息
  2253. $userList = AndroidBindCorp::select('corpid', 'user_id')->where('device_id', $deviceId)
  2254. ->where('enable', 1)->get();
  2255. if(empty($userList)) {
  2256. Log::logError('此设备号未绑定任何企微信息', [
  2257. 'device_id' => $deviceId
  2258. ], 'waitInviteTargetGroup');
  2259. return [];
  2260. }
  2261. # 查询支持无障碍模式的企微列表
  2262. $corpIds = $userList->pluck('corpid');
  2263. $corpList = AuthorizeCorp::select('corpid', 'corp_name')->whereIn('corpid', $corpIds)->where('enable', 1)->get();
  2264. $data = [];
  2265. # 查询企微和客服是否配置了邀请入群功能
  2266. foreach ($userList as $userInfo) {
  2267. $corpid = $userInfo->corpid ?? null;
  2268. $userId = $userInfo->user_id ?? null;
  2269. if(!$corpid || !$userId) {
  2270. Log::logError('检索待邀请入群的客服时存在异常数据', [
  2271. 'user_info' => $userInfo->toArray(),
  2272. 'device_id' => $deviceId
  2273. ], 'waitInviteTargetGroup');
  2274. continue;
  2275. }
  2276. # 使用更新时间最近的配置
  2277. $inviteConfig = InviteChatGroupRule::where('corpid', $corpid)
  2278. ->whereRaw("FIND_IN_SET('".$userId."', `users`)")
  2279. ->where('status', 1)->where('enable', 1)->where('supplementary_invite', 1)
  2280. ->orderBy('updated_at', 'desc')
  2281. ->first();
  2282. if(empty($inviteConfig)) {
  2283. $inviteConfig = InviteChatGroupRule::where('corpid', $corpid)
  2284. ->where('is_for_all', 1)->where('supplementary_invite', 1)
  2285. ->where('enable', 1)->where('status', 1)
  2286. ->orderBy('updated_at', 'desc')
  2287. ->first();
  2288. }
  2289. if(empty($inviteConfig)) {
  2290. Log::logError('客服暂未配置邀请入群规则', [
  2291. 'user_info' => $userInfo->toArray(),
  2292. 'device_id' => $deviceId
  2293. ], 'waitInviteTargetGroup');
  2294. continue;
  2295. }
  2296. $datetimeNow = date('Y-m-d H:i:s');
  2297. if($inviteConfig->invite_type == 2 && strtotime($inviteConfig->next_supplementary_invite_time) > time()) {
  2298. Log::logError('对应客服尚未到定时补充邀请入群时间', [
  2299. 'user_info' => $userInfo->toArray(),
  2300. 'device_id' => $deviceId,
  2301. 'config_info' => $inviteConfig->toArray()
  2302. ], 'waitInviteTargetGroup');
  2303. continue;
  2304. }
  2305. $lastSupplementaryInviteTime = $inviteConfig->last_supplementary_invite_time;
  2306. # 满足邀请入群时间,更新下次邀请入群时间
  2307. if($inviteConfig->invite_type == 2) { // 定时邀请
  2308. $nextSupplementaryInviteTime = CircleMassMsgService::getNextSupplementaryInviteTime(1
  2309. , $inviteConfig->invite_time, $inviteConfig->supplementary_invite_cycle, $datetimeNow);
  2310. $result = InviteChatGroupRule::where('id', $inviteConfig->id)->update([
  2311. 'next_supplementary_invite_time' => $nextSupplementaryInviteTime,
  2312. 'last_supplementary_invite_time' => $datetimeNow
  2313. ]);
  2314. if(!$result) {
  2315. Log::logError('更新下次邀请时间失败', [
  2316. 'user_info' => $userInfo->toArray(),
  2317. 'device_id' => $deviceId,
  2318. 'config_info' => $inviteConfig->toArray(),
  2319. 'last_supplementary_invite_time' => $lastSupplementaryInviteTime,
  2320. 'next_supplementary_invite_time' => $nextSupplementaryInviteTime,
  2321. 'now_supplementary_invite_time' => $datetimeNow
  2322. ], 'waitInviteTargetGroup');
  2323. }
  2324. }
  2325. # 规则ID
  2326. $ruleId = $inviteConfig->id;
  2327. # 建群类型 1当前客户直接建群 2其他客服建群
  2328. $ownerType = in_array($inviteConfig->owner_type, [1, 2]) ? $inviteConfig->owner_type : 1;
  2329. # 获取邀请入群配置
  2330. $configList = InviteChatGroupDetail::select('corpid', 'chat_id', 'sort', 'user_limit')
  2331. ->where('enable', 1)->where('status', 1)->where('rule_id', $ruleId)
  2332. ->orderBy('sort')->get();
  2333. if(empty($configList)) {
  2334. Log::logError('该企业成员无可用的群配置', [
  2335. 'device_id' => $deviceId, 'add_time_et' => $datetimeNow,
  2336. 'corpid' => $corpid, 'add_time_st' => $lastSupplementaryInviteTime,
  2337. 'userId' => $userId, 'rule_id' => $ruleId,
  2338. ], 'waitInviteTargetGroup');
  2339. continue;
  2340. }
  2341. # 获取群信息
  2342. $chatIdList = $configList->pluck('chat_id');
  2343. $chatGroupData = ChatGroup::select('chat_id', 'name', 'owner')->where('corpid', $corpid)
  2344. ->whereIn('chat_id', $chatIdList)->get();
  2345. $userList = DjUser::getUserBySearch([
  2346. 'corp_list' => [$corpid]
  2347. ]);
  2348. # 拉取客户人数
  2349. foreach ($configList as $config) {
  2350. $groupInfo = $chatGroupData->where('chat_id', $config->chat_id)->first();
  2351. $config->groupName = $groupInfo->name ?? '';
  2352. $config->owner = $groupInfo->owner ?? '';
  2353. $ownerInfo = $userList->where('user_id', $config->owner)->first();
  2354. $config->owner_name = $ownerInfo->name ?? '';
  2355. # 找到所有群主为当前客服的模板群聊
  2356. $chatGroupModel = ChatGroup::getSearchModel([
  2357. 'status' => 1, 'corpid' => $corpid, 'owner' => $config->owner, 'name' => $config->groupName
  2358. ]);
  2359. $userChatList = $chatGroupModel->select(['chat_id'])->get();
  2360. if($userChatList->isEmpty()) {
  2361. Log::logError('该群模板无法找到当前客服所属的群聊', [
  2362. 'device_id' => $deviceId, 'corpid' => $corpid, 'user_id' => $userId,
  2363. 'name' => $config->groupName, 'rule_id' => $ruleId, 'invite_config' => $inviteConfig,
  2364. 'config' => $config
  2365. ], 'waitInviteTargetGroup');
  2366. $config->people_list = [];
  2367. continue;
  2368. }
  2369. $userChatIdList = array_column($userChatList->toArray(), 'chat_id');
  2370. Log::logInfo('根据群模板获取到的群聊结果', [
  2371. 'device_id' => $deviceId, 'corpid' => $corpid, 'user_id' => $userId, 'owner' => $config->owner,
  2372. 'name' => $config->groupName, 'rule_id' => $ruleId, 'invite_config' => $inviteConfig,
  2373. 'config' => $config, 'user_chat_id_list' => $userChatIdList
  2374. ], 'waitInviteTargetGroup');
  2375. if(2 == $ownerType) { // 群主与进粉客服不是同一人,再次筛选包含进粉客服的群聊ID
  2376. $newChatList = ChatGroupMember::getGroupMember([
  2377. 'corpid' => $corpid, 'chat_id_list' => $userChatIdList, 'user_id' => $userId, 'status' => 1
  2378. ]);
  2379. if(empty($newChatList)) {
  2380. Log::logError('查找进粉客服在群聊中的群聊信息', [
  2381. 'device_id' => $deviceId, 'corpid' => $corpid, 'user_id' => $userId, 'owner' => $config->owner,
  2382. 'name' => $config->groupName, 'rule_id' => $ruleId, 'invite_config' => $inviteConfig,
  2383. 'config' => $config, 'user_chat_id_list' => $userChatIdList, 'new_chat_list'=> $newChatList
  2384. ], 'waitInviteTargetGroup');
  2385. $config->people_list = [];
  2386. continue;
  2387. }
  2388. $newUserChatIdList = array_unique(array_column($newChatList, 'chat_id'));
  2389. Log::logInfo('查找进粉客服在群聊中的群聊信息结果', [
  2390. 'device_id' => $deviceId, 'corpid' => $corpid, 'user_id' => $userId, 'owner' => $config->owner,
  2391. 'name' => $config->groupName, 'rule_id' => $ruleId, 'invite_config' => $inviteConfig,
  2392. 'config' => $config, 'user_chat_id_list' => $userChatIdList, 'new_user_chat_id_list' => $newUserChatIdList
  2393. ], 'waitInviteTargetGroup');
  2394. $userChatIdList = $newUserChatIdList;
  2395. }
  2396. $inviteList = [];
  2397. foreach ($userChatIdList as $userChatId) {
  2398. # 查找对应群在指定时间的流失客户
  2399. $externalUserList = ChatGroupMember::getGroupMember([
  2400. 'corpid' => $corpid, 'chat_id' => $userChatId, 'status' => 2,
  2401. 'quit_time_st' => $lastSupplementaryInviteTime, 'quit_time_ed' => $datetimeNow
  2402. ]);
  2403. if(empty($externalUserList)) {
  2404. Log::logError('退群客户为空', [
  2405. 'device_id' => $deviceId, 'corpid' => $corpid, 'user_id' => $userId,
  2406. 'name' => $config->groupName, 'rule_id' => $ruleId, 'invite_config' => $inviteConfig,
  2407. 'config' => $config, 'chat_id' => $userChatId, 'quit_time_st' => $lastSupplementaryInviteTime,
  2408. 'quit_time_ed' => $datetimeNow
  2409. ], 'waitInviteTargetGroup');
  2410. continue;
  2411. }
  2412. # 通过外部联系人ID查询客户信息
  2413. $externalUserIdList = array_unique(array_column($externalUserList, 'user_id'));
  2414. Log::logInfo('客户ID列表', $externalUserIdList, 'waitInviteTargetGroup');
  2415. $customerList = CustomerDetails::suffix($corpid)->select(['external_userid', 'name', 'remark', 'id'])
  2416. ->where('corpid', $corpid)->whereIn('external_userid', $externalUserIdList)
  2417. ->where('loss_status', 1)->where('blacklist_status', 0)->groupBy('external_userid')
  2418. ->orderBy('id', 'desc')
  2419. ->get();
  2420. if($customerList->isEmpty()) {
  2421. Log::logError('查询客户列表结果为空', [
  2422. 'device_id' => $deviceId, 'corpid' => $corpid, 'user_id' => $userId,
  2423. 'name' => $config->groupName, 'rule_id' => $ruleId, 'invite_config' => $inviteConfig,
  2424. 'config' => $config, "external_userid_list" => $externalUserIdList
  2425. ], 'waitInviteTargetGroup');
  2426. continue;
  2427. }
  2428. $inviteList[] = [
  2429. 'chat_id' => $userChatId,
  2430. 'group_name' => $config->groupName,
  2431. 'owner' => $config->owner,
  2432. 'owner_name' => $config->owner_name,
  2433. 'customer_list' => $customerList
  2434. ];
  2435. }
  2436. $config->invite_list = $inviteList;
  2437. }
  2438. $corpInfo = $corpList->where('corpid', $corpid)->first();
  2439. $corpName = $corpInfo->corp_name ?? null;
  2440. if(is_null($corpName)) {
  2441. Log::logError('企微名称获取为空', [
  2442. 'device_id' => $deviceId,
  2443. 'corpid' => $corpid,
  2444. 'userId' => $userId,
  2445. ], 'waitInviteTargetGroup');
  2446. continue;
  2447. }
  2448. $data[] = [
  2449. 'companyName' => $corpName,
  2450. 'corpid' => $corpid,
  2451. 'owner_type' => $ownerType,
  2452. 'groupBeans' => $configList,
  2453. ];
  2454. }
  2455. return $data;
  2456. }
  2457. public static function inviteNewUser($deviceId, $beginTime, $endTime)
  2458. {
  2459. # 校验开始时间和结束时间,最大时间差不超过十分钟
  2460. $endTimestamps = strtotime($endTime);
  2461. $beginTimestamps = strtotime($beginTime);
  2462. if($endTimestamps - $beginTimestamps > 600) {
  2463. $beginTimestamps = strtotime('-10 minute', $endTimestamps);
  2464. }
  2465. # 查询设备对应的企微及客服信息
  2466. $userList = AndroidBindCorp::select('corpid', 'user_id')->where('device_id', $deviceId)
  2467. ->where('enable', 1)->get();
  2468. if(empty($userList)) {
  2469. Log::logError('此设备号未绑定任何企微信息', [
  2470. 'device_id' => $deviceId
  2471. ], 'inviteNewUser');
  2472. return [];
  2473. }
  2474. # 查询支持无障碍模式的企微列表
  2475. $corpIds = $userList->pluck('corpid');
  2476. $corpList = AuthorizeCorp::select('corpid', 'corp_name')->whereIn('corpid', $corpIds)->where('enable', 1)->get();
  2477. $data = [];
  2478. # 查询企微和客服是否配置了新用户邀请入群功能
  2479. foreach ($userList as $userInfo) {
  2480. $corpid = $userInfo->corpid ?? null;
  2481. $userId = $userInfo->user_id ?? null;
  2482. if(!$corpid || !$userId) {
  2483. Log::logError('检索待邀请入群的客服时存在异常数据', [
  2484. 'user_info' => $userInfo->toArray(),
  2485. 'device_id' => $deviceId
  2486. ], 'inviteNewUser');
  2487. continue;
  2488. }
  2489. $ruleInfo = WelcomeMsgRelation::select('id', 'is_invite_user', 'group_name_prefix')->where('corpid', $corpid)
  2490. ->whereRaw("FIND_IN_SET('".$userId."', users)")->where('enable', 1)->where('is_for_all', 0)
  2491. ->where('is_del', 0)->orderBy('updated_at', 'desc')
  2492. ->first();
  2493. if(empty($ruleInfo)) { // 未查询到该员工的专属配置,则查询企业全体员工通用配置
  2494. $ruleInfo = WelcomeMsgRelation::select('id', 'is_invite_user', 'group_name_prefix')->where('corpid', $corpid)
  2495. ->where('enable', 1)->where('is_del', 0)
  2496. ->where('is_for_all', 1)->orderBy('updated_at', 'desc')
  2497. ->first();
  2498. }
  2499. if(empty($ruleInfo) || !$ruleInfo->is_invite_user || !$ruleInfo->group_name_prefix) {
  2500. Log::logError('客服未配置邀请新用户入群', [
  2501. 'user_info' => $userInfo->toArray(),
  2502. 'device_id' => $deviceId,
  2503. 'config' => $ruleInfo
  2504. ], 'inviteNewUser');
  2505. continue;
  2506. }
  2507. $search = array('is_invited' => 0, 'add_time_start' => $beginTimestamps, 'add_time_end' => $endTimestamps);
  2508. $field = 'id, name';
  2509. list($customerList, $count) = CustomerDetails::getUserInfo($corpid, $search, $field, 1, 200, []);
  2510. if(!$count) {
  2511. Log::logError('该企业成员无新添加的客户', [
  2512. 'device_id' => $deviceId, 'corpid' => $corpid, 'datetime_now' => date("Y-m-d H:i:s"),
  2513. 'userId' => $userId, 'begin_time' => $beginTimestamps,
  2514. 'end_time' => $endTimestamps, 'invite_config' => $ruleInfo,
  2515. ], 'inviteNewUser');
  2516. continue;
  2517. }
  2518. if($count == 200) {
  2519. Log::logError('待邀请入群用户数超出200', [
  2520. 'user_info' => $userInfo->toArray(),
  2521. 'device_id' => $deviceId,
  2522. 'config' => $ruleInfo
  2523. ], 'inviteNewUser');
  2524. }
  2525. # 更新该批次用户的is_invited信息
  2526. CustomerDetails::suffix($corpid)->whereIn('id', $customerList->pluck('id'))->update(['is_invited' => 1]);
  2527. $corpInfo = $corpList->where('corpid', $corpid)->first();
  2528. $corpName = $corpInfo->corp_name ?? null;
  2529. if(is_null($corpName)) {
  2530. Log::logError('企微名称获取为空', [
  2531. 'device_id' => $deviceId,
  2532. 'corpid' => $corpid,
  2533. 'userId' => $userId,
  2534. ], 'inviteNewUser');
  2535. continue;
  2536. }
  2537. $data[] = [
  2538. 'companyName' => $corpName,
  2539. 'corpid' => $corpid,
  2540. 'groupNamePrefix' => $ruleInfo->group_name_prefix,
  2541. 'peopleList' => $customerList->pluck('name'),
  2542. ];
  2543. }
  2544. return $data;
  2545. }
  2546. /**
  2547. * 获取客户首次关注时间
  2548. * */
  2549. public static function getCustomerFirstAddTime($name, $avatar, $createTime)
  2550. {
  2551. if(!$name && !$avatar) { // 客户头像和昵称都为空,异常情况
  2552. $datetimeNow = time();
  2553. return [$datetimeNow, $datetimeNow];
  2554. }
  2555. # 生成uni_customer_label
  2556. $uniCustomerLabel = md5($name . $avatar);
  2557. # 优先从dj_customer_unique中获取最早关注时间
  2558. $firstAddTimeData = CustomerUnique::getFirstAddTime($uniCustomerLabel);
  2559. $firstAddTime = $firstAddTimeData->first_add_time ?? null;
  2560. $firstAddTimeNoLoss = $firstAddTimeData->first_add_time_no_loss ?? null;
  2561. if($firstAddTime && $firstAddTimeNoLoss) return [strtotime($firstAddTime), strtotime($firstAddTimeNoLoss)];
  2562. # 遍历50个客户详情表,找到最早关注时间
  2563. $firstAddTime = $firstAddTimeNoLoss = $createTime;
  2564. for ($i=0; $i<50; $i++) {
  2565. $minCreateTime = CustomerDetails::suffix($i)->where('uni_customer_label', $uniCustomerLabel)
  2566. ->min('createtime');
  2567. $minCreateTimeNoLoss = CustomerDetails::suffix($i)->where('uni_customer_label', $uniCustomerLabel)
  2568. ->where('enable', 1)->where('loss_status', 1)
  2569. ->min('createtime');
  2570. if($minCreateTime && $minCreateTime < $firstAddTime)
  2571. $firstAddTime = $minCreateTime;
  2572. if($minCreateTimeNoLoss && $minCreateTimeNoLoss < $firstAddTimeNoLoss)
  2573. $firstAddTimeNoLoss = $minCreateTimeNoLoss;
  2574. }
  2575. # 记录并返回首次关注时间
  2576. CustomerUnique::updateOrCreate(['uni_customer_label' => $uniCustomerLabel], [
  2577. 'name' => $name, 'avatar' => $avatar, 'first_add_time' => date('Y-m-d H:i:s', $firstAddTime),
  2578. 'first_add_time_no_loss' => date('Y-m-d H:i:s', $firstAddTimeNoLoss), 'enable' => 1
  2579. ]);
  2580. return [$firstAddTime, $firstAddTimeNoLoss];
  2581. }
  2582. /**
  2583. * 更新客户首次关注时间
  2584. * */
  2585. public static function updateCustomerFirstAddTime($name, $avatar, $createtime)
  2586. {
  2587. if(!$name && !$avatar) { // 客户头像和昵称都为空,异常情况
  2588. EmailQueue::rPush(
  2589. '更新客户首次关注时间时,用户头像和昵称异常',
  2590. json_encode(['name' => $name, 'avatar' => $avatar]),
  2591. ['xiaohua.hou@kuxuan-inc.com'], '猎羽'
  2592. );
  2593. return false;
  2594. }
  2595. # 生成uni_customer_label
  2596. $uniCustomerLabel = md5($name . $avatar);
  2597. # 优先从dj_customer_unique中获取最早关注时间
  2598. $firstAddTimeData = CustomerUnique::getFirstAddTime($uniCustomerLabel);
  2599. $firstAddTimeNoLoss = $firstAddTimeData->first_add_time_no_loss ?? null;
  2600. if($createtime == strtotime($firstAddTimeNoLoss)) { // 当前流失用户为留存用户中关注最早的
  2601. # 重新检索该用户在用户池中还留存的最早关注时间
  2602. $firstAddTimeNoLoss = null;
  2603. for ($i=0; $i<50; $i++) {
  2604. $minCreateTimeNoLoss = CustomerDetails::suffix($i)->where('uni_customer_label', $uniCustomerLabel)
  2605. ->where('enable', 1)->where('loss_status', 1)
  2606. ->min('createtime');
  2607. if($minCreateTimeNoLoss && (is_null($firstAddTimeNoLoss) || $minCreateTimeNoLoss <= $firstAddTimeNoLoss))
  2608. $firstAddTimeNoLoss = $minCreateTimeNoLoss;
  2609. }
  2610. if(is_null($firstAddTimeNoLoss)) {
  2611. CustomerUnique::where('uni_customer_label', $uniCustomerLabel)->update(['enable' => 0]);
  2612. } else {
  2613. CustomerUnique::where('uni_customer_label', $uniCustomerLabel)->update([
  2614. 'first_add_time_no_loss' => date('Y-m-d H:i:s', $firstAddTimeNoLoss
  2615. )]);
  2616. }
  2617. }
  2618. return true;
  2619. }
  2620. /**
  2621. * 校验客户是否在单主体内全部流失
  2622. * */
  2623. public static function checkCustomerLossStatusInCorp($corpid, $externalUserId)
  2624. {
  2625. # 判断客户是否有单主体内全部流失标识
  2626. $isExist = CustomerDetails::suffix($corpid)->where('corpid', $corpid)
  2627. ->where('external_userid', $externalUserId)
  2628. ->where('loss_status', 1)->where('can_receive', 1)
  2629. ->where('enable', 1)->exists();
  2630. if($isExist) {
  2631. $rst = CustomerDetails::suffix($corpid)->where('corpid', $corpid)
  2632. ->where('external_userid', $externalUserId)
  2633. ->where('retained_status', 0)
  2634. ->update(['retained_status'=>1]);
  2635. } else {
  2636. $rst = CustomerDetails::suffix($corpid)->where('corpid', $corpid)
  2637. ->where('retained_status', 1)
  2638. ->where('external_userid', $externalUserId)
  2639. ->update(['retained_status'=>0]);
  2640. }
  2641. Log::logInfo('本次起始ID为:', [
  2642. 'corpid' => $corpid, 'external_userid' => $externalUserId,
  2643. 'rst' => $rst, 'is_exist' => $isExist
  2644. ], 'CheckCustomerLossStatusInCorp');
  2645. }
  2646. public static function customerCountStat($search) {
  2647. $source = 1;
  2648. $customerQuery = CustomerDetails::getCustomerBySearchNew($search, $source);
  2649. $data = $customerQuery->selectRaw("user_id, count(1) as count")->groupBy('user_id')->orderByDesc('count')->get();
  2650. $insideUserIdList = array_unique(array_column($data->toArray(), 'user_id'));
  2651. // 查询客服基本信息
  2652. $search = ['corpid' => $search['corpid'], 'user_list' => $insideUserIdList];
  2653. $userDataList = DjUser::getUserBySearch($search);
  2654. $result = [];
  2655. foreach($data as $key => $item) {
  2656. $userInfo = $userDataList->where('user_id', $item->user_id)->first();
  2657. if(empty($userInfo)) {// 已离职的客服过滤不显示
  2658. unset($data[$key]);
  2659. continue;
  2660. }
  2661. $item->user_name = $userInfo->name ?? null;
  2662. $item->is_active = $userInfo->is_active ?? null;
  2663. $result[] = $item;
  2664. }
  2665. return $result;
  2666. }
  2667. }