Profile.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. <?php
  2. declare(strict_types=1);
  3. namespace app\mobile\controller;
  4. use app\model\VrFloder;
  5. use app\model\VrGroup;
  6. use app\model\VrView;
  7. use think\facade\Request;
  8. use app\model\Employee;
  9. use app\model\CustomerClue;
  10. use app\model\UserCollect;
  11. use app\model\Org;
  12. use app\model\Customer;
  13. use app\model\Footprints;
  14. use app\model\CustomerSubscribe;
  15. use app\model\User;
  16. use app\model\Designer;
  17. use wx\Oplatform;
  18. use app\model\Setting;
  19. use app\model\Miniprogram;
  20. use openssl\Aes;
  21. use toolkits\Aec;
  22. use app\model\Camp as CampModel;
  23. use app\model\Company;
  24. use app\model\CampEmployee;
  25. use Firebase\JWT\JWT;
  26. use app\model\AgentUser;
  27. use app\model\UserSignLog;
  28. class Profile extends Base
  29. {
  30. /*
  31. * 获客线索数量
  32. */
  33. public function getCustomerCount()
  34. {
  35. $conditon = [['employee_id', '=', $this->employeeId]];
  36. $total = CustomerClue::where($conditon)->count();
  37. $conditon[] = ['addtime', 'like', date('Y-m-d') . '%'];
  38. $today = CustomerClue::where($conditon)->count();
  39. return json(['code' => 0, 'msg' => '获取成功', 'total' => $total, 'today' => $today]);
  40. }
  41. /**
  42. * 线索列表
  43. */
  44. public function customernew()
  45. {
  46. $param = Request::only(['page'=>1,'limit'=>10,'state'=>'','phone'=>'']);
  47. $condition[] = ['employee_id', '=', $this->employeeId];
  48. if($param['state'] !== '') setCondition($param['state'], 'state', '=', $condition);
  49. $token['root_org'] = $this->rootId;
  50. if ($param['phone'] == 'has') $condition[] = ['phone', 'not null', ''];
  51. elseif ($param['phone'] == 'hasno') $condition[] = ['phone', 'null', ''];
  52. $data = CustomerClue::field('id,uid,phone,addtime,updatetime,state')->with(['user'])->withCount(['subscribe' => function ($query) use ($token) {
  53. $query->where('root_id', $token['root_org']);
  54. }, 'footprints'])->where($condition)->order('updatetime desc,id desc')->page((int)$param['page'], (int)$param['limit'])->select();
  55. foreach ($data as $k => $v) {
  56. if ($v['nickname'] == '微信用户'){
  57. $data[$k]['nickname'] = '游客';
  58. }
  59. $data[$k]['updatetime'] = $v['updatetime'] ? explode(' ', $v['updatetime']) : explode(' ', $v['addtime']);
  60. }
  61. return json(['code' =>0, 'msg' => '获取成功', 'user' => $data]);
  62. }
  63. /**
  64. * 用户收藏记录
  65. */
  66. public function collect()
  67. {
  68. $param = Request::only(['page'=>1,'limit'=>10]);
  69. $data = UserCollect::with(['content'])->where('user_id', $this->uid)->order('addtime desc')
  70. // ->page($page,$limit)
  71. ->select()->toArray();
  72. $data = array_filter($data, function ($v) {
  73. if (isset($v['content']['del']) && $v['content']['del'] == 0) {
  74. return true;
  75. } elseif (isset($v['content']['delete_time']) && $v['content']['delete_time'] == 0) {
  76. return true;
  77. } else {
  78. return false;
  79. }
  80. });
  81. $page = (int)$param['page'];
  82. $limit = (int)$param['limit'];
  83. $data = array_slice($data, ($page - 1) * $limit, $limit);
  84. return json(['code' => 0, 'data' => $data]);
  85. }
  86. /*
  87. * 我的团队
  88. * 公司员工列表
  89. * screened:company企业,team团队
  90. * screened = team团队,company公司
  91. * type= ke获客量 , ding签单量 , credits贡献值 , xuefen学分
  92. *
  93. */
  94. public function employee()
  95. {
  96. $param = Request::only(['date'=>'', 'type'=>'ke', 'page'=>1, 'screened'=>'company']);
  97. $param['date'] = $param['date'] ?: date('Y-m');
  98. $param['page'] = $param['page'] ?: 1;
  99. $param['type'] = $param['type'] ?: 'ke';
  100. $param['screened'] = $param['screened'] ?: 'company';
  101. $token['root_org'] = $this->rootId;
  102. $token['employee_id'] = $this->employeeId;
  103. $info = Employee::where('id',$this->employeeId)->field('id,org_id,uid')->find();
  104. $token['org_id'] = $info->org_id;
  105. //选取时间月初和月末时间
  106. $start_time = $param['date'] . '-01 00:00:00';
  107. $end_time = date('Y-m-d H:i:s', strtotime('+1 months', strtotime($start_time)));
  108. $where = [['state', '=', '在职'], ['uid', '<>', 0]];
  109. if ($param['screened'] == 'company') {
  110. $where[] = ['root_id', '=', $token['root_org']];
  111. } else {
  112. $where[] = ['root_id', '=', $token['root_org']];
  113. $org_id = orgSubIds($token['org_id']);
  114. $where[] = ['org_id', 'in', $org_id];
  115. }
  116. $searchObj = Employee::with(['user' => function ($query) {
  117. $query->field('id, headimgurl');
  118. }])->field('id,uid,name,org_id')->where($where);
  119. if ($param['type'] == 'ding') {
  120. $chaos = Customer::changeState('签单', 'chaos');
  121. $searchObj->withCount([
  122. 'customer' => function ($query) use ($start_time, $end_time, $chaos) {
  123. $query->where([['addtime', 'between', [$start_time, $end_time]], ['state', 'in', $chaos]]);
  124. }
  125. ]);
  126. $field = 'customer_count';
  127. $gdp = 'userdingGdp';
  128. $totalType = 'total_customer';
  129. } elseif ($param['type'] == 'ke') {
  130. $searchObj->withCount([
  131. 'clue' => function ($query) use ($start_time, $end_time) {
  132. $query->where([['addtime', 'between', [$start_time, $end_time]]]);
  133. }
  134. ]);
  135. $field = 'clue_count';
  136. $gdp = 'userFanGdp';
  137. $totalType = 'total_clue';
  138. } elseif ($param['type'] == 'xuefen') {
  139. $searchObj->withSum(['studycredit' => function ($query) use ($start_time, $end_time) {
  140. $query->where([['addtime', 'between', [$start_time, $end_time]]]);
  141. }], 'credits');
  142. $field = 'studycredit_sum';
  143. $gdp = 'usersdyctGdp';
  144. $totalType = 'total_studycredit';
  145. }else {
  146. $searchObj->withSum(['credits' => function ($query) use ($start_time, $end_time) {
  147. $query->where([['addtime', 'between', [$start_time, $end_time]], ['type', '=', 1]]);
  148. }], 'credits');
  149. $field = 'credits_sum';
  150. $gdp = 'userdcreditsGdp';
  151. $totalType = 'total_credits';
  152. }
  153. $data = $searchObj->select()->toArray();
  154. array_multisort(array_column($data, $field), SORT_DESC, $data);
  155. // 统计总数
  156. $count = array_sum(array_column($data, $field));
  157. // 获取我的数据与排名
  158. $found_arr = array_column($data, 'id');
  159. $key = array_search($token['employee_id'], $found_arr);
  160. $myRanking = $data[$key];
  161. $myRanking['ranking'] = $key + 1;
  162. if(!isset($myRanking['org_name'])) $myRanking['org_name'] = Org::where('id',$myRanking['org_id'])->value('name');
  163. // 获取我的占比
  164. $myRanking[$gdp] = $myRanking['grawth'] = $count == 0 ? 0 : floor($myRanking[$field] / $count * 100);
  165. $myRanking['value'] = $myRanking[$field];//统一字段
  166. // 总人数
  167. $myRanking['count'] = count($data);
  168. $myRanking[$totalType] = $myRanking['total'] = $count;//统一字段
  169. $data = array_slice($data, ($param['page'] - 1) * 10, 10);
  170. if ($data) {
  171. $org_ids = array_column($data,'org_id');
  172. $org_names = Org::where([['id','in',$org_ids]])->column('name','id');
  173. foreach ($data as $key => $value) {
  174. $data[$key]['org_name'] = isset($org_names[$value['org_id']]) ? $org_names[$value['org_id']] : '';
  175. }
  176. }
  177. if ($param['type'] !== 'ke' && $param['type'] !== 'ding') {
  178. if($param['type']== 'xuefen'){
  179. foreach ($data as $k => $v) {
  180. $data[$k]['studycredit_max'] = $data[$k]['studycredit_sum'];
  181. }
  182. }else{
  183. foreach ($data as $k => $v) {
  184. $data[$k]['credits_max'] = $data[$k]['credits_sum'];
  185. }
  186. }
  187. }
  188. $companyName = Org::where(['id' => $token['root_org']])->value('name');
  189. foreach ($data as $key => $value) {
  190. $data[$key]['value'] = $value[$field];
  191. }
  192. return json(['code' => 0, 'msg' => '获取成功', 'company_ranking_list' => $data, 'my_ranking' => $myRanking, 'company' => $companyName]);
  193. }
  194. /**
  195. * 线索无效
  196. */
  197. public function invalidClue($id)
  198. {
  199. $clue = CustomerClue::where([['id', '=', $id], ['employee_id', '=', $this->employeeId]])->findOrEmpty();
  200. if ($clue->isEmpty()) return json(['code' => 1, 'msg' => '操作失败']);
  201. $clue->state = 2;
  202. $clue->save();
  203. return json(['code' =>0, 'msg' => '操作成功']);
  204. }
  205. /**
  206. * 客户访问记录(👣)
  207. */
  208. public function footprints($id)
  209. {
  210. $token['employee_id'] = $this->employeeId;
  211. $token['root_org'] = $this->rootId;
  212. $clue = CustomerClue::where([['id', '=', $id], ['employee_id', '=', $token['employee_id']],['uid','>',0]])->findOrEmpty();
  213. if ($clue->isEmpty()) return json(['code' => 0, 'data' => [], 'msg' => '获取成功']);
  214. // 查询条件设置
  215. $condition = [
  216. ['addtime','>=',$clue->addtime],
  217. ['uid', '=', $clue->uid],
  218. ['org_id', 'in', orgSubIds($token['root_org'])]
  219. ];
  220. $data = Footprints::where($condition)->order('id desc')->field('id,pipe_type,reg_info,visit_due_time,addtime,uid')->select();
  221. if (!$data->isEmpty()) {
  222. //装修案例预约记录
  223. $w2[] = ['uid', 'in', array_column($data->toArray(), 'uid')];
  224. $w2[] = ['root_id', '=', $token['root_org']];
  225. $sub = CustomerSubscribe::where($w2)->field('sucai_id,uid')->select()->toArray();
  226. foreach ($data as $k => $v) {
  227. $data[$k]['subscribe'] = 0;
  228. $data[$k]['group_view'] = [];
  229. if ($v['pipe_type'] == 'materialCase') {
  230. $json = json_decode($v->getData('reg_info'), true);
  231. if(empty($josn) || !isset($json['id'])) break;//json字段缺失导致报错
  232. foreach ($sub as $k2 => $v2) {
  233. if ($v2['uid'] == $v['uid'] && $v2['sucai_id'] == $json['id']) {
  234. $data[$k]['subscribe'] = 1;
  235. break;
  236. }
  237. }
  238. }elseif($v['pipe_type'] == 'groupVr') {
  239. $json = json_decode($v->getData('reg_info'), true);
  240. $data[$k]['group_view'] = isset($json['view_foots']) ? $json['view_foots'] : [];
  241. }
  242. }
  243. }
  244. return json(['code' =>0, 'data' => $data, 'msg' => '获取成功']);
  245. }
  246. /**
  247. * 更新个人信息
  248. */
  249. public function update()
  250. {
  251. $param = Request::only(['qrcode'=>'', 'image_photo'=>'','name'=>'','nickname'=>'','wx'=>'','position'=>'','headimg'=>'']);
  252. if(!array_filter($param)) return json(['code' =>1, 'msg' => '修改失败']);
  253. $find = Employee::find($this->employeeId);
  254. if ($param['nickname']) {
  255. User::where('id', $this->uid)->update(['nickname' => $param['nickname']]);
  256. unset($param['nickname']);
  257. }
  258. if ($param['headimg']) {
  259. $ali_oss_bindurl = 'https://' . config('app.ali_oss_bindurl') . '/';
  260. $headimgurl = $ali_oss_bindurl . $param['headimg'] . '?x-oss-process=image/resize,w_132';
  261. User::where('id', $this->uid)->update(['headimgurl' => $headimgurl]);
  262. unset($param['headimg']);
  263. }
  264. if($param['name']) $param['initials'] = strtoupper(substr(hanzi2pinyin($param['name']),0,1));
  265. foreach ($param as $k => $v) {
  266. if($v) $find->$k = $v;
  267. }
  268. $find->save();
  269. if($param['image_photo']) Designer::where('employee_id', '=', $this->employeeId)->save(['headimgurl' => $param['image_photo']]);
  270. return json(['code' =>0, 'msg' => '修改成功']);
  271. }
  272. /**
  273. * 绑定微信
  274. * 薛鹊开放平台,同时同步数据
  275. * 绑定所有手机号 同时关联旧数据
  276. */
  277. public function wxBind($code)
  278. {
  279. $appid = config('app.xqmobile_appid');
  280. $appscreap = config('app.xqmobile_secret');
  281. // 授权获取accesstoken
  282. $tokendata = (new Oplatform())->getAccessToken($appid, $appscreap, $code);
  283. if(!isset($tokendata['unionid'])) return json(['code' => 1, 'msg' => 'code无效']);
  284. $url = "https://api.weixin.qq.com/sns/userinfo?access_token=" . $tokendata['access_token'] . "&openid=" . $tokendata['openid'];
  285. $info = file_get_contents($url);
  286. $check = json_decode($info, true);
  287. if (!isset($check['unionid'])) return json(['code' => 1, 'msg' => 'access_token失效']);
  288. //手机号
  289. $phone = Employee::where('id',$this->employeeId)->value('phone');
  290. $where = [
  291. ['phone','=',$phone],
  292. ['uid','>',0],
  293. ['state','=','在职'],
  294. ['xq_unionid','<>',''],
  295. ['xq_unionid','<>',$check['unionid']]
  296. ];
  297. $find = Employee::where($where)->value('id');
  298. if($find) return json(['code' => 1, 'msg' => '已绑定过其他微信,请先解绑']);
  299. $where = [
  300. ['phone','=',$phone],
  301. ['uid','>',0],
  302. ['state','=','在职']
  303. ];
  304. $emps = Employee::where($where)->column('id,uid');
  305. $ids = array_column($emps,'id');
  306. $uids = array_column($emps,'uid');
  307. Employee::where([['id','in',$ids]])->update(['xq_unionid'=>$check['unionid'],'xq_mobile_openid'=>$check['openid']]);
  308. User::where([['id','in',$uids]])->update(['xq_unionid'=>$check['unionid'],'xq_mobile_openid'=>$check['openid']]);
  309. return json(['code' => 0, 'msg' => '绑定成功']);
  310. }
  311. /**
  312. * 解绑微信
  313. */
  314. public function unbinding()
  315. {
  316. //手机号
  317. $phone = Employee::where('id',$this->employeeId)->value('phone');
  318. $where = [
  319. ['phone','=',$phone],
  320. ['uid','>',0],
  321. ['state','=','在职']
  322. ];
  323. $emps = Employee::where($where)->column('id,uid');
  324. $ids = array_column($emps,'id');
  325. $uids = array_column($emps,'uid');
  326. Employee::where([['id','in',$ids]])->update(['xq_mobile_openid'=>'']);
  327. User::where([['id','in',$uids]])->update(['xq_mobile_openid'=>'']);
  328. return json(['code' => 0, 'msg' => '解绑成功']);
  329. }
  330. /**
  331. * 报备客户
  332. */
  333. public function add()
  334. {
  335. $param = Request::only(['id'=>'', 'name'=>'','phone'=>'']);
  336. $clue = CustomerClue::where([['id','=',$param['id']],['employee_id','=',$this->employeeId],['state','=',0]])->findOrEmpty();
  337. if($clue->isEmpty()) return json(['code' => 1, 'msg' => '报备失败']);
  338. ////判断手机号是否在公海,资源库,已建档,回收站
  339. $orgids = orgSubIds($this->rootId);
  340. $tips = $this->checkPepeat([$param['phone']], $orgids, $this->employeeId);
  341. if (!empty($tips)) return $tips;
  342. // 检测线索是否已经被其他同事建档过
  343. $exist = CustomerClue::where([
  344. ['org_id', 'in', $orgids],
  345. ['uid', '=', $clue->uid],
  346. ['state', '=', 1],
  347. ['id', '<>', $param['id']]
  348. ])->find();
  349. if ($exist) return json(['code' => 1, 'msg' => '建档失败,线索已被其他员工先行建档']);
  350. // 添加数据
  351. $data['clue_id'] = $clue->id;
  352. $data['uid'] = $clue->uid;
  353. $data['employee_id'] = $this->employeeId;
  354. $data['org_id'] = Employee::where('id',$this->employeeId)->value('org_id');
  355. $data['agents_id'] = $clue->agent_id ?? NULL; //如果线索是经纪人端获取的记录
  356. $state = Customer::changeState('待确认');
  357. $data['state'] = $state;
  358. $clue->state = 1;
  359. $clue->save();
  360. return json(['code' => 0, 'msg' => '报备成功','data'=>'报备成功']);
  361. }
  362. /**
  363. * 添加客户检测手机号的重复
  364. * 1.客户录入不和资源库和回收站排重 (创艺)
  365. * 2.客户录入需查询是否和公海重复,如重复,提示可以获取该客户 (创艺) 工单id6077
  366. */
  367. public function checkPepeat($pharr, $org_id, $employee_id, $id = null)
  368. {
  369. // 同部门能否重复录入开关设置
  370. $root_id = $this->rootId;
  371. $empcrm_repeat[] = ['root_id', '=', $root_id];
  372. $empcrm_repeat[] = ['name', '=', 'empcrm_customer_repeat'];
  373. $repeat_setting = Setting::where($empcrm_repeat)->findOrEmpty();
  374. $emp_org = Employee::where('id',$this->employeeId)->value('org_id');
  375. foreach ($pharr as $val) {
  376. if (empty($val)) continue;
  377. // 先判断公海 只有添加时候判断
  378. if (empty($id)) {
  379. $poolExist = Customer::where([
  380. ['phone|phone1|phone2', '=', cypherphone(trim($val))],
  381. ['org_id', '=', $emp_org],
  382. ['employee_id', 'null', null],
  383. ['is_resource', '=', 0]
  384. ])->column('id');
  385. if (!empty($poolExist)) {
  386. $poolCrmIdStr = implode(',', $poolExist);
  387. return json(['code' => 3, 'data' => $poolCrmIdStr, 'msg' => '录入失败:客户信息已在公海中。']);
  388. }
  389. }
  390. // 新建档,检测手机号是否重复
  391. $customerExist = Customer::where([
  392. ['phone|phone1|phone2', '=', cypherphone(trim($val))],
  393. ['org_id', 'in', $org_id],
  394. ['employee_id', '>', 0] // 员工客户
  395. ])->where(
  396. function($query) {
  397. $not_sure = Customer::changeState('待确认', 'chaos');
  398. $or1[] = ['crm_res_id', 'null', null];
  399. $or2[] = ['crm_res_id', '>', 0];
  400. $or2[] = ['state', 'not in', $not_sure];
  401. $query->whereOr([$or1, $or2]);
  402. }
  403. )->field('id,employee_id,is_resource,org_id')->select()->toArray();
  404. $id_arr = array_column($customerExist, 'id');
  405. $employee_id_arr = array_column($customerExist, 'employee_id');
  406. if (empty($id)) {
  407. // 对非当前业务员名下重复客户不在判断
  408. if (in_array($employee_id, $employee_id_arr)) {
  409. // 是否已存在为自己的客户
  410. return json(['code' => 2, 'msg' => '录入失败。' . $val . '已经是您的客户。']);
  411. }
  412. } else {
  413. if (!in_array($id, $id_arr) && in_array($employee_id, $employee_id_arr)) {
  414. // 是否已存在为自己的客户
  415. return json(['code' => 2, 'msg' => '录入失败。' . $val . '已经是您的客户。']);
  416. }
  417. }
  418. //判断同部门
  419. if (!$repeat_setting->isEmpty()) {
  420. $repeat_org = explode(',', $repeat_setting['content']);
  421. if (in_array($emp_org, $repeat_org)) {
  422. // 需要验证
  423. if (!empty($customerExist)) {
  424. foreach ($customerExist as $ex) {
  425. if (in_array($ex['org_id'], $repeat_org) && $ex['employee_id'] != $employee_id) {
  426. // 为了避免开启撞单之前都已经重复的情况,所以要查一下这个id的现数据
  427. if ($id) {
  428. $this_customer = Customer::find($id);
  429. if ($this_customer) {
  430. $this_customer_phone = array_filter([$this_customer['phone'], $this_customer['phone1'], $this_customer['phone2']]);
  431. if (!in_array($val, $this_customer_phone)) { // 不是原来的客户手机号,编辑时候判断撞单
  432. $have_emp = Employee::where('id', '=', $ex['employee_id'])->value('name');
  433. return json(['code' => 2, 'msg' => '该客户与员工' . $have_emp . '撞单,无法录入']);
  434. }
  435. }
  436. } else {
  437. $have_emp = Employee::where('id', '=', $ex['employee_id'])->value('name');
  438. return json(['code' => 2, 'msg' => '该客户与员工' . $have_emp . '撞单,无法录入']);
  439. }
  440. }
  441. }
  442. }
  443. }
  444. }
  445. }
  446. }
  447. /**
  448. * 获取小程序token
  449. */
  450. public function getAppletToken()
  451. {
  452. $employee = Employee::where('id',$this->employeeId)->find();
  453. $user = User::where('id',$this->uid)->field(['id', 'nickname', 'headimgurl', 'sex', 'subscribe', 'phone','mini_openid','unionid'])->find();
  454. $miniprogram = Miniprogram::where('root_id',$this->rootId)->find();
  455. // 初始token
  456. $token = [
  457. 'openid' => $user->mini_openid,
  458. 'unionid' => $user->unionid,
  459. 'session_key' => '',
  460. 'client_type' => $miniprogram->notify,
  461. 'isEmployee' => true,
  462. 'mini' => $miniprogram->id,
  463. 'uid' => $user->id,
  464. 'org_id' => $employee->org_id,
  465. 'org_type' => Org::where('id', $employee->org_id)->value('org_type'),
  466. 'name' => $employee->name,
  467. 'root_org' => $this->rootId,
  468. 'employee_id' => $this->employeeId,
  469. 'isManager' => $employee->is_manager ? true : false
  470. ];
  471. $data = http_build_query($token);
  472. $aes = new Aes(config('app.jwt_key'));
  473. $key = $aes->encrypt($data);
  474. // token数据设置
  475. $payload = array(
  476. "iss" => "https://" . Request()->domain(),
  477. "aud" => 'mini',
  478. "iat" => time(),
  479. "nbf" => time(),
  480. "data" => $key
  481. );
  482. // 自定义登陆状态
  483. $token = JWT::encode($payload, config('app.jwt_key'));
  484. return json(['code' => 0,'token'=>$token,'empid'=>$this->employeeId,'orgid'=>$employee->org_id,'client_type'=>$miniprogram->notify]);
  485. }
  486. }