企微短剧业务系统

CustomerService.php 135KB

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