Resources.php 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918
  1. <?php
  2. namespace app\api\controller;
  3. use app\model\Customer;
  4. use app\model\CustomerVisitLog;
  5. use app\model\Employee;
  6. use app\model\OutCallLog;
  7. use app\model\OutCallMbLog;
  8. use app\model\Setting;
  9. use think\facade\Db;
  10. use toolkits\Aec;
  11. use app\model\CustomerSource;
  12. use app\model\Company;
  13. use app\model\FishData;
  14. use app\model\Org;
  15. use app\model\CustomerPortraitField;
  16. use clue\Fish;
  17. class Resources extends Base
  18. {
  19. /**
  20. * 客户分配列表
  21. */
  22. public function list($page = 1, $limit = 20)
  23. {
  24. $param = $this->request->only(['connect', 'label', 'start_date', 'end_date', 'keyword']);
  25. $employeeId = $this->request->token['employee_id'];
  26. $condition = [
  27. ['customer.crm_res_id', 'NOTNULL', null],
  28. ['customer.employee_id', '=', $employeeId],
  29. ['customer.state', 'in', Customer::changeState('待确认', 'chaos')],
  30. ['customer.valid_time','NULL', null]
  31. ];
  32. if (!empty($param['end_date'])) {
  33. $condition[] = ['customer.employee_time', '<', date('Y-m-d H:i:s', strtotime($param['end_date']) + 86400)];
  34. }
  35. if (!empty($param['start_date'])) {
  36. $condition[] = ['customer.employee_time', '>=', date('Y-m-d H:i:s', strtotime($param['start_date']))];
  37. }
  38. if (!empty($param['label'])) $condition[] = ['customer.remark', 'like', '%' . $param['label'] . '%'];
  39. $orCondition = [];
  40. if (!empty($param['keyword'])) {
  41. $aec = new Aec(config('app.aec_key'), config('app.aec_iv'));
  42. $value = $aec->encrypt($param['keyword']);
  43. $orCondition = [
  44. [['customer.name', 'like', '%' . $param['keyword'] . '%']],
  45. [['customer.community_name', 'like', '%' . $param['keyword'] . '%']],
  46. [['customer.phone', '=', $value]]
  47. ];
  48. }
  49. // 是否显示分配前的跟进信息
  50. $visitReset = Setting::where([['root_id', '=', $this->request->token['root_org']], ['name', '=', 'visit_reset']])->findOrEmpty();
  51. $visitResetSetting = !$visitReset->isEmpty() ? (int)$visitReset->content : 1;
  52. // 并未查已联系未联系
  53. if (!isset($param['connect']) || $param['connect'] == '') {
  54. $data = Customer::alias('customer')->where($condition)->where(function ($query) use ($orCondition) {
  55. $query->whereOr($orCondition);
  56. })->field('id,sex,name,community_name,square,employee_time,remark,phone')->page($page, $limit)->order('updatetime desc')->select();
  57. } else {
  58. $calCondition = [['out_call_log.employee_id', '=', $employeeId]];
  59. $mbCalCondition = [['out_call_mb_log.employee_id', '=', $employeeId]];
  60. $visitCondition = [['customer_visit_log.employee_id', '=', $employeeId], ['customer_visit_log.remark', '<>', '资源库分配']];
  61. if (!$visitResetSetting) {
  62. $calCondition[] = ['out_call_log.addtime', '>', Db::raw('customer.employee_time')];
  63. $mbCalCondition[] = ['out_call_mb_log.addtime', '>', Db::raw('customer.employee_time')];
  64. $visitCondition[] = ['customer_visit_log.addtime', '>', Db::raw('customer.employee_time')];
  65. }
  66. // 查询已联系客户id及联系次数
  67. $calNum = OutCallLog::withJoin('customer')
  68. ->where($condition)
  69. ->where($calCondition)
  70. ->where([['is_redistribution','=',0]])//只查询重新分配之后的外呼记录
  71. ->where(function ($query) use ($orCondition) {
  72. $query->whereOr($orCondition);
  73. })->group('customer.id')
  74. ->column('count(customer.id) as num', 'customer.id');
  75. // 手机直接拨打
  76. $mbCalNum = OutCallMbLog::withJoin('customer')
  77. ->where($condition)
  78. ->where($mbCalCondition)
  79. ->where([['is_redistribution','=',0]])//只查询重新分配之后的外呼记录
  80. ->where(function ($query) use ($orCondition) {
  81. $query->whereOr($orCondition);
  82. })->group('customer.id')
  83. ->column('count(customer.id) as num', 'customer.id');
  84. // 跟进过的客户
  85. $visitNum = CustomerVisitLog::withJoin('customer')
  86. ->where($condition)
  87. ->where($visitCondition)
  88. ->where(function ($query) use ($orCondition) {
  89. $query->whereOr($orCondition);
  90. })->group('customer.id')
  91. ->column('count(customer.id) as num', 'customer.id');
  92. $connectCustomerId = array_keys($calNum);
  93. $connectCustomerId1 = array_keys($mbCalNum);
  94. $connectCustomerId2 = array_keys($visitNum);
  95. $connectCustomerId = array_merge_recursive($connectCustomerId, $connectCustomerId1, $connectCustomerId2);
  96. $connectCustomerId = array_unique($connectCustomerId);
  97. // 查询已联系未联系
  98. $order = 'updatetime desc';
  99. if ($param['connect'] < 0)
  100. $condition[] = ['id', 'not in', $connectCustomerId];
  101. else {
  102. $order = 'updatetime desc';
  103. $condition[] = ['id', 'in', $connectCustomerId];
  104. }
  105. $data = Customer::alias('customer')->where($condition)->where(function ($query) use ($orCondition) {
  106. $query->whereOr($orCondition);
  107. })->field('id,sex,name,community_name,square,employee_time,remark,phone')->page($page, $limit)->order($order)->select();
  108. }
  109. if (!$visitResetSetting) {
  110. $data = $data->toArray();
  111. $pageCustomerId = array_column($data, 'id');
  112. $visited = CustomerVisitLog::withJoin('customer')
  113. ->where([
  114. ['customer_visit_log.customer_id', 'in', $pageCustomerId],
  115. ['customer_visit_log.addtime', '>', Db::raw('customer.employee_time')]
  116. ])->group('customer_visit_log.customer_id')->column('customer_visit_log.customer_id');
  117. foreach ($data as &$d) {
  118. if (!in_array($d['id'], $visited)) $d['remark'] = '';
  119. }
  120. }
  121. return json(['code' => self::success, 'data' => $data]);
  122. }
  123. /**
  124. * 统计
  125. */
  126. public function statistics()
  127. {
  128. $param = $this->request->only(['start_date', 'end_date']);
  129. $startDate = empty($param['end_date']) ? date('Y-m-d H:i:s', 0) : date('Y-m-d H:i:s', strtotime($param['start_date']));
  130. $endDate = empty($param['end_date']) ? date('Y-m-d H:i:s') : date('Y-m-d H:i:s', strtotime($param['end_date']) + 86400);
  131. $employeeId = $this->request->token['employee_id'];
  132. $data = ['total' => 0, 'call_total' => 0, 'call_put_on' => 0, 'not_connected' => 0, 'billsec' => 0, 'wechat_num' => 0, 'intention' => 0];
  133. // 客户资源总数
  134. $data['total'] = Customer::where([
  135. ['crm_res_id', 'NOTNULL', null],
  136. ['employee_id', '=', $employeeId],
  137. //['state', 'in', Customer::changeState('待确认', 'chaos')],
  138. ['employee_time', '>=', $startDate],
  139. ['employee_time', '<', $endDate],
  140. ])->count();
  141. // $list = Customer::with(['outLog' => function ($query) use ($employeeId, $startDate, $endDate) {
  142. // $query->field('id,customer_id,billsec,status')->where([['employee_id', '=', $employeeId]]);
  143. // }])->field('id,add_wechat_time')
  144. // ->where([
  145. // ['crm_res_id', 'NOTNULL', null],
  146. // ['employee_id', '=', $employeeId],
  147. // ['addtime', '>=', $startDate],
  148. // ['addtime', '<', $endDate],
  149. // ])
  150. // ->select();
  151. // foreach ($list as $i) {
  152. // if (!empty($i->add_wechat_time)) $data['wechat_num']++; // 已加微信数
  153. // foreach ($i->outLog as $o) {
  154. // // 拨打电话数
  155. // $data['call_total']++;
  156. // $o->status > 0 ? $data['call_put_on']++ : $data['not_connected']++; // 拨打电话数 未接通数
  157. // $data['billsec'] += $o->billsec; // 通话时长
  158. // }
  159. // }
  160. // 电话量大统计按时间统计不再按客户分配时间统计(外呼系统)
  161. $list = OutCallLog::hasWhere('customer', function ($query) use ($employeeId) {
  162. $query->where([
  163. ['crm_res_id', 'NOTNULL', null]
  164. ]);
  165. })->where([
  166. ['OutCallLog.addtime', '>=', $startDate],
  167. ['OutCallLog.addtime', '<', $endDate],
  168. ['OutCallLog.employee_id', '=', $employeeId]
  169. ])->select();
  170. foreach ($list as $item) {
  171. $data['call_total']++;
  172. $item->status > 0 ? $data['call_put_on']++ : $data['not_connected']++;
  173. $data['billsec'] += $item->billsec;
  174. }
  175. // 普通电话拨打记录
  176. $mbCount = OutCallMbLog::hasWhere('customer', function ($query) use ($employeeId) {
  177. $query->where([
  178. ['crm_res_id', 'NOTNULL', null]
  179. ]);
  180. })->where([
  181. ['OutCallMbLog.addtime', '>=', $startDate],
  182. ['OutCallMbLog.addtime', '<', $endDate],
  183. ['OutCallMbLog.employee_id', '=', $employeeId]
  184. ])->count();
  185. $data['call_total'] += $mbCount;
  186. $data['wechat_num'] = Customer::where([
  187. ['crm_res_id', 'NOTNULL', null],
  188. ['employee_id', '=', $employeeId],
  189. ['add_wechat_time', '>=', $startDate],
  190. ['add_wechat_time', '<', $endDate],
  191. ])->count();
  192. $data['intention'] = Customer::where([
  193. ['crm_res_id', 'NOTNULL', null],
  194. ['employee_id', '=', $employeeId],
  195. ['valid_time', '>=', $startDate],
  196. ['valid_time', '<', $endDate],
  197. ])->count();
  198. return json(['code' => self::success, 'data' => $data]);
  199. }
  200. /**
  201. * 标签添加
  202. */
  203. public function label($cid, $v)
  204. {
  205. $customer = Customer::where([
  206. ['id', '=', $cid],
  207. ['crm_res_id', 'NOTNULL', null],
  208. ['employee_id', '=', $this->request->token['employee_id']],
  209. ['state', 'in', Customer::changeState('待确认', 'chaos')]
  210. ])->find();
  211. if (empty($customer)) return json(['code' => self::error_msg, 'msg' => '客户不存在']);
  212. // 将汉语中的#替换成英文#
  213. $v = str_replace('#', '#', $v);
  214. // 检测是否有标签
  215. if (preg_match('/#(?P<label>.*)#.*/', $v, $matches)) {
  216. if ($matches['label'] !== '有效') {
  217. $customer->save(['remark' => $matches['label']]);
  218. } else {
  219. // 同部门能否重复录入开关设置
  220. $root_id = request()->token['root_org'];
  221. $empcrm_repeat[] = ['root_id', '=', $root_id];
  222. $empcrm_repeat[] = ['name', '=', 'empcrm_customer_repeat'];
  223. $repeat_setting = Setting::where($empcrm_repeat)->findOrEmpty();
  224. $emp_org = request()->token['org_id'];
  225. //判断同部门
  226. if (!$repeat_setting->isEmpty()) {
  227. $repeat_org = explode(',', $repeat_setting['content']);
  228. if (in_array($emp_org, $repeat_org)) {
  229. // 需要验证
  230. // 新建档,检测手机号是否重复
  231. $customerExist = Customer::where([
  232. ['phone|phone1|phone2', '=', cypherphone($customer['phone'])],
  233. ['org_id', 'in', $repeat_org],
  234. ['employee_id', '>', 0] // 员工客户
  235. ])->where(
  236. function($query) {
  237. $not_sure = Customer::changeState('待确认', 'chaos');
  238. $or1[] = ['crm_res_id', 'null', null];
  239. $or2[] = ['crm_res_id', '>', 0];
  240. $or2[] = ['state', 'not in', $not_sure];
  241. $query->whereOr([$or1, $or2]);
  242. }
  243. )->field('id,employee_id,is_resource,org_id')->select()->toArray();
  244. if (!empty($customerExist)) {
  245. foreach ($customerExist as $ex) {
  246. if ($ex['employee_id'] != $customer['employee_id']) {
  247. $have_emp = Employee::where('id', '=', $ex['employee_id'])->value('name');
  248. return json(['code' => 1, 'msg' => '该客户与员工' . $have_emp . '撞单,无法标记有效']);
  249. }
  250. }
  251. }
  252. }
  253. }
  254. $customer->save(['state' => 1, 'valid_time' => date('Y-m-d H:i:s')]);
  255. }
  256. }
  257. $employee = Employee::find($this->request->token['employee_id']);
  258. // 添加跟进记录
  259. CustomerVisitLog::Create([
  260. 'customer_id' => $customer->id,
  261. 'type' => 1,
  262. 'employee_id' => $this->request->token['employee_id'],
  263. 'user_id' => $employee->uid,
  264. 'org_id' => $employee->org_id,
  265. 'customer_employee_id' => $customer->employee_id,
  266. 'customer_org_id' => $customer->org_id,
  267. 'data_type' => 'out_call',
  268. 'remark' => $v
  269. ]);
  270. return json(['code' => self::success, 'msg' => '标识成功']);
  271. }
  272. /**
  273. * 标签列表接口
  274. */
  275. public function labelList()
  276. {
  277. $label = Setting::where(['root_id' => $this->request->token['root_org'], 'name' => 'crm_label'])->value('content');
  278. $label = empty($label) ? [] : explode(',', $label);
  279. return json(['code' => self::success, 'data' => $label]);
  280. }
  281. /**
  282. * 标注记录接口
  283. */
  284. public function labelog($cid){
  285. $log = CustomerVisitLog::where(['employee_id' => $this->request->token['employee_id'], 'customer_id' => $cid, 'data_type' => 'out_call'])->field('addtime,remark')->order('addtime desc')->select();
  286. return json(['code'=>self::success, 'data'=>$log]);
  287. }
  288. /**
  289. * 飞鱼线索
  290. */
  291. public function lableClueList()
  292. {
  293. $channel_setting = Company::where(['root_id' => $this->request->token['root_org']])->value('channel_setting');
  294. if(isset($channel_setting) && $channel_setting){
  295. $empdata = Employee::find($this->request->token['employee_id']);
  296. // $employeeId = $this->request->token['employee_id'];
  297. // $empdata['clue_disable'] == 0//允许查看
  298. $tmp_data = json_decode($channel_setting, true);
  299. $fish = isset($tmp_data['fish_access_token']) && $empdata['clue_disable'] == 0 ? true : false;
  300. $tx = isset($tmp_data['tx_access_token']) && $empdata['clue_disable'] == 0 ? true : false;
  301. return json(['code' => self::success, 'data' => ['fish' => $fish, 'tx' => $tx,'root_id' => $this->request->token['root_org']]]);
  302. }
  303. return json(['code' => self::success, 'data' => ['fish' => false,'tx' => false, 'root_id' => $this->request->token['root_org']]]);
  304. }
  305. /**
  306. * 飞鱼资源线索统计
  307. */
  308. public function statistics_fish()
  309. {
  310. $token = $this->request->token;
  311. $param = $this->request->only(['start_date', 'end_date', 'type' => 1]);
  312. $employeeId = $token['employee_id'];
  313. $data = ['total' => 0, 'call_total' => 0, 'call_put_on' => 0, 'not_connected' => 0, 'billsec' => 0, 'allocation'=>0,'zhuang'=>0];
  314. //如果是部门领导
  315. $is_manager = $token['isManager'];
  316. $condition[] = ['root_id', '=', $token['root_org']];
  317. if ($param['type'] == 2) {
  318. $condition[] = ['type', '=', $param['type']];
  319. } else {
  320. $condition[] = ['type', 'in', [1, 4]];
  321. }
  322. $org_employee = [$employeeId];
  323. if($is_manager == 1){
  324. $team_orgs = orgSubIds($token['org_id']);
  325. $condition[] = ['org_id', 'in', $team_orgs];
  326. }else{
  327. $condition[] = ['employee_id', 'in', $org_employee];
  328. }
  329. if (!empty($param['end_date'])) {
  330. $condition[] = ['create_time_detail', '<', date('Y-m-d H:i:s', strtotime($param['end_date']) + 86400)];
  331. }
  332. if (!empty($param['start_date'])) {
  333. $condition[] = ['create_time_detail', '>=', date('Y-m-d H:i:s', strtotime($param['start_date']))];
  334. }
  335. $fish_list = FishData::where($condition)->field('id,customer_id,employee_id,is_allocation,employee_remark')->select();
  336. $data['total'] = count($fish_list);
  337. $fish_id = [];
  338. foreach ($fish_list as $val) {
  339. $fish_id[] = $val['id'];
  340. if ($val['is_allocation'] == 1) $data['allocation']++; //已分配数量
  341. $pos = strpos($val['employee_remark'], '撞单');
  342. if($pos !== false) $data['zhuang']++;
  343. }
  344. $list = OutCallLog::where([['call_id', 'in', $fish_id]])->select();
  345. foreach ($list as $item) {
  346. $data['call_total']++;
  347. $item->status > 0 ? $data['call_put_on']++ : $data['not_connected']++;
  348. $data['billsec'] += $item->billsec;
  349. }
  350. return json(['code' => self::success, 'data' => $data]);
  351. }
  352. /**
  353. * 飞鱼标签
  354. */
  355. public function list_fish_tags()
  356. {
  357. $table_tags = [
  358. [
  359. 'title' => '高级意向',
  360. 'name' => 'all0',
  361. 'value' => 0,
  362. 'tags' =>
  363. [
  364. ['title' => '已签约合同', 'name' => 'all0_1'],
  365. ['title' => '已缴纳定金', 'name' => 'all0_2'],
  366. ['title' => '客户已到店', 'name' => 'all0_3'],
  367. ['title' => '预约上门量房', 'name' => 'all0_4'],
  368. ]
  369. ],
  370. [
  371. 'title' => '中级意向',
  372. 'name' => 'all1',
  373. 'value' => 1,
  374. 'tags' =>
  375. [
  376. ['title' => '能提供户型图', 'name' => 'all1_1'],
  377. ['title' => '有装修预算', 'name' => 'all1_2'],
  378. ['title' => '添加微信待通过', 'name' => 'all1_3'],
  379. ['title' => '添加微信已通过', 'name' => 'all1_4'],
  380. ]
  381. ],
  382. [
  383. 'title' => '初级意向',
  384. 'name' => 'all2',
  385. 'value' => 2,
  386. 'tags' =>
  387. [
  388. ['title' => '仅咨询了解', 'name' => 'all2_1'],
  389. ['title' => '无真实需求', 'name' => 'all2_2'],
  390. ['title' => '对比其他平台', 'name' => 'all2_3'],
  391. ['title' => '对价格不反感', 'name' => 'all2_4'],
  392. ['title' => '近期有装修需求', 'name' => 'all2_5'],
  393. ['title' => '添加微信', 'name' => 'all2_6'],
  394. ]
  395. ],
  396. [
  397. 'title' => '意向',
  398. 'name' => 'all3',
  399. 'value' => 3,
  400. 'tags' =>
  401. [
  402. ['title' => '低意向', 'name' => 'all3_1'],
  403. ['title' => '中意向', 'name' => 'all3_2'],
  404. ['title' => '高意向', 'name' => 'all3_3'],
  405. ]
  406. ],
  407. [
  408. 'title' => '无效',
  409. 'name' => 'all4',
  410. 'value' => 4,
  411. 'tags' =>
  412. [
  413. ['title' => '添加微信未通过', 'name' => 'all4_1'],
  414. ['title' => '多次联系不上', 'name' => 'all4_2'],
  415. ['title' => '拒绝加微信', 'name' => 'all4_3'],
  416. ['title' => '无需求', 'name' => 'all4_4'],
  417. ['title' => '错号', 'name' => 'all4_5'],
  418. ['title' => '关机', 'name' => 'all4_6'],
  419. ['title' => '空号', 'name' => 'all4_7'],
  420. ['title' => '联系不上', 'name' => 'all4_8'],
  421. ['title' => '无意愿', 'name' => 'all4_9'],
  422. ['title' => '非本人', 'name' => 'all4_10']
  423. ]
  424. ],
  425. [
  426. 'title' => '默认',
  427. 'name' => 'all5',
  428. 'value' => 5,
  429. 'tags' =>
  430. [
  431. ['title' => '加微信', 'name' => 'all5_1'],
  432. ['title' => '待确认', 'name' => 'all5_2'],
  433. ['title' => '无效', 'name' => 'all5_3'],
  434. ]
  435. ],
  436. ];
  437. return json(['code' => self::success, 'data' => $table_tags]);
  438. }
  439. /**
  440. * 详情
  441. */
  442. public function list_fish_info()
  443. {
  444. $token = $this->request->token;
  445. $is_manager = $token['isManager'];
  446. $emp_id = $token['employee_id'];
  447. $param = $this->request->only(['id']);
  448. $info = FishData::where(['id' => $param['id']])->find();
  449. if(empty($info)) return json(['code' => 1, 'msg' => '线索信息有误']);
  450. if($info['type'] == 2){
  451. $clue_source = [
  452. '0' => '未知',
  453. '1' => '其他',
  454. '2' => '外部导入',
  455. '3' => '广告创意',
  456. '4' => '蹊径落地页',
  457. '5' => '一叶智能',
  458. '6' => '微信原生推广页',
  459. '7' => '讯达小程序',
  460. '8'=>'互动落地页',
  461. '9'=>'教培通'
  462. ];
  463. $clue_state = [
  464. 'LEADS_FOLLOW_TAG_DEFAULT' => '未分配',
  465. 'LEADS_FOLLOW_TAG_ASSIGNED' => '已分配待跟进',
  466. 'LEADS_FOLLOW_TAG_FOLLOWING' => '跟进中',
  467. 'LEADS_FOLLOW_TAG_VALID' => '已转换-潜在客户',
  468. 'LEADS_FOLLOW_TAG_VALID_HIGH_INTENTION' => '已转换-高意向客户',
  469. 'LEADS_FOLLOW_TAG_VALID_CONVERTED' => '已转换-已成单',
  470. 'LEADS_FOLLOW_TAG_INVALID_REPEAT' => '无效-重复',
  471. 'LEADS_FOLLOW_TAG_INVALID_NOTSELF' => '无效-非本人',
  472. 'LEADS_FOLLOW_TAG_INVALID_NOTTOUCH' => '无效-未接通',
  473. 'LEADS_FOLLOW_TAG_INVALID_NOTINTENTION' => '无效-无意向',
  474. 'LEADS_FOLLOW_TAG_INVALID_REGION_MISMATCHED' => '无效-定向外',
  475. 'LEADS_FOLLOW_TAG_INVALID_OTHER' => '无效-其他',
  476. 'LEADS_INEFFECT_REASON_TEL_NOT_CONNECTED' => '推送',
  477. ];
  478. $info['clue_state_name'] = $clue_state[$info['clue_state_name']];
  479. }else{
  480. $clue_type = [0 => '表单提交', 1 => '在线咨询', 2 => '智能电话', 3 => '网页回呼', 4 => '卡券', 5 => '抽奖'];
  481. $clue_source = [0 => '外部流量', 1 => '正常投放', 2 => '外部导入', 3 => '异常提交', 4 => '广告预览', 5 => '抖音私信', 6 => '鲁班线索'];
  482. // $clue_state = [1 => '新线索', 2 => '有意向', 3 => '转商机', 4 => '无效', 5 => '已加微信', 6 => '待再次沟通'];
  483. $info['clue_type'] = $clue_type[$info['clue_type']];
  484. }
  485. $info['clue_source'] = $clue_source[$info['clue_source']];
  486. $info['telephone'] = substr_replace($info['telephone'], '******', 3, 6);
  487. $info['gender'] = ['未知','男','女'][$info['gender']];
  488. $info['allocation_status'] = $info['is_allocation'] == 1 ? '已分配' : '未分配';
  489. $info['is_followup'] = 0;
  490. if($emp_id == $info['employee_id']) $info['is_followup'] = 1;
  491. if(!empty($info['remark_dict'])){
  492. $tmp = json_decode($info['remark_dict'],true);
  493. $info['remark_dict'] = implode(',',$tmp);
  494. }
  495. return json(['code' => 0, 'data' => $info]);
  496. }
  497. /**
  498. * 更新数据
  499. */
  500. public function list_fish_info_update()
  501. {
  502. // $request = request();
  503. $param = $this->request->only(['id', 'address', 'age', 'date', 'email', 'gender', 'location', 'name', 'qq', 'remark', 'weixin', 'employee_remark', 'tags']);
  504. $data = FishData::find($param['id']);
  505. if (empty($data)) return json(['code' => 1, 'msg' => '数据信息有误']);
  506. $param['gender'] = $param['gender'] == '男' ? 1 : ($param['gender'] == '女' ? 2 : 0);
  507. $data->save($param);
  508. return json(['code' => 0, 'msg' => '确认完成']);
  509. }
  510. /**
  511. * 客户分配列表
  512. */
  513. public function list_fish($page = 1, $limit = 20)
  514. {
  515. $token = $this->request->token;
  516. $param = $this->request->only(['connect', 'start_date', 'end_date', 'keyword','allocation','type']);
  517. $employeeId = $token['employee_id'];
  518. $order = 'id desc';
  519. //如果是部门领导
  520. $is_manager = $token['isManager'];
  521. $condition[] = ['root_id', '=', $token['root_org']];
  522. if ($param['type'] == 1) {
  523. $condition[] = ['type', 'in', [1, 4]];
  524. } else {
  525. $condition[] = ['type', '=', $param['type']];
  526. }
  527. $org_employee = [$employeeId];
  528. if($is_manager == 1){
  529. $team_orgs = orgSubIds($token['org_id']);
  530. $condition[] = ['org_id', 'in', $team_orgs];
  531. }else{
  532. $condition[] = ['employee_id', 'in', $org_employee];
  533. }
  534. if (!empty($param['end_date'])) {
  535. $condition[] = ['create_time_detail', '<', date('Y-m-d H:i:s', strtotime($param['end_date']) + 86400)];
  536. }
  537. if (!empty($param['start_date'])) {
  538. $condition[] = ['create_time_detail', '>=', date('Y-m-d H:i:s', strtotime($param['start_date']))];
  539. }
  540. if (!empty($param['connect'])) {
  541. if ($param['connect'] == 1) {
  542. $condition[] = ['is_contact', '=', 1];
  543. $order = 'is_contact desc,update_time_detail desc';
  544. }
  545. if ($param['connect'] == -1) {
  546. $condition[] = ['is_contact', '=', 0];
  547. $order = 'is_contact asc';
  548. }
  549. }
  550. if (!empty($param['allocation'])) {
  551. if ($param['allocation'] == 1) {
  552. $condition[] = ['is_allocation', '=', 1];
  553. }
  554. if ($param['allocation'] == -1) {
  555. $condition[] = ['is_allocation', '=', 0];
  556. }
  557. }
  558. $orCondition = [];
  559. if (!empty($param['keyword'])) {
  560. $orCondition = [
  561. [['location', 'like', '%' . $param['keyword'] . '%']],
  562. [['clue_owner_name', 'like', '%' . $param['keyword'] . '%']],
  563. [['telephone', '=', $param['keyword']]]
  564. ];
  565. }
  566. $path = Org::where([['pid','=',$token['org_id']]])->count();
  567. $data = FishData::where($condition)->field('id,name,telephone,location,create_time_detail,is_allocation,employee_id,is_contact')->whereOr($orCondition)->page($page, $limit)->order($order)->select();
  568. foreach($data as $key => $val){
  569. $data[$key]['phone'] = $val['telephone'];
  570. $data[$key]['is_followup'] = 0;
  571. if($is_manager==1 && empty($path)){
  572. $data[$key]['is_followup'] = 1;
  573. }
  574. if($employeeId == $data[$key]['employee_id']){
  575. $data[$key]['is_followup'] = 2;
  576. }
  577. }
  578. return json(['code' => self::success, 'data' => $data]);
  579. }
  580. //拨号
  581. public function create_bridge_call()
  582. {
  583. $token = $this->request->token;
  584. $root_id = $token['root_org'];
  585. $employee_id = $token['employee_id'];
  586. $param = $this->request->only(['customer_id']);
  587. $fish_data = FishData::where(['id' => $param['customer_id']])->find();
  588. if (empty($fish_data)) return json(['code' => 1, 'msg' => '线索信息有误']);
  589. $empcrm = Employee::where('id', $token['employee_id'])->find();
  590. $employeePhone = $empcrm['phone'];
  591. $customerPhone = $fish_data['telephone'];
  592. if ($fish_data['type'] == 1) {
  593. $fish = new Fish(config('app.fish_clue_appid'), config('app.fish_clue_secret'));
  594. $channel_setting = Company::where(['root_id' => $root_id])->value('channel_setting');
  595. } elseif($fish_data['type'] == 4) {
  596. $fish = new Fish(config('app.fish_clue_appid_wh'), config('app.fish_clue_secret_wh'));
  597. $channel_setting = Company::where(['root_id' => $root_id])->value('channel_setting_wh');
  598. } else {
  599. //腾讯外呼目前直接返回手机号
  600. // 添加记录
  601. $logData = [
  602. 'session_id' => str_rand(10),
  603. 'bind_num' => $customerPhone,
  604. 'caller_num' => $employeePhone,
  605. 'callee_num' => $customerPhone,
  606. 'employee_id' => $employee_id,
  607. 'root_id' => $root_id,
  608. 'customer_id' => 0, //$param['customer_id']
  609. 'call_id' => $param['customer_id'] // 存储飞鱼线索id
  610. ];
  611. $log = OutCallLog::create($logData);
  612. // 添加访问记录
  613. CustomerVisitLog::Create([
  614. 'customer_id' => 0,
  615. 'type' => 1,
  616. 'employee_id' => $employee_id,
  617. 'user_id' => $empcrm['uid'],
  618. 'org_id' => $empcrm['org_id'],
  619. 'customer_employee_id' => 0,
  620. 'customer_org_id' => 0,
  621. 'data_type' => 'out_call',
  622. 'data_id' => $log->id,
  623. 'remark' => '回访跟踪了腾讯线索客户'
  624. ]);
  625. $fish_data->save(['is_contact' => 1, 'update_time_detail' => date('Y-m-d H:i:s')]);
  626. return ['code' => 0, 'msg' => '拨打中,请接听', 'mobile' => $customerPhone];
  627. }
  628. // $fish = new Fish(config('app.fish_clue_appid'), config('app.fish_clue_secret'));
  629. // $channel_setting = Company::where(['root_id' => $root_id])->value('channel_setting');
  630. $tmp_data = json_decode($channel_setting, true);
  631. $tmp_info = [
  632. //广告主id
  633. 'advertiser_id' => intval($fish_data['advertiser_id']),
  634. //线索id
  635. 'clue_id' => intval($fish_data['clue_id']),
  636. // 主叫号码,必须为11位手机号码,否则呼叫失败
  637. 'caller_number' => $employeePhone,//'15713803101'
  638. // 被叫号码,即线索号码
  639. 'callee_number' => $customerPhone,
  640. ];
  641. $rs = $fish->getCallVirtualNumber($tmp_data['fish_access_token'], $tmp_info);
  642. if ($rs['code'] != 0) {
  643. $virtual_number = $customerPhone;
  644. // 添加记录
  645. $logData = [
  646. 'session_id' => str_rand(10),
  647. 'bind_num' => $virtual_number,
  648. 'caller_num' => $employeePhone,
  649. 'callee_num' => $customerPhone,
  650. 'employee_id' => $employee_id,
  651. 'root_id' => $root_id,
  652. 'customer_id' => 0, //$param['customer_id']
  653. 'call_id' => $param['customer_id'] // 存储飞鱼线索id
  654. ];
  655. $log = OutCallLog::create($logData);
  656. // return json(['code' => 1, 'msg' => '呼叫失败']);
  657. } else {
  658. if($rs['data']['call_result_code'] != 103){
  659. return json(['code' => 1, 'msg' => $rs['data']['call_result_message']]);
  660. }
  661. $virtual_number = $rs['data']['virtual_number'];
  662. // 添加记录
  663. $logData = [
  664. 'session_id' => $rs['data']['contact_id'],
  665. 'bind_num' => $virtual_number,
  666. 'caller_num' => $employeePhone,
  667. 'callee_num' => $customerPhone,
  668. 'employee_id' => $employee_id,
  669. 'root_id' => $root_id,
  670. 'customer_id' => 0, //$param['customer_id']
  671. 'call_id' => $param['customer_id'] // 存储飞鱼线索id
  672. ];
  673. $log = OutCallLog::create($logData);
  674. }
  675. // 添加访问记录
  676. CustomerVisitLog::Create([
  677. 'customer_id' => 0,
  678. 'type' => 1,
  679. 'employee_id' => $employee_id,
  680. 'user_id' => $empcrm['uid'],
  681. 'org_id' => $empcrm['org_id'],
  682. 'customer_employee_id' => 0,
  683. 'customer_org_id' => 0,
  684. 'data_type' => 'out_call',
  685. 'data_id' => $log->id,
  686. 'remark' => '回访跟踪了飞鱼线索客户'
  687. ]);
  688. $fish_data->save(['is_contact' => 1, 'update_time_detail' => date('Y-m-d H:i:s')]);
  689. if (isset($virtual_number)) {
  690. return ['code' => 0, 'msg' => '拨打中,请接听', 'mobile' => $virtual_number];
  691. } else {
  692. return ['code' => 1, 'msg' => '拨打中,请接听'];
  693. }
  694. }
  695. /**
  696. * 童通话记录接口
  697. */
  698. public function labelog_fish($cid){
  699. $log = OutCallLog::where(['call_id' => $cid])->field('addtime,call_msg as remark')->order('addtime desc')->select();
  700. foreach ($log as &$val) {
  701. $val['remark'] = (isset($val['remark']) && $val['remark']) ? $val['remark'] : '跟踪回访了此客户';
  702. }
  703. return json(['code'=>self::success, 'data'=>$log]);
  704. }
  705. /**
  706. * 转化
  707. */
  708. public function fish_validation()
  709. {
  710. $token = $this->request->token;
  711. $root_id = $token['root_org'];
  712. $empid = $token['employee_id'];
  713. $orgids = orgSubIds($root_id);
  714. $param = $this->request->only(['customer_id']);
  715. $customer_info = FishData::where(['id'=>$param['customer_id']])->find();
  716. if(empty($customer_info)) return json(['code' => 1, 'msg' => '线索信息有误']);
  717. if($customer_info['is_allocation'] == 1) return json(['code' => 1, 'msg' => '线索已转化,无法重复转化']);
  718. $customerExist = Customer::where([
  719. ['phone|phone1|phone2', '=', cypherphone($customer_info['telephone'])],
  720. ['org_id', 'in', $orgids],
  721. ['employee_id', '>', 0]
  722. ])->where(
  723. function($query) {
  724. $not_sure = Customer::changeState('待确认', 'chaos');
  725. $or1[] = ['crm_res_id', 'null', null];
  726. $or2[] = ['crm_res_id', '>', 0];
  727. $or2[] = ['state', 'not in', $not_sure];
  728. $query->whereOr([$or1, $or2]);
  729. }
  730. )->field('id,employee_id,is_resource,org_id,source_id')->select()->toArray();
  731. if (!empty($customerExist)) {
  732. foreach ($customerExist as $ex) {
  733. if ($ex['employee_id'] != $empid) {
  734. //查询撞单客户渠道
  735. $source_name = '';
  736. if ($ex['source_id']) {
  737. $source = CustomerSource::where(['id' => $ex['source_id']])->value('source');
  738. $source_name = $source . '渠道客户';
  739. }
  740. $have_emp = Employee::where('id', '=', $ex['employee_id'])->value('name');
  741. return json(['code' => 1, 'msg' => '该客户与员工' . $have_emp . $source_name . '撞单,无法转化']);
  742. }
  743. }
  744. }
  745. //检查线索来源
  746. if ($customer_info['type'] == 1 || $customer_info['type'] == 4) {
  747. $fish_source = CustomerSource::where(['root_id' => $root_id, 'source' => '飞鱼线索'])->find();
  748. } else {
  749. $fish_source = CustomerSource::where(['root_id' => $root_id, 'source' => '腾讯线索'])->find();
  750. }
  751. //检查是否被分配
  752. if (empty($customer_info['employee_id']) || !isset($customer_info['employee_id'])) return json(['code' => 1, 'msg' => '请先分配线索']);
  753. $employee_info = Employee::where(['id' => $customer_info['employee_id'], 'root_id' => $root_id])->field('id,name,org_id,uid')->find();
  754. if (!$employee_info) return json(['code' => 1, 'msg' => '员工信息有误']);
  755. $save['employee_id'] = $customer_info['employee_id'];
  756. $save['phone'] = $customer_info['telephone'];
  757. $save['name'] = $customer_info['name'];
  758. $save['is_distribution'] = 0;
  759. $save['is_resource'] = 0;
  760. $save['state'] = 1;
  761. $save['community_name'] = $customer_info['location'];
  762. $save['addtime'] = $customer_info['create_time_detail'];
  763. $save['org_id'] = $employee_info['org_id'];
  764. $save['sex'] = boolval($employee_info['gender']);
  765. $save['source_id'] = $fish_source['id'];
  766. $save['employee_time'] = date('Y-m-d H:i:s');
  767. $save['valid_time'] = date('Y-m-d H:i:s');
  768. //客户来源扩展字段
  769. $visitField_decode = [];
  770. $visit_default = CustomerPortraitField::where([['root_id', '=', $root_id], ['keyname', 'in', ['source_id', 'sign_time']]])->column('id', 'keyname');
  771. if (isset($visit_default['sign_time']) && $visit_default['sign_time']){
  772. $visitField_decode[] = ['id' => $visit_default['sign_time'], 'keyname' => 'sign_time', 'value' => $customer_info['create_time_detail']];
  773. }
  774. if (isset($visit_default['source_id']) && $visit_default['source_id']){
  775. $visitField_decode[] = ['id' => $visit_default['source_id'], 'keyname' => 'source_id', 'value' => $fish_source['id']];
  776. }
  777. $save['ext'] = !empty($visitField_decode) ? json_encode($visitField_decode) : NULL;
  778. $ms = Customer::create($save);
  779. if ($ms) {
  780. FishData::where(['id' => $param['customer_id']])->update(['is_allocation' => 1, 'customer_id' => $ms->id, 'is_contact' => 1]);
  781. //更新通话记录
  782. $log_list = OutCallLog::where(['call_id' => $param['customer_id']])->select()->toArray();
  783. OutCallLog::where(['call_id' => $param['customer_id']])->update(['customer_id' => $ms->id]);
  784. foreach ($log_list as $val) {
  785. CustomerVisitLog::where(['data_id' => $val['id'], 'data_type' => 'out_call'])->update(['customer_id' => $ms->id]);
  786. }
  787. if(!isset($log_list) || empty($log_list)){
  788. CustomerVisitLog::Create([
  789. 'customer_id' => $ms->id,
  790. 'type' => '',
  791. 'employee_id' => $empid,
  792. 'user_id' => $employee_info['uid'],
  793. 'org_id' => $employee_info['org_id'],
  794. 'customer_employee_id' => 0,
  795. 'customer_org_id' => 0,
  796. 'data_type' => '',
  797. 'data_id' => '',
  798. 'remark' => '标记有效'
  799. ]);
  800. }
  801. return json(['code' => 0, 'msg' => '确认完成']);
  802. } else {
  803. return json(['code' => 1, 'msg' => '确认失败']);
  804. }
  805. }
  806. /**
  807. * 重新分配员工
  808. */
  809. public function fish_validation_again()
  810. {
  811. $token = $this->request->token;
  812. $root_id = $token['root_org'];
  813. $param = $this->request->only(['customer_id', 'employee_id']);
  814. $data = FishData::find($param['customer_id']);
  815. if (empty($data)) return json(['code' => 1, 'msg' => '数据信息有误']);
  816. $employee_info = Employee::where(['id' => $param['employee_id'], 'root_id' => $root_id])->field('id,name,org_id')->find();
  817. if (!$employee_info) return json(['code' => 1, 'msg' => '员工信息有误']);
  818. $data->save(['allocation_status' => 1, 'org_id' => $employee_info['org_id'], 'employee_id' => $employee_info['id'], 'clue_owner_name' => $employee_info['name']]);
  819. return json(['code' => 0, 'msg' => '确认完成']);
  820. }
  821. /**
  822. * 重新分配员工
  823. */
  824. public function fish_validation_lable()
  825. {
  826. $token = $this->request->token;
  827. $root_id = $token['root_org'];
  828. $param = $this->request->only(['customer_id', 'remark']);
  829. $data = FishData::find($param['customer_id']);
  830. if (empty($data)) return json(['code' => 1, 'msg' => '数据信息有误']);
  831. $data->save(['employee_remark' => $param['remark']]);
  832. return json(['code' => 0, 'msg' => '确认完成']);
  833. }
  834. }