CrmImport.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. <?php
  2. namespace app\jobs;
  3. use app\model\CrmImportLog;
  4. use app\model\CrmImportRecord;
  5. use app\model\Customer;
  6. use app\model\CustomerPortraitField;
  7. use cutExcel\MyreadFilter;
  8. use OSS\Core\OssException;
  9. use PHPExcel_Shared_Date;
  10. use PhpOffice\PhpSpreadsheet\IOFactory;
  11. use think\facade\Db;
  12. use think\facade\Log;
  13. use think\queue\Job;
  14. use think\facade\Queue;
  15. use toolkits\Aec;
  16. class CrmImport
  17. {
  18. public function fire(Job $job, $queuedata)
  19. {
  20. $delete = false; // 失败
  21. try {
  22. if ($job->attempts() > 2) {
  23. $job->delete();
  24. $delete = true;
  25. }
  26. $is_done = $this->jobDone($queuedata);
  27. if($is_done){
  28. $job->delete();
  29. } else {
  30. $job->delete();
  31. $delete = true;
  32. }
  33. } catch (OssException $e) {
  34. //报错直接结束
  35. $job->delete();
  36. $delete = true;
  37. }
  38. if ($delete) {
  39. // 导入过程失败
  40. $log = CrmImportLog::where('id', $queuedata['id'])->find();
  41. $tmp_file = $log['tmp_path'];
  42. if (file_exists($tmp_file)) {
  43. unlink($tmp_file);
  44. }
  45. $log->tmp_path = '';
  46. $log->status = 3;
  47. $log->save();
  48. }
  49. }
  50. public function failed($queuedata)
  51. {
  52. // ...任务达到最大重试次数后,失败了
  53. }
  54. // 导入
  55. private function jobDone($data){
  56. $log_id = $data['id'];
  57. $log = CrmImportLog::where('id', $log_id)->findOrEmpty();
  58. if ($log->isEmpty()) {
  59. return false;
  60. }
  61. if ($log['status'] == 2) {
  62. return true;
  63. }
  64. if (empty($log['tmp_path'])) {
  65. // 开始导入
  66. $ali_oss_bindurl = config('app.ali_oss_bindurl');
  67. $path = 'https://' . $ali_oss_bindurl . '/' . $log['path'];
  68. // 抓取远程Excel文件的内容
  69. try {
  70. $content = file_get_contents($path);
  71. if (!$content) {
  72. return false;
  73. }
  74. } catch (\Exception $e) {
  75. return false;
  76. }
  77. // 保存Excel文件到本地临时文件
  78. $run_path = runtime_path();
  79. $tmp_file = tempnam($run_path, 'PHPExcel');
  80. file_put_contents($tmp_file, $content);
  81. $log->tmp_path = $tmp_file;
  82. $log->save();
  83. } else {
  84. $tmp_file = $log['tmp_path'];
  85. }
  86. //实例化PHPExcel类
  87. $reader = IOFactory::createReader('Xlsx');
  88. if (!$reader->canRead($tmp_file)) {
  89. Log::record('-------crm import error------')->save();
  90. Log::record('file canRead false')->save();
  91. Log::record('-------crm import error------')->save();
  92. return false;
  93. }
  94. $rows = 100;
  95. if ($log['execute_rows'] == 0) {
  96. $start_rows = 1;
  97. } else {
  98. $start_rows = $log['execute_rows'];
  99. }
  100. $end_rows = $start_rows + $rows;
  101. $MyReadFilter = new MyreadFilter($start_rows, $end_rows);
  102. $reader->setReadFilter($MyReadFilter);
  103. $reader->setReadDataOnly(true);
  104. $objPHPExcel = $reader->load($tmp_file);
  105. $sheet = $objPHPExcel->getSheet(0); //excel中的第一张sheet
  106. $highestRow = $sheet->getHighestRow(); // 取得总行数
  107. $highestColumn = $sheet->getHighestColumn(); // 取得总列数
  108. \PhpOffice\PhpSpreadsheet\Cell\Coordinate::columnIndexFromString($highestColumn);
  109. $lines = $highestRow - 1;
  110. $end = true;
  111. $root_id = $log['root_id'];
  112. if ($lines > 0) {
  113. //客户来源扩展字段
  114. $datas = [];
  115. $j = $start_rows + 1;
  116. for ($j; $j <= $highestRow; $j++) {
  117. $date_check = ['I'];
  118. $date_value = [];
  119. foreach ($date_check as $v) {
  120. try {
  121. $get_value = $objPHPExcel->getActiveSheet()->getCell($v . $j)->getValue();
  122. if (empty($get_value)) {
  123. $date_value[$v] = '';
  124. continue;
  125. }
  126. if ($v !== "I") {
  127. $date_value[$v] = gmdate('Y-m-d H:i:s', PHPExcel_Shared_Date::ExcelToPHP($get_value));
  128. } else {
  129. $date_value[$v] = gmdate('Y-m-d', PHPExcel_Shared_Date::ExcelToPHP($get_value));
  130. }
  131. } catch (\Exception $e) {
  132. $date_value[$v] = '';
  133. }
  134. }
  135. $datas[] = [
  136. 'name' => trim($objPHPExcel->getActiveSheet()->getCell("A" . $j)->getValue()),
  137. 'sex' => trim($objPHPExcel->getActiveSheet()->getCell("B" . $j)->getValue()),
  138. 'phone' => trim($objPHPExcel->getActiveSheet()->getCell("C" . $j)->getValue()),
  139. 'level' => trim($objPHPExcel->getActiveSheet()->getCell("D" . $j)->getValue()),
  140. 'community_name' => trim($objPHPExcel->getActiveSheet()->getCell("E" . $j)->getValue()),
  141. 'square' => trim($objPHPExcel->getActiveSheet()->getCell("F" . $j)->getValue()),
  142. 'housetype_arrow' => trim($objPHPExcel->getActiveSheet()->getCell("G" . $j)->getValue()),
  143. 'follow' => trim($objPHPExcel->getActiveSheet()->getCell("H" . $j)->getValue()),
  144. 'house_delivery_time' => !empty($date_value["I"]) ? $date_value["I"] : date('Y-m-d'),
  145. 'age_range' => trim($objPHPExcel->getActiveSheet()->getCell("J" . $j)->getValue()),
  146. 'unit_number' => trim($objPHPExcel->getActiveSheet()->getCell("K" . $j)->getValue()),
  147. 'house_location' => trim($objPHPExcel->getActiveSheet()->getCell("L" . $j)->getValue()),
  148. 'row'=> $j
  149. ];
  150. }
  151. //error 手机号错误数量 repeat 手机号重复数量
  152. $rs = ['ok' => 0, 'error' => 0, 'repeat' => 0];
  153. $error = [];
  154. $portrait_field = CustomerPortraitField::with('select')->where([['root_id', '=', $root_id], ['keyname', 'in', ['housetype_arrow', 'follow', 'age_range', 'unit_number', 'house_location']]])->select()->toArray();
  155. $save_data = [];
  156. $have_phone = []; //已经导入过的手机号
  157. $have_phone_search = Customer::where([['crm_res_id', '=', $log_id]])->column('phone,phone1,phone2');
  158. if (!empty($have_phone_search)) {
  159. foreach ($have_phone_search as $k => $v) {
  160. if (!empty($v['phone'])) {
  161. $have_phone[] = $v['phone'];
  162. }
  163. if (!empty($v['phone1'])) {
  164. $have_phone[] = $v['phone1'];
  165. }
  166. if (!empty($v['phone2'])) {
  167. $have_phone[] = $v['phone2'];
  168. }
  169. }
  170. }
  171. $aec = new Aec(config('app.aec_key'), config('app.aec_iv'));
  172. foreach ($datas as $k => $v) {
  173. $one_cus = [];
  174. $one_cus['phone1'] = $one_cus['phone2'] = '';
  175. ////2023-02-15 修改手机号可以输入多个
  176. $s = true;
  177. $ls_phone = explode('、', $v['phone']);
  178. if(!empty($ls_phone) && is_array($ls_phone)){
  179. $p = 0;
  180. foreach ($ls_phone as $k2 => $v2) {
  181. if ($this->checkphone($v2)) {
  182. $field = $p ? 'phone'.$p : 'phone';
  183. $one_cus[$field] = $aec->encrypt($v2);
  184. $p+=1;
  185. }
  186. if($p>2) break;//只取三个有效手机号
  187. }
  188. if($p>0) $s = false;
  189. }
  190. if($s){
  191. $rs['error']++;
  192. $one_error['rows'] = $v['row'];
  193. $one_error['type'] = 1; // 1、手机号格式错误 2、手机号重复
  194. $one_error['phone'] = $v['phone'];
  195. $error[] = $one_error;
  196. continue;
  197. }
  198. // 手机号验重
  199. $one_phone = array_values(array_filter([$one_cus['phone'], $one_cus['phone1'], $one_cus['phone2']]));
  200. if (!empty($have_phone)) {
  201. $is_repeat = array_intersect($have_phone, $one_phone);
  202. if (!empty($is_repeat)) {
  203. $rs['error']++;
  204. $one_error['rows'] = $v['row'];
  205. $one_error['type'] = 2; // 1、手机号格式错误 2、手机号重复
  206. $one_error['phone'] = $v['phone'];
  207. $error[] = $one_error;
  208. continue;
  209. }
  210. $have_phone = array_merge($have_phone, $one_phone);
  211. } else {
  212. $have_phone = $one_phone;
  213. }
  214. // 数据处理
  215. $one_cus['name'] = $v['name'];
  216. $one_cus['sex'] = ($v['sex'] == '男') ? 1 : 2;
  217. $one_cus['level'] = in_array($v['level'], ['A', 'B', 'C', 'D']) ? $v['level'] : '';
  218. $one_cus['square'] = $v['square'];
  219. $one_cus['community_name'] = $v['community_name'];
  220. $one_cus['state'] = 0;
  221. $one_cus['org_id'] = $log['org_id'];
  222. $one_cus['bad_phone'] = 0;
  223. $one_cus['source_id'] = $log['source'];
  224. $one_cus['house_type'] = mb_strlen($v['housetype_arrow']) > 20 ? mb_substr($v['housetype_arrow'], 0, 20) : trim($v['housetype_arrow']);
  225. $one_cus['is_resource'] = 1;
  226. $one_cus['crm_res_id'] = $log_id;
  227. $ext = [];
  228. // 扩展字段 户型、装修关注点、是否加微
  229. foreach ($portrait_field as $kk => $vv) {
  230. switch ($vv['keyname']) {
  231. case 'housetype_arrow':
  232. $house_type_val = 0;
  233. if (!empty($vv['select'])) {
  234. foreach ($vv['select'] as $vvv) {
  235. if ($vvv['name'] == $v['housetype_arrow']) {
  236. $house_type_val = $vvv['id'];
  237. }
  238. }
  239. }
  240. if ($house_type_val) {
  241. $ext[] = ['id'=> $vv['id'], 'keyname'=> 'housetype_arrow', 'value'=> (string)$house_type_val];
  242. }
  243. break;
  244. case 'age_range':
  245. if (!empty($vv['select'])) {
  246. foreach ($vv['select'] as $vvv) {
  247. if ($vvv['name'] == $v['age_range']) {
  248. $ext[] = ['id'=> $vv['id'], 'keyname'=> 'age_range', 'value'=> $vvv['id']];
  249. $one_cus['age_range'] = $vvv['id'];
  250. }
  251. }
  252. }
  253. break;
  254. case 'follow':
  255. $follow_list = explode('、', $v['follow']);
  256. $follow_ids = [];
  257. if (!empty($vv['select'])) {
  258. foreach ($vv['select'] as $vvv) {
  259. if (in_array($vvv['name'], $follow_list)) {
  260. $follow_ids[] = $vvv['id'];
  261. }
  262. }
  263. }
  264. if (!empty($follow_ids)) {
  265. $ext[] = ['id'=> $vv['id'], 'keyname'=> 'follow', 'value'=> implode(',', $follow_ids)];
  266. }
  267. break;
  268. case 'unit_number':
  269. if (!empty($v['unit_number'])) {
  270. $ext[] = ['id'=> $vv['id'], 'keyname'=> 'unit_number', 'value'=> (string)$v['unit_number']];
  271. }
  272. break;
  273. case 'house_location':
  274. if (!empty($v['house_location'])) {
  275. $ext[] = ['id'=> $vv['id'], 'keyname'=> 'house_location', 'value'=> (string)$v['house_location']];
  276. }
  277. break;
  278. default:
  279. break;
  280. }
  281. }
  282. $one_cus['ext'] = $ext ? json_encode($ext) : NULL;
  283. $save_data[] = $one_cus;
  284. $rs['ok']++;
  285. }
  286. Db::startTrans();
  287. try {
  288. foreach ($save_data as $k => $v) {
  289. Customer::insert($v);
  290. }
  291. Db::commit();
  292. } catch (\Exception $e) {
  293. trace($e->getMessage(), 'error');
  294. // 回滚事务
  295. Db::rollback();
  296. return false;
  297. }
  298. // 执行记录
  299. $excute_log['log_id'] = $log_id;
  300. $excute_log['employee_id'] = $log['employee_id'];
  301. $excute_log['start_rows'] = $start_rows;
  302. $excute_log['end_rows'] = $end_rows;
  303. $excute_log['error_count'] = count($error);
  304. $excute_log['content'] = $error ? json_encode($error) : '';
  305. $excute_log['root_id'] = $root_id;
  306. CrmImportRecord::create($excute_log);
  307. if ($log['status'] == 0) {
  308. $log->status = 1;
  309. }
  310. $log->left_num = $log['left_num'] + $rs['ok'];
  311. $log->count = $log['count'] + count($datas);
  312. $log->avaliable_count = $log['avaliable_count'] + $rs['ok'];
  313. $log->bad_phone_num = $log['bad_phone_num'] + $rs['error'];
  314. $log->execute_rows = $highestRow;
  315. $log->save();
  316. if (count($datas) > 0) {
  317. $end = false;
  318. } else {
  319. $log->tmp_path = '';
  320. $log->status = 2;
  321. $log->save();
  322. if (file_exists($tmp_file)) {
  323. unlink($tmp_file);
  324. }
  325. }
  326. } else {
  327. $log->tmp_path = '';
  328. $log->status = 2;
  329. $log->save();
  330. if (file_exists($tmp_file)) {
  331. unlink($tmp_file);
  332. }
  333. }
  334. if($lines == $rows) {
  335. $end = false;
  336. }
  337. if (!$end) {
  338. $jobHandlerClassName = 'app\jobs\CrmImport';
  339. $jobQueueName = 'crm_import';
  340. $orderData = ['id'=> $log_id]; //这个是需要传到消费者的数据
  341. Queue::later(0, $jobHandlerClassName, $orderData, $jobQueueName);
  342. }
  343. return true;
  344. }
  345. private function checkphone($value)
  346. {
  347. if (preg_match("/^1[356789]\d{9}$/", $value)) {
  348. return true;
  349. } else {
  350. return false;
  351. }
  352. }
  353. }