1
0

Customer.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. <?php
  2. namespace app\model;
  3. use think\db\exception\PDOException;
  4. use think\facade\Db;
  5. use think\Model;
  6. use toolkits\Aec;
  7. use think\model\concern\SoftDelete;
  8. class Customer extends Model
  9. {
  10. use SoftDelete;
  11. protected $deleteTime = 'delete_time';
  12. protected $defaultSoftDelete = 0;
  13. /**
  14. * 关联员工
  15. */
  16. public function employee()
  17. {
  18. return $this->belongsTo(Employee::class, 'employee_id');
  19. }
  20. /**
  21. * 销售部门关联
  22. */
  23. public function employeeOrg()
  24. {
  25. return $this->hasOneThrough(Org::class, Employee::class, 'id', 'id', 'employee_id', 'org_id');
  26. }
  27. /**
  28. * 关联部门
  29. */
  30. public function org()
  31. {
  32. return $this->belongsTo(Org::class, 'org_id')->bind(['org_name' => 'name']);
  33. }
  34. /**
  35. * 关联公海
  36. */
  37. public function pool()
  38. {
  39. return $this->belongsTo(Pool::class, 'org_id')->bind(['pool_name' => 'name']);
  40. }
  41. /**
  42. * 来源
  43. */
  44. public function source()
  45. {
  46. return $this->belongsTo(CustomerSource::class, 'source_id');
  47. }
  48. /**
  49. * 设计师
  50. */
  51. public function designer()
  52. {
  53. return $this->belongsTo(Employee::class, 'designer_id');
  54. }
  55. /**
  56. * 设计师部门
  57. */
  58. public function designerOrg()
  59. {
  60. return $this->hasOneThrough(Org::class, Employee::class, 'id', 'id', 'designer_id', 'org_id');
  61. }
  62. /**
  63. * 电话拨打记录
  64. */
  65. public function outLog()
  66. {
  67. return $this->hasMany(OutCallLog::class, 'customer_id');
  68. }
  69. /**
  70. * 建档机制
  71. * 1,拿到手机号的才能建档
  72. * 2,档案中有该手机号也不能建档
  73. */
  74. public static function onBeforeInsert($customer)
  75. {
  76. // 没有手机号不能建档
  77. if (empty($customer->phone)) return false;
  78. // 手机号已经存在无法建档
  79. $condition = [
  80. 'employee_id' => $customer->employee_id,
  81. 'phone' => $customer->phone
  82. ];
  83. $had = self::where($condition)->find();
  84. if ($had) return false;
  85. return true;
  86. }
  87. /**
  88. * 手机号解密
  89. */
  90. public function getPhoneAttr($value)
  91. {
  92. if (preg_match('/^1[\d]{10}$/', $value)) return $value;
  93. $aec = new Aec(config('app.aec_key'), config('app.aec_iv'));
  94. $value = $aec->decrypt($value);
  95. return $value;
  96. }
  97. /**
  98. * 手机号加密
  99. */
  100. public function setPhoneAttr($value)
  101. {
  102. $aec = new Aec(config('app.aec_key'), config('app.aec_iv'));
  103. $value = $aec->encrypt($value);
  104. return $value;
  105. }
  106. /**
  107. * 手机号解密
  108. */
  109. public function getPhone1Attr($value)
  110. {
  111. if (empty($value)) return $value;
  112. if (preg_match('/^1[\d]{10}$/', $value)) return $value;
  113. $aec = new Aec(config('app.aec_key'), config('app.aec_iv'));
  114. $value = $aec->decrypt($value);
  115. return $value;
  116. }
  117. /**
  118. * 手机号加密
  119. */
  120. public function setPhone1Attr($value)
  121. {
  122. if (empty($value)) return $value;
  123. $aec = new Aec(config('app.aec_key'), config('app.aec_iv'));
  124. $value = $aec->encrypt($value);
  125. return $value;
  126. }
  127. /**
  128. * 手机号解密
  129. */
  130. public function getPhone2Attr($value)
  131. {
  132. if (empty($value)) return $value;
  133. if (preg_match('/^1[\d]{10}$/', $value)) return $value;
  134. $aec = new Aec(config('app.aec_key'), config('app.aec_iv'));
  135. $value = $aec->decrypt($value);
  136. return $value;
  137. }
  138. /**
  139. * 手机号加密
  140. */
  141. public function setPhone2Attr($value)
  142. {
  143. if (empty($value)) return $value;
  144. $aec = new Aec(config('app.aec_key'), config('app.aec_iv'));
  145. $value = $aec->encrypt($value);
  146. return $value;
  147. }
  148. // protected $json = ['ext'];
  149. private $extAttr = [
  150. 'ext1' => '年龄',
  151. 'ext2' => '是否首次装修',
  152. 'ext3' => '消费能力',
  153. 'ext4' => '预计装修时间',
  154. 'ext5' => '爱好',
  155. 'ext6' => '微信',
  156. 'ext7' => '客户空闲时间段',
  157. 'ext8' => '家庭结构',
  158. 'ext9' => '家庭成员意见',
  159. 'ext10' => '户型',
  160. 'ext11' => '决策人',
  161. 'ext12' => '喜欢色调',
  162. 'ext13' => '房屋结构',
  163. 'ext14' => '装修方式',
  164. 'ext15' => '客户需求',
  165. 'ext16' => '客户痛点',
  166. 'ext17' => '重视环保',
  167. 'ext18' => '重视设计',
  168. 'ext19' => '关注的工艺',
  169. 'ext20' => '特别在意的空间设计',
  170. 'ext21' => '汽车价格',
  171. 'ext22' => '房屋价格',
  172. 'ext23' => '买小区的原因',
  173. 'ext24' => '周边配置',
  174. 'ext25' => '设计师服务满意度',
  175. 'ext26' => '方案满意度',
  176. 'ext27' => '报价满意度',
  177. 'ext28' => '意向程度',
  178. 'ext29' => '是否到店',
  179. 'ext30' => '预计到店时间',
  180. 'ext31' => '是否交定',
  181. 'ext32' => '客户其他需求',
  182. 'ext33' => '当前所在区域',
  183. 'ext34' => '加微类型',
  184. 'ext35' => '建群情况',
  185. 'ext36' => '直播情况',
  186. 'ext37' => '直播人员',
  187. 'ext38' => '房屋用途',
  188. 'ext39' => '房屋位置',
  189. 'ext40' => '其他房屋信息备注',
  190. 'ext41' => '谈单时长',
  191. 'ext42' => '门牌单元号',
  192. 'ext43' => '1对1业务人员',
  193. 'ext44' => '1对1设计人员'
  194. ];
  195. /**
  196. * 客户扩展属性
  197. */
  198. // public function getExtAttr($ext)
  199. // {
  200. // $data = [];
  201. // foreach ($this->extAttr as $k => $v) {
  202. // $data[$v] = isset($ext->$k) ? $ext->$k : '';
  203. // }
  204. // return $data;
  205. // }
  206. //设置下次回访时间去掉日期后面时间
  207. public function getRevisitTimeAttr($value)
  208. {
  209. if (!empty($value)) {
  210. $value = str_replace(' 00:00:00', '', $value);
  211. }
  212. return $value;
  213. }
  214. // public function setExtAttr($ext)
  215. // {
  216. // $data = [];
  217. // foreach ($this->extAttr as $key => $val) {
  218. // $data[$key] = isset($ext[$val]) ? $ext[$val] : '';
  219. // }
  220. // return $data;
  221. // }
  222. /**
  223. * 关联经济人客户记录
  224. */
  225. public function agentLog()
  226. {
  227. return $this->hasMany(AgentCustomerLog::class, 'customer_id', 'id');
  228. }
  229. /**
  230. * 收定金额转换单位
  231. */
  232. public function getDepositMoneyAttr($value)
  233. {
  234. if (empty($value)) return $value;
  235. $length = strlen($value);
  236. if ($length < 5) return $value . '元';
  237. if ($length > 4) {
  238. $num = $value / 10000;
  239. $value = round($num, 2) . '万元';
  240. return $value;
  241. }
  242. }
  243. /**
  244. * 签单金额转换单位
  245. */
  246. public function getSignedMoneyAttr($value)
  247. {
  248. if (empty($value)) return $value;
  249. $length = strlen($value);
  250. if ($length < 5) return $value . '元';
  251. if ($length > 4) {
  252. $num = $value / 10000;
  253. $value = round($num, 2) . '万元';
  254. return $value;
  255. }
  256. }
  257. /**
  258. * 获取预约次数
  259. * @return \think\model\relation\HasMany
  260. */
  261. public function visitLog()
  262. {
  263. return $this->hasMany('CustomerVisitLog', 'customer_id', 'id');
  264. }
  265. /**
  266. * 参加活动次数 确认到场次数
  267. * @return \think\model\relation\HasMany
  268. */
  269. public function activityFrequency()
  270. {
  271. return $this->hasMany('CustomerVisitLog', 'customer_id', 'id');
  272. }
  273. /**
  274. * 关联活动
  275. */
  276. public function activity()
  277. {
  278. return $this->belongsTo(Activity::class, 'aid');
  279. }
  280. /**
  281. * 关联员工
  282. */
  283. public function User()
  284. {
  285. return $this->belongsTo(User::class, 'uid');
  286. }
  287. /**
  288. * 客户产品
  289. */
  290. public function package()
  291. {
  292. return $this->belongsTo(CustomerPackage::class, 'package_id');
  293. }
  294. /**
  295. * 客户的方案资料
  296. */
  297. public function plandata()
  298. {
  299. return $this->belongsTo(DesignerCustomerPlandata::class,'id','customer_id');
  300. }
  301. /**
  302. * 关联经济人
  303. */
  304. public function agent()
  305. {
  306. return $this->hasOne(AgentUser::class, 'id', 'agents_id');
  307. }
  308. /**
  309. * 关联外呼系统客户表
  310. */
  311. public function outcall()
  312. {
  313. return $this->hasMany(OutCallLog::class, 'customer_id', 'id');
  314. }
  315. /** 统一状态 */
  316. public static $states = [
  317. ['n' => 0, 'chaos' => [0, '待确认', '0'], 'state' => '待确认'],
  318. ['n' => 1, 'chaos' => [1, '未到访', '1'], 'state' => '未到访'],
  319. ['n' => 2, 'chaos' => [2, '已到访', '确认到店', '确定到店', '已到店', '2'], 'state' => '已到店'],
  320. ['n' => 3, 'chaos' => [3, '确定到场', '确认到场', '已到场', '3'], 'state' => '已到场'],
  321. ['n' => 4, 'chaos' => [4, '已量房', '确定量房', '确认量房', '4'], 'state' => '已量房'],
  322. ['n' => 5, 'chaos' => [5, '定金', '交定', '已交定', '5'], 'state' => '已交定'],
  323. ['n' => 6, 'chaos' => [6, '签单', '已签单', '6'], 'state' => '已签单'],
  324. ['n' => 7, 'chaos' => [7, '无效', '7'], 'state' => '无效'],
  325. ['n' => 8, 'chaos' => [8, '已卖卡', '8'], 'state' => '已卖卡'],
  326. ];
  327. public function getStateAttr($value)
  328. {
  329. if (is_numeric($value)) $value = intval($value);
  330. foreach (static::$states as $s) {
  331. if ($value === $s['n'] || in_array($value, $s['chaos'], true)) return $s['state'];
  332. }
  333. return '';
  334. }
  335. public function setStateAttr($value)
  336. {
  337. if (is_numeric($value)) $value = intval($value);
  338. foreach (static::$states as $s) {
  339. if ($value === $s['n'] || in_array($value, $s['chaos'], true)) return $s['n'];
  340. }
  341. return 0;
  342. }
  343. /**
  344. * 变更记录中的查询条件
  345. */
  346. public static function changeState($state, $type = 'state')
  347. {
  348. foreach (static::$states as $s) {
  349. if (in_array($state, $s['chaos'], true)) return $s[$type];
  350. }
  351. return $type == 'chaos' ? [] : '';
  352. }
  353. /**
  354. * 改变客户保护期 触发操作:添加、跟踪、分配
  355. * @param $id
  356. * @param int $root_org
  357. */
  358. public static function changeProtectedTo($id, $root_org)
  359. {
  360. // 历史状态有交定状态且没过保护期,保护时间不变
  361. $setting = Setting::where([['name', '=', 'pubpool'], ['root_id', '=', $root_org]])->find();
  362. if (empty($setting)) return;
  363. if (is_array($id)) {
  364. $customer_list = static::where('id', 'in', $id)->select();
  365. // 状态与上方state保持一致
  366. $content = json_decode($setting['content'], true);
  367. if (count($content, 0) == count($content, 1)) return;
  368. $jd_state = static::changeState('交定', 'n');
  369. $jd_state_switch = 0; //交定状态是否开启自动回收 1开启
  370. $jd_state_day = 0; //交定状态保护期天数
  371. foreach ($content as $k => $v) {
  372. if (intval($jd_state) == intval($k) && $v['state'] == 1 && $v['day'] > 0) {
  373. $jd_state_switch = 1;
  374. $jd_state_day = $v['day'];
  375. }
  376. }
  377. foreach ($customer_list as $find) {
  378. foreach ($content as $k => $v) {
  379. $n = static::changeState($find['state'], 'n');
  380. if (intval($k) == intval($n) && $v['state'] == 1 && $v['day'] > 0) {
  381. $jd_visit_state = CustomerVisitLog::changeState('交定', 'chaos');
  382. $jd_visit_log = CustomerVisitLog::where([['state', 'in', $jd_visit_state], ['customer_id', '=', $find['id']]])->order('id desc')->find();
  383. $protected_to = date('Y-m-d H:i:s', time() + intval($v['day']) * 24 * 3600); //默认是当前状态的保护期
  384. if (!empty($jd_visit_log) && $jd_state_switch == 1 && $n != 5) { //有交定,有设置,不是交定
  385. $jd_protected_to = strtotime($jd_visit_log['addtime']) + $jd_state_day * 24 * 3600;
  386. if ($jd_protected_to > time()) {
  387. $protected_to = date('Y-m-d H:i:s', $jd_protected_to); //以交定保护期为主,不更新保护期
  388. }
  389. }
  390. $find->protected_to = $protected_to;
  391. $find->save();
  392. }
  393. }
  394. }
  395. } else {
  396. $find = static::find($id);
  397. // 状态与上方state保持一致
  398. $content = json_decode($setting['content'], true);
  399. if (count($content, 0) == count($content, 1)) return;
  400. $jd_state = static::changeState('交定', 'n');
  401. $jd_state_switch = 0; //交定状态是否开启自动回收 1开启
  402. $jd_state_day = 0; //交定状态保护期天数
  403. foreach ($content as $k => $v) {
  404. if (intval($jd_state) == intval($k) && $v['state'] == 1 && $v['day'] > 0) {
  405. $jd_state_switch = 1;
  406. $jd_state_day = $v['day'];
  407. }
  408. }
  409. foreach ($content as $k => $v) {
  410. $n = static::changeState($find['state'], 'n');
  411. if (intval($k) == intval($n) && $v['state'] == 1 && $v['day'] > 0) {
  412. $jd_visit_state = CustomerVisitLog::changeState('交定', 'chaos');
  413. $jd_visit_log = CustomerVisitLog::where([['state', 'in', $jd_visit_state], ['customer_id', '=', $id]])->order('id desc')->find();
  414. $protected_to = date('Y-m-d H:i:s', time() + intval($v['day']) * 24 * 3600); //默认是当前状态的保护期
  415. if (!empty($jd_visit_log) && $jd_state_switch == 1 && $n != 5) { //有交定,有设置,不是交定
  416. $jd_protected_to = strtotime($jd_visit_log['addtime']) + $jd_state_day * 24 * 3600;
  417. if ($jd_protected_to > time()) {
  418. $protected_to = date('Y-m-d H:i:s', $jd_protected_to); //以交定保护期为主,不更新保护期
  419. }
  420. }
  421. $find->protected_to = $protected_to;
  422. $find->save();
  423. }
  424. }
  425. }
  426. }
  427. /**
  428. * 预计交房时间
  429. */
  430. public function getHouseDeliveryTimeAttr($value)
  431. {
  432. $time = strtotime($value);
  433. if ($time) return date('Y/m/d', $time);
  434. return '';
  435. }
  436. /**
  437. * 预计交房时间
  438. */
  439. public function setHouseDeliveryTimeAttr($value)
  440. {
  441. $time = strtotime($value);
  442. if ($time) return date('Y/m/d', $time);
  443. return null;
  444. }
  445. /**
  446. * 预计装修时间
  447. */
  448. public function getPlanDecoTime($value)
  449. {
  450. $time = strtotime($value);
  451. if ($time) return date('Y/m/d', $time);
  452. return '';
  453. }
  454. /**
  455. * 预计装修时间
  456. */
  457. public function setPlanDecoTime($value)
  458. {
  459. $time = strtotime($value);
  460. if ($time) return date('Y/m/d', $time);
  461. return null;
  462. }
  463. /**
  464. * 更新前操作
  465. */
  466. public static function onBeforeUpdate($customer)
  467. {
  468. Db::name('customer_sharing')->where([['customer_id', '=', $customer->id]])->delete();
  469. }
  470. /**
  471. * 更新后操作
  472. */
  473. public static function onAfterWrite($customer)
  474. {
  475. if (in_array($customer->state, self::changeState('签单', 'chaos')) || in_array($customer->state, self::changeState('无效', 'chaos'))) return;
  476. if (empty($customer->id)) return;
  477. $idList = [];
  478. if (!empty($customer->designer_id)) {
  479. $idList[] = $customer->designer_id;
  480. }
  481. if (!empty($customer->assigned_personnel)) {
  482. $assignedPersonnel = explode(',', $customer->assigned_personnel);
  483. $idList = array_merge($idList, $assignedPersonnel);
  484. }
  485. $path = Org::where('id', $customer->org_id)->value('path');
  486. $orgIdList = explode('-', $path);
  487. $root_id = array_shift($orgIdList);
  488. if (!empty($idList)) {
  489. $idList = array_unique($idList);
  490. foreach ($idList as $i) {
  491. try {
  492. Db::name('customer_sharing')->insert([
  493. 'customer_id' => $customer->id,
  494. 'employee_id' => $i,
  495. 'root_id' => $root_id,
  496. 'addtime' => time()
  497. ]);
  498. } catch (PDOException $e) {
  499. continue;
  500. }
  501. }
  502. }
  503. }
  504. /**
  505. * 删除
  506. */
  507. public static function onAfterDelete($customer)
  508. {
  509. Db::name('customer_sharing')->where([['customer_id', '=', $customer->id]])->delete();
  510. }
  511. /**
  512. * 关联部门
  513. */
  514. public function orgs()
  515. {
  516. return $this->belongsTo(Org::class, 'org_id');
  517. }
  518. }