123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | Description: 自定义字段模块数据Excel导入导出
  4. // +----------------------------------------------------------------------
  5. // | Author: Michael_xu | gengxiaoxu@5kcrm.com
  6. // +----------------------------------------------------------------------
  7. namespace app\admin\model;
  8. use app\admin\model\Common;
  9. use app\admin\model\Message;
  10. use app\work\model\Task;
  11. use com\PseudoQueue as Queue;
  12. use think\Cache;
  13. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  14. use think\cache\driver\Redis;
  15. class Excel extends Common
  16. {
  17. /**
  18. * 支持自定义字段的表,不包含表前缀
  19. *
  20. * @var array
  21. */
  22. private $types_arr = [
  23. 'crm_leads',
  24. 'crm_customer',
  25. 'crm_contacts',
  26. 'crm_product',
  27. 'crm_contract',
  28. 'crm_business',
  29. 'admin_user',
  30. 'task'
  31. ];
  32. /**
  33. * 字段类型为 map_address 的地址类型字段,导入导出时占四个字段,四个单元格
  34. */
  35. private $map_address = ['省', '市', '区/县', '详细地址'];
  36. /**
  37. * 导入锁缓存名称
  38. */
  39. const IMPORT_QUEUE = DB_NAME . 'IMPORT_QUEUE';
  40. /**
  41. * 导出锁缓存名称
  42. */
  43. const EXPORT_QUEUE = DB_NAME . 'EXPORT_QUEUE';
  44. /**
  45. *获取excel相关列
  46. **/
  47. public function stringFromColumnIndex($pColumnIndex = 0)
  48. {
  49. static $_indexCache = array();
  50. if (!isset($_indexCache[$pColumnIndex])) {
  51. if ($pColumnIndex < 26) {
  52. $_indexCache[$pColumnIndex] = chr(65 + $pColumnIndex);
  53. } elseif ($pColumnIndex < 702) {
  54. $_indexCache[$pColumnIndex] = chr(64 + ($pColumnIndex / 26)) . chr(65 + $pColumnIndex % 26);
  55. } else {
  56. $_indexCache[$pColumnIndex] = chr(64 + (($pColumnIndex - 26) / 676)) . chr(65 + ((($pColumnIndex - 26) % 676) / 26)) . chr(65 + $pColumnIndex % 26);
  57. }
  58. }
  59. return $_indexCache[$pColumnIndex];
  60. }
  61. /**
  62. * 自定义字段模块导入模板下载
  63. * @param $field_list 自定义字段数据
  64. * @param $types 分类
  65. * @author
  66. **/
  67. public function excelImportDownload($field_list, $types, $save_path = '')
  68. {
  69. $fieldModel = new \app\admin\model\Field();
  70. //实例化主文件
  71. $objPHPExcel = new Spreadsheet();
  72. $objProps = $objPHPExcel->getProperties(); // 设置excel文档的属性
  73. $objProps->setCreator("5kcrm"); //创建人
  74. $objProps->setLastModifiedBy("5kcrm"); //最后修改人
  75. $objProps->setTitle("5kcrm"); //标题
  76. $objProps->setSubject("5kcrm data"); //题目
  77. $objProps->setDescription("5kcrm data"); //描述
  78. $objProps->setKeywords("5kcrm data"); //关键字
  79. $objProps->setCategory("5kcrm"); //种类
  80. $objPHPExcel->setActiveSheetIndex(0); //设置当前的sheet
  81. $objActSheet = $objPHPExcel->getActiveSheet();
  82. $objActSheet->setTitle('导入模板' . date('Y-m-d', time())); //设置sheet的标题
  83. //存储Excel数据源到其他工作薄
  84. $objPHPExcel->createSheet();
  85. $subObject = $objPHPExcel->getSheet(1);
  86. $subObject->setTitle('data');
  87. //保护数据源
  88. $subObject->getProtection()->setSheet(true);
  89. $subObject->protectCells('A1:C1000', time());
  90. //填充边框
  91. $styleArray = [
  92. 'borders' => [
  93. 'outline' => [
  94. 'style' => \PHPExcel_Style_Border::BORDER_THICK, //设置边框
  95. 'color' => ['argb' => '#F0F8FF'], //设置颜色
  96. ],
  97. ],
  98. ];
  99. if ($save_path) {
  100. $objActSheet->setCellValue('A2', '错误原因(导入时需删除本列)');
  101. $objActSheet->getColumnDimension('A')->setWidth(40); //设置单元格宽度
  102. $k = 1;
  103. } else {
  104. $k = 0;
  105. }
  106. $objActSheet->getColumnDimension('A1')->setWidth(70);
  107. foreach ($field_list as $field) {
  108. if ($field['form_type'] == 'map_address' && $types == 'crm_customer') {
  109. for ($a = 0; $a <= 3; $a++) {
  110. $objActSheet->getColumnDimension($this->stringFromColumnIndex($k))->setWidth(20); //设置单元格宽度
  111. //如果是所在省的话
  112. $objActSheet->setCellValue($this->stringFromColumnIndex($k) . '2', $this->map_address[$a]);
  113. $k++;
  114. }
  115. } else {
  116. $objActSheet->getColumnDimension($this->stringFromColumnIndex($k))->setWidth(20); //设置单元格宽度
  117. if ($field['form_type'] == 'select' || $field['form_type'] == 'checkbox' || $field['form_type'] == 'radio' || $field['form_type'] == 'category' || $field['form_type'] == 'user') {
  118. //产品类别
  119. if ($field['form_type'] == 'category' && $field['types'] == 'crm_product') {
  120. $setting = db('crm_product_category')->order('pid asc')->column('name');
  121. } elseif ($field['form_type'] == 'user' && ($field['field'] == 'owner_user_id' || $field['field'] == 'create_user_id' || $field['field'] == 'before_owner_user_id')) {
  122. $setting = db('admin_user')->order('id asc')->column('realname');
  123. } else {
  124. $setting = $field['setting'] ?: [];
  125. }
  126. $select_value = implode(',', $setting);
  127. //解决下拉框数据来源字串长度过大:将每个来源字串分解到一个空闲的单元格中
  128. $str_len = strlen($select_value);
  129. $selectList = array();
  130. if ($str_len >= 255) {
  131. $str_list_arr = explode(',', $select_value);
  132. if ($str_list_arr) {
  133. foreach ($str_list_arr as $i1 => $d) {
  134. $c = $this->stringFromColumnIndex($k) . ($i1 + 1);
  135. $subObject->setCellValue($c, $d);
  136. $selectList[$d] = $d;
  137. }
  138. $endcell = $c;
  139. }
  140. for ($j = 3; $j <= 70; $j++) {
  141. $objActSheet->getStyle($this->stringFromColumnIndex($k) . $j)->getFont()->setName("宋体")->setSize(11)->getColor()->setARGB('#00000000');
  142. $objActSheet->getStyle($this->stringFromColumnIndex($k) . $j)->getNumberFormat()->setFormatCode(\PHPExcel_Style_NumberFormat::FORMAT_TEXT);//设置单元格格式 (文本)
  143. //数据有效性 start
  144. $objValidation = $objActSheet->getCell($this->stringFromColumnIndex($k) . $j)->getDataValidation();
  145. $objValidation->setType(\PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_LIST)
  146. ->setErrorStyle(\PhpOffice\PhpSpreadsheet\Cell\DataValidation::STYLE_INFORMATION)
  147. ->setAllowBlank(false)
  148. ->setShowInputMessage(true)
  149. ->setShowErrorMessage(true)
  150. ->setShowDropDown(true)
  151. ->setErrorTitle('输入的值有误')
  152. ->setError('您输入的值不在下拉框列表内.')
  153. ->setPromptTitle('--请选择--')
  154. ->setFormula1('data!$' . $this->stringFromColumnIndex($k) . '$1:$' . $this->stringFromColumnIndex($k) . '$' . count(explode(',', $select_value)));
  155. //数据有效性 end
  156. }
  157. } else {
  158. if ($select_value) {
  159. for ($j = 3; $j <= 70; $j++) {
  160. $objActSheet->getStyle($this->stringFromColumnIndex($k) . $j)->getFont()->setName("宋体")->setSize(11)->getColor()->setARGB('#00000000');
  161. $objActSheet->getStyle($this->stringFromColumnIndex($k) . $j)->getNumberFormat()->setFormatCode(\PHPExcel_Style_NumberFormat::FORMAT_TEXT);//设置单元格格式 (文本)
  162. //数据有效性 start
  163. $objValidation = $objActSheet->getCell($this->stringFromColumnIndex($k) . $j)->getDataValidation();
  164. $objValidation->setType(\PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_LIST)
  165. ->setErrorStyle(\PhpOffice\PhpSpreadsheet\Cell\DataValidation::STYLE_INFORMATION)
  166. ->setAllowBlank(false)
  167. ->setShowInputMessage(true)
  168. ->setShowErrorMessage(true)
  169. ->setShowDropDown(true)
  170. ->setErrorTitle('输入的值有误')
  171. ->setError('您输入的值不在下拉框列表内.')
  172. ->setPromptTitle('--请选择--')
  173. ->setFormula1('"' . $select_value . '"');
  174. //数据有效性 end
  175. }
  176. }
  177. }
  178. }
  179. $objActSheet->getStyle($this->stringFromColumnIndex($k))->getNumberFormat()->setFormatCode(\PHPExcel_Style_NumberFormat::FORMAT_TEXT);//设置单元格格式 (文本)
  180. //检查该字段若必填,加上"*"
  181. $field['name'] = sign_required($field['is_null'], $field['name']);
  182. $objActSheet->setCellValue($this->stringFromColumnIndex($k) . '2', $field['name']);
  183. if (strstr($field['name'], '*')) {
  184. $objActSheet->getStyle($this->stringFromColumnIndex($k) . '2')->getFont()->getColor()->setARGB('#FF0000');
  185. }
  186. $k++;
  187. }
  188. }
  189. $max_row = $this->stringFromColumnIndex($k - 1);
  190. $mark_row = $this->stringFromColumnIndex($k);
  191. $objActSheet->mergeCells('A1:' . $max_row . '1');
  192. $objActSheet->getStyle('A1')->getFont()->getColor()->setARGB('FFFF0000');
  193. $objActSheet->getStyle('A2:' . $max_row . '2')->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER); //
  194. //设置单元格格式范围的字体、字体大小、加粗
  195. $objActSheet->getStyle('A1:' . $max_row . '1')->getFont()->setName("宋体")->setSize(11)->getColor()->setARGB('#00000000');
  196. //给单元格填充背景色
  197. // $objActSheet->getStyle('A1:' . $max_row . '1')->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB('#ff9900');
  198. $objActSheet->getStyle('A1:' . $max_row . '1')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT); //水平居中
  199. $objActSheet->getStyle('A1:' . $max_row . '1')->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT); //垂直居中
  200. $objActSheet->getRowDimension(1)->setRowHeight(100); //设置行高
  201. $objActSheet->getRowDimension(2)->setRowHeight(30); //设置行高
  202. $objActSheet->getStyle('A1')->getAlignment()->setWrapText(true);
  203. switch ($types) {
  204. case 'crm_leads' :
  205. $types_name = '线索信息';
  206. $type_name = 'leads';
  207. break;
  208. case 'crm_customer' :
  209. $types_name = '客户信息';
  210. $type_name = 'customer';
  211. break;
  212. case 'crm_contacts' :
  213. $types_name = '联系人信息';
  214. $type_name = 'contacts';
  215. break;
  216. case 'crm_product' :
  217. $types_name = '产品信息';
  218. $type_name = 'product';
  219. break;
  220. case 'crm_business' :
  221. $types_name = '商机信息';
  222. $type_name = 'business';
  223. break;
  224. case 'crm_contract' :
  225. $types_name = '合同信息';
  226. $type_name = 'contract';
  227. break;
  228. case 'crm_receivables' :
  229. $types_name = '回款信息';
  230. $type_name = 'receivables';
  231. break;
  232. case 'admin_user' :
  233. $types_name = '员工信息';
  234. $type_name = 'user';
  235. break;
  236. case 'work_task' :
  237. $types_name = '任务信息';
  238. $type_name = 'task';
  239. break;
  240. case 'crm_pool' :
  241. $types_name = '公海信息';
  242. $type_name = 'pool';
  243. break;
  244. case 'crm_activity' :
  245. $types_name = '跟进记录';
  246. $type_name = 'activity';
  247. break;
  248. default :
  249. $types_name = '悟空软件';
  250. $type_name = 'WuKong';
  251. break;
  252. }
  253. //内容设置
  254. $content='';
  255. if ($types == 'admin_user') {
  256. $content ="注意事项:\n1、表头标“*”的红色字体为必填项\n2、手机号:目前只支持中国大陆的11位手机号码;且手机号不允许重复\n3、登录密码:密码由6-20位字母、数字组成\n4、部门:上下级部门间用“/”隔开,且从最上级部门开始,例如“上海分公司/市场部/市场一部”。如出现相同的部门,则默认导入组织架构中顺序靠前的部门";
  257. } elseif($types == 'crm_activity'){
  258. $content = "注意事项:\n1、表头标“*”的红色字体为必填项\n2、跟进时间:推荐格式为2020-2-1\n3、若相关数据有多条时用“/”区分例如:杭州科技有限公司/卡卡罗特软件科技有限公司\n4、所属客户中的客户需要存在系统中,且填写的所属客户名称与系统中的客户名称必须保持一致否则会导入失败\n5、创建人为系统员工,请填写系统员工“姓名”,若匹配不到系统员工,则会导致导入失败\n6、如果系统中存在多个名称重复的情况,会默认导入到最新的数据中";
  259. }elseif($types == 'crm_pool'){
  260. $content = "注意事项:\n1、表头标“*”的红色字体为必填项\n2、日期时间:推荐格式为2020-02-02 13:13:13\n3、日期:推荐格式为2020-02-02\n4、手机号:支持6-15位数字(包含国外手机号格式)\n5、邮箱:只支持邮箱格式\n6、多行文本:字数限制为800字\n7、负责人不必填,若填写负责人,则数据将进入客户模块,否则进入公海模块";
  261. }else{
  262. $content = "注意事项:\n1、表头标“*”的红色字体为必填项\n2、日期时间:推荐格式为2020-02-02 13:13:13\n3、日期:推荐格式为2020-02-02\n4、手机号:支持6-15位数字(包含国外手机号格式)\n5、邮箱:只支持邮箱格式\n6、多行文本:字数限制为800字";
  263. }
  264. $objActSheet->setCellValue('A1', $content);
  265. //设置A1单元格内容为
  266. $objActSheet->getStyle('A1')->getAlignment()->setWrapText(true);//合并单元格换行
  267. $objWriter = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($objPHPExcel, 'Xls');
  268. ob_end_clean();
  269. if ($save_path) {
  270. $objWriter->save($save_path);
  271. } else {
  272. header("Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  273. header("Content-Disposition:attachment;filename=" . $type_name . '_' . date('Y-m-d') . ".xls");
  274. header("Pragma:no-cache");
  275. header("Expires:0");
  276. $objWriter = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($objPHPExcel, 'Xls');
  277. $objWriter->save('php://output');
  278. }
  279. }
  280. /**
  281. * 自定义字段模块导出csv
  282. * @param $file_name 导出文件名称
  283. * @param $field_list 导出字段列表
  284. * @param $callback 回调函数,查询需要导出的数据
  285. * @author
  286. **/
  287. public function exportCsv($file_name, $field_list, $callback)
  288. {
  289. $fieldModel = new \app\admin\model\Field();
  290. ini_set('memory_limit', '1024M');
  291. ini_set('max_execution_time', '300');
  292. // set_time_limit(0);
  293. //调试时,先把下面这个两个header注释即可
  294. header("Access-Control-Expose-Headers: Content-Disposition");
  295. header("Content-type:application/vnd.ms-excel;charset=UTF-8");
  296. header("Content-Disposition:attachment;filename=" . $file_name . ".csv");
  297. header('Expires: 0');
  298. header('Cache-control: private');
  299. header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
  300. header('Content-Description: File Transfer');
  301. header('Content-Encoding: UTF-8');
  302. // 加上bom头,防止用office打开时乱码
  303. echo "\xEF\xBB\xBF"; // UTF-8 BOM
  304. // 打开PHP文件句柄,php://output 表示直接输出到浏览器
  305. $fp = fopen('php://output', 'a');
  306. // 将中文标题转换编码,否则乱码
  307. foreach ($field_list as $i => $v) {
  308. $title_cell[$i] = $v['name'];
  309. }
  310. // 将标题名称通过fputcsv写到文件句柄
  311. fputcsv($fp, $title_cell);
  312. // $export_data = $callback(0);
  313. $round = round(1000, 9999);
  314. Cache::set($file_name . $round, $callback['list'], config('export_cache_time'));
  315. $sheetContent = cache($file_name . $round);
  316. $sheetCount = $callback['dataCount'];
  317. $forCount = 1000; //每次取出1000个
  318. for ($i = 0; $i <= ceil(round($sheetCount / $forCount, 2)); $i++) {
  319. $_sub = array_slice($sheetContent, ($i) * $forCount, 1000);
  320. foreach ($_sub as $kk => $item) {
  321. $rows = [];
  322. foreach ($field_list as $key => $rule) {
  323. $rows[] = ($key = $item[$key]);
  324. }
  325. fputcsv($fp, $rows);
  326. }
  327. ob_flush();//清除内存
  328. flush();
  329. }
  330. // 将已经写到csv中的数据存储变量销毁,释放内存占用
  331. //$m = memory_get_usage();
  332. Cache::rm($file_name . $round);
  333. ob_flush();
  334. flush();
  335. fclose($fp);
  336. exit();
  337. }
  338. /**
  339. * 分批导出csv
  340. *
  341. * @param string $file_name 下载文件名称
  342. * @param string $temp_file 临时文件名称 (不带 .csv 后缀)
  343. * @param array $field_list 字段表头
  344. * @param int $page 响应页码默认1
  345. * @param callback $callback 回调函数,返回数据
  346. * ($page, $page_size) 查询页码,查询数量
  347. * @param array $config 设置信息
  348. * - response_size 单次请求响应数量 默认2000
  349. * - page_size 单次数据库查询数量 默认 500
  350. * @author Ymob
  351. */
  352. public function batchExportCsv($file_name, $temp_file, $field_list, $page, $callback, $config = [])
  353. {
  354. $queue = new Queue(self::EXPORT_QUEUE, 3);
  355. $export_queue_index = input('export_queue_index');
  356. if (!$export_queue_index) {
  357. if (!$export_queue_index = $queue->makeTaskId()) {
  358. return resultArray(['error' => $queue->error]);
  359. }
  360. } else {
  361. if (!$queue->setTaskId($export_queue_index)) {
  362. return resultArray(['error' => $queue->error]);
  363. }
  364. }
  365. // 已取消
  366. if ($page == -1) {
  367. $queue->dequeue();
  368. return resultArray([
  369. 'data' => [
  370. 'msg' => '导出已取消',
  371. 'page' => -1
  372. ]
  373. ]);
  374. }
  375. // 排队中
  376. if (!$queue->canExec()) {
  377. return resultArray([
  378. 'data' => [
  379. 'page' => -2,
  380. 'export_queue_index' => $export_queue_index,
  381. 'info' => $queue->error
  382. ]
  383. ]);
  384. }
  385. // 没有临时文件名,代表第一次导出,生成临时文件名称,并写入表头数据
  386. if ($temp_file === null) {
  387. // 生成临时文件路径
  388. $file_path = tempFileName('csv');
  389. $fp = fopen($file_path, 'a');
  390. $title_cell = [];
  391. foreach ($field_list as $v) {
  392. if ($v['form_type'] == 'customer_address') {
  393. $title_cell[] = $this->map_address[0];
  394. $title_cell[] = $this->map_address[1];
  395. $title_cell[] = $this->map_address[2];
  396. } else {
  397. $title_cell[] = $v['name'];
  398. }
  399. }
  400. fputcsv($fp, $title_cell);
  401. $temp_file = \substr($file_path, strlen(TEMP_DIR));
  402. } else {
  403. $file_path = TEMP_DIR . $temp_file;
  404. if (!file_exists($file_path)) {
  405. return resultArray(['error' => '参数错误,临时文件不存在']);
  406. }
  407. $fp = fopen($file_path, 'a');
  408. }
  409. // 自定义字段模型
  410. $fieldModel = new \app\admin\model\Field();
  411. // 单次响应条数 (必须是单次查询条数的整数倍)
  412. $response_size = $config['response_size'] ?: 10000;
  413. // 单次查询条数
  414. $page_size = $config['page_size'] ?: 200;
  415. // 最多查询次数
  416. $max_query_count = $response_size / $page_size;
  417. // 总数
  418. $total = 0;
  419. for ($i = 1; $i <= $max_query_count; $i++) {
  420. // 两个参数,第一个参数是 page (传入 model\customer::getDataList 方法的参数),
  421. $data = $callback($i + ($page - 1) * ($response_size / $page_size), $page_size);
  422. $total = $data['dataCount'];
  423. foreach ($data['list'] as $val) {
  424. $rows = [];
  425. foreach ($field_list as $rule) {
  426. if ($rule['form_type'] == 'customer_address') {
  427. $address_arr = explode(chr(10), $val['address']);
  428. $rows[] = $address_arr[0] ?: '';
  429. $rows[] = $address_arr[1] ?: '';
  430. $rows[] = $address_arr[2] ?: '';
  431. } else {
  432. if (is_numeric($val[$rule['field']]) && strlen($val[$rule['field']]) > 15) {
  433. $val[$rule['field']] = "\t" . $val[$rule['field']] . "\t";
  434. }
  435. $rows[] = $fieldModel->getValueByFormtype($val[$rule['field']], $rule['form_type'],$val);
  436. }
  437. }
  438. fputcsv($fp, $rows);
  439. }
  440. }
  441. fclose($fp);
  442. // 已查询数据条数 小于 数据总数
  443. $done = $page * $response_size;
  444. if ($done < $total) {
  445. return resultArray([
  446. 'data' => [
  447. 'export_queue_index' => $export_queue_index,
  448. 'temp_file' => $temp_file,
  449. // 总数
  450. 'total' => $total,
  451. // 已完成
  452. 'done' => $done,
  453. // 返回前端页码
  454. 'page' => $page + 1
  455. ]
  456. ]);
  457. }
  458. $res = $queue->dequeue();
  459. // 所有数据已导入 csv 文件,返回文件流完成后删除
  460. return download($file_path, $file_name . '.csv', true);
  461. }
  462. /**
  463. * 分批导入文件
  464. *
  465. * @param null|array|\think\File $file
  466. * @param array $param
  467. * @param Controller $controller
  468. * @return bool
  469. *
  470. * @author Ymob
  471. */
  472. public function batchImportData($file, $param, $controller = null)
  473. {
  474. // 导入模块
  475. $types = $param['types'];
  476. if (!in_array($types, $this->types_arr)) {
  477. $this->error = '参数错误!';
  478. $queue->dequeue();
  479. return false;
  480. }
  481. $user_id = $param['create_user_id'];
  482. // 采用伪队列 允许三人同时导入数据
  483. $queue = new Queue(self::IMPORT_QUEUE, 50000000);
  484. $import_queue_index = input('import_queue_index');
  485. // 队列任务ID
  486. if (!$import_queue_index) {
  487. if (!$import_queue_index = $queue->makeTaskId()) {
  488. $this->error = $queue->error;
  489. $queue->dequeue();
  490. return false;
  491. }
  492. } else {
  493. if (!$queue->setTaskId($import_queue_index)) {
  494. $this->error = $queue->error;
  495. $queue->dequeue();
  496. return false;
  497. }
  498. }
  499. // 取消导入
  500. if ($param['page'] == -1) {
  501. @unlink(UPLOAD_PATH . $param['temp_file']);
  502. $this->error = [
  503. 'msg' => '导入已取消',
  504. 'page' => -1
  505. ];
  506. if ($param['error']) {
  507. $this->error['error_file_path'] = 'temp/' . $param['error_file'];
  508. } else {
  509. @unlink(TEMP_DIR . $param['error_file']);
  510. }
  511. $temp = $queue->cache('last_import_cache');
  512. (new ImportRecord())->createData([
  513. 'type' => $types,
  514. 'total' => $temp['total'],
  515. 'done' => $temp['done'],
  516. 'cover' => $temp['cover'],
  517. 'error' => $temp['error'],
  518. 'user_id' => $user_id,
  519. 'error_data_file_path' => $temp['error'] ? 'temp/' . $error_data_file_name : ''
  520. ]);
  521. $queue->dequeue();
  522. return true;
  523. }
  524. if (!empty($file) || $param['temp_file']) {
  525. // 导入初始化 上传文件
  526. if (!empty($file)) {
  527. $save_name = $this->upload($file);
  528. if ($save_name === false) {
  529. $queue->dequeue();
  530. return false;
  531. }
  532. } else {
  533. $save_name = $param['temp_file'];
  534. }
  535. // 文件类型
  536. $ext = pathinfo($save_name, PATHINFO_EXTENSION);
  537. // 文件路径
  538. $save_path = UPLOAD_PATH . $save_name;
  539. // 队列-判断是否需要排队
  540. if (!$queue->canExec()) {
  541. $this->error = [
  542. 'temp_file' => $save_name,
  543. 'page' => -2,
  544. 'import_queue_index' => $import_queue_index,
  545. 'info' => $queue->error
  546. ];
  547. return true;
  548. }
  549. // 加载类库
  550. vendor("phpexcel.PHPExcel");
  551. vendor("phpexcel.PHPExcel.Writer.Excel5");
  552. vendor("phpexcel.PHPExcel.Writer.Excel2007");
  553. vendor("phpexcel.PHPExcel.IOFactory");
  554. // 错误数据临时文件路径 错误数据开始行数
  555. if ($param['error_file']) {
  556. $error_path = TEMP_DIR . $param['error_file'];
  557. $error_row = $param['error'] + 3;
  558. $cover = $param['cover'] ?: 0;
  559. } else {
  560. // 生成临时文件名称
  561. $error_path = tempFileName($ext);
  562. // 将导入模板保存至临时路径
  563. $controller->excelDownload($error_path);
  564. $error_row = 3;
  565. $cover = 0;
  566. }
  567. // 错误数据临时文件名称 相对于临时目录
  568. $error_data_file_name = \substr($error_path, strlen(TEMP_DIR));
  569. // 加载错误数据文件
  570. $err_PHPExcel = \PHPExcel_IOFactory::load($error_path);
  571. $error_sheet = $err_PHPExcel->setActiveSheetIndex(0);
  572. /**
  573. * 添加错误数据到临时文件
  574. *
  575. * @param array $data 原数据
  576. * @param string $error 错误原因
  577. * @return void
  578. */
  579. $error_data_func = function ($data, $error) use ($error_sheet, &$error_row) {
  580. foreach ($data as $key => $val) {
  581. // 第一列为错误原因 所以+1
  582. $error_col = \PHPExcel_Cell::stringFromColumnIndex($key + 1);
  583. $error_sheet->setCellValue($error_col . $error_row, $val);
  584. }
  585. $error_sheet->setCellValue('A' . $error_row, $error);
  586. $error_sheet->getStyle('A' . $error_row)->getFont()->getColor()->setARGB('FF000000');
  587. $error_sheet->getStyle('A' . $error_row)->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('FFFF0000');
  588. $error_row++;
  589. };
  590. // 字段列表条件
  591. $fieldParam = [];
  592. // 导入模块
  593. switch ($types) {
  594. case 'crm_leads' :
  595. $dataModel = new \app\crm\model\Leads();
  596. $db = 'crm_leads';
  597. $db_id = 'leads_id';
  598. break;
  599. case 'crm_customer' :
  600. $dataModel = new \app\crm\model\Customer();
  601. $db = 'crm_customer';
  602. $db_id = 'customer_id';
  603. $fieldParam['form_type'] = ['not in', ['file', 'form', 'user', 'structure']];
  604. break;
  605. case 'crm_contacts' :
  606. $dataModel = new \app\crm\model\Contacts();
  607. $db = 'crm_contacts';
  608. $db_id = 'contacts_id';
  609. break;
  610. case 'crm_product' :
  611. $model = db('crm_product');
  612. $dataModel = new \app\crm\model\Product();
  613. $db = 'crm_product';
  614. $db_id = 'product_id';
  615. // 产品分类
  616. $productCategoryArr = db('crm_product_category')->field('category_id', 'name')->select();
  617. break;
  618. case 'admin_user' :
  619. $dataModel = new User();
  620. $db_id = 'id';
  621. break;
  622. }
  623. // 字段
  624. # 下次升级
  625. $fieldModel = new \app\admin\model\Field();
  626. $fieldParam['types'] = $types;
  627. $fieldParam['action'] = 'excel';
  628. if (!empty($param['pool_id'])) {
  629. $list = [];
  630. $field_list = db('crm_customer_pool_field_setting')->where(['pool_id' => $param['pool_id'], 'is_hidden' => 0,
  631. 'field_name' => ['not in', ['deal_status', 'create_user_id']]])->field('field_name as field,form_type,name')->select();
  632. foreach ($field_list as $k => &$v) {
  633. if ($v['field'] == 'address') {
  634. $v['field'] = 'customer_address';
  635. $v['form_type'] = 'map_address';
  636. $list[] = $v;
  637. unset($field_list[$k]);
  638. } elseif ($v['field'] == 'detail_address') {
  639. unset($field_list[$k]);
  640. }
  641. }
  642. $field_list = array_merge($field_list, $list);
  643. } else {
  644. $field_list = $fieldModel->field($fieldParam);
  645. }
  646. if ($types != 'admin_user' && empty($param['pool_id'])) {
  647. $field = [1 => [
  648. 'field' => 'owner_user_id',
  649. 'types' => 'crm_leads',
  650. 'name' => '负责人',
  651. 'form_type' => 'user',
  652. 'default_value' => '',
  653. 'is_null' => 1,
  654. 'input_tips' => '',
  655. 'setting' => array(),
  656. 'is_hidden' => 0,
  657. 'writeStatus' => 1,
  658. 'value' => '']
  659. ];
  660. $first_array = array_splice($field_list, 2, 0, $field);
  661. }
  662. $field_list = array_map(function ($val) {
  663. if (method_exists($val, 'toArray')) {
  664. return $val->toArray();
  665. } else {
  666. return $val;
  667. }
  668. }, $field_list);
  669. $field_key_name_list = array_column($field_list, 'name', 'field');
  670. // 加载导入数据文件
  671. $objRender = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xls');
  672. $objRender->setReadDataOnly(true);
  673. $ExcelObj = $objRender->load($save_path);
  674. // 指定工作表
  675. $sheet = $ExcelObj->getSheet(0);
  676. // 总行数
  677. $max_row = $sheet->getHighestRow();
  678. // 最大列数
  679. $max_col_num = count($field_list) - 1;
  680. // customer_address地址类字段 占4列
  681. $max_col_num += 3 * array_count_values(array_column($field_list, 'form_type'))['map_address'];
  682. $max_col = \PHPExcel_Cell::stringFromColumnIndex($max_col_num);
  683. // 检测导入文件是否使用最新模板
  684. $header = $sheet->rangeToArray("A2:{$max_col}2")[0];
  685. $temp = 0;
  686. for ($i = 0; $i < count($field_list); $i++) {
  687. if (
  688. $header[$i] == $field_list[$i]['name']
  689. || $header[$i] == '*' . $field_list[$i]['name']
  690. ) {
  691. $res[] = $header[$i];
  692. $temp++;
  693. // 字段为地址时,占四列
  694. } elseif ($field_list[$i]['form_type'] == 'map_address') {
  695. if (
  696. $header[$i] == $this->map_address[0]
  697. && $header[$i + 1] == $this->map_address[1]
  698. && $header[$i + 2] == $this->map_address[2]
  699. && $header[$i + 3] == $this->map_address[3]
  700. ) {
  701. $ress[] = $header[$i];
  702. $temp++;
  703. }
  704. }
  705. }
  706. // 每次导入条数
  707. $page_size = 100;
  708. // 当前页码
  709. $page = ((int)$param['page']) ?: 1;
  710. // 数据总数
  711. $total = $max_row - 2;
  712. // 总页数
  713. $max_page = ceil($total / $page_size);
  714. if ($page > $max_page) {
  715. // $this->error = 'page参数错误';
  716. // @unlink($save_path);
  717. // $queue->dequeue();
  718. // return false;
  719. }
  720. // 开始行 +3 跳过表头
  721. $start_row = ($page - 1) * $page_size + 3;
  722. // 结束行
  723. $end_row = $start_row + $page_size - 1;
  724. if ($end_row > $max_row) {
  725. $end_row = $max_row;
  726. }
  727. // 读取数据
  728. $dataList = $sheet->rangeToArray("A{$start_row}:{$max_col}{$end_row}");
  729. // 数据重复时的处理方式 0跳过 1覆盖
  730. $config = $param['config'] ?: 0;
  731. // 默认数据
  732. if (!empty($param['pool_id'])) {
  733. //公海导入
  734. $default_data = [
  735. 'create_user_id' => $param['create_user_id'],
  736. 'create_time' => time(),
  737. 'update_time' => time(),
  738. 'owner_user_id' => 0,
  739. 'into_pool_time' => time(),
  740. 'pool_id' => $param['pool_id'],
  741. ];
  742. } else {
  743. $default_data = [
  744. 'create_user_id' => $param['create_user_id'],
  745. 'create_time' => time(),
  746. 'update_time' => time(),
  747. ];
  748. }
  749. if ($temp !== count($field_list)) {
  750. @unlink($save_path);
  751. $queue->dequeue();
  752. foreach ($dataList as $val) {
  753. $error_data_func($val, '请使用最新导入模板');
  754. }
  755. $objWriter = \PHPExcel_IOFactory::createWriter($err_PHPExcel, 'Excel5');
  756. $objWriter->save($error_path);
  757. $error = [
  758. // 文件总计条数
  759. 'total' => $total,
  760. // 已完成条数
  761. 'done' => 0,
  762. // 覆盖
  763. 'cover' => 0,
  764. // 错误数据写入行号
  765. 'error' => $total,
  766. 'error_file_path' => 'temp/' . $error_data_file_name
  767. ];
  768. $queue->cache('last_import_cache', [
  769. 'total' => $total,
  770. 'done' => 0,
  771. 'cover' => 0,
  772. 'error' => $total
  773. ]);
  774. (new ImportRecord())->createData([
  775. 'type' => $types,
  776. 'total' => $total,
  777. 'done' => 0,
  778. 'cover' => 0,
  779. 'error' => $total,
  780. 'user_id' => $user_id,
  781. 'error_data_file_path' => 'temp/' . $error_data_file_name
  782. ]);
  783. Cache::rm('item');
  784. Cache::rm('excel_item');
  785. Cache::set('item', 1, config('import_cache_time'));
  786. Cache::set('excel_item', serialize($error), config('import_cache_time'));
  787. return true;
  788. } else {
  789. // 开始导入数据
  790. foreach ($dataList as $val) {
  791. $data = [];
  792. $unique_where = [];
  793. $empty_count = 0;
  794. $not_null_field = [];
  795. $fk = 0;
  796. foreach ($field_list as $field) {
  797. if ($field['form_type'] == 'map_address') {
  798. $data['address'] = $address = [
  799. trim((string)$val[$fk]),
  800. trim((string)$val[$fk + 1]),
  801. trim((string)$val[$fk + 2]),
  802. ];
  803. $data['detail_address'] = trim($val[$fk + 3]);
  804. $fk += 4;
  805. continue;
  806. } else {
  807. $temp_value = trim($val[$fk]);
  808. }
  809. if ($field['field'] == 'category_id' && $types == 'crm_product') {
  810. $data['category_id'] = $productCategoryArr[$temp_value] ?: 0;
  811. $data['category_str'] = $dataModel->getPidStr($productCategoryArr[$temp_value], '', 1);
  812. }
  813. // 特殊字段特殊处理
  814. $temp_value = $this->handleData($temp_value, $field);
  815. $data[$field['field']] = $temp_value;
  816. // 查重字段
  817. if ($field['is_unique'] && $temp_value) {
  818. $unique_where[$field['field']] = $temp_value;
  819. }
  820. if ($temp_value == '') {
  821. if ($field['is_null']) {
  822. $not_null_field[] = $field['name'];
  823. }
  824. $empty_count++;
  825. }
  826. $fk++;
  827. }
  828. if (!empty($not_null_field) && empty($param['pool_id'])) {
  829. $error_data_func($val, implode(', ', $not_null_field) . '不能为空');
  830. continue;
  831. }
  832. if ($empty_count == count($field_list)) {
  833. $error_data_func($val, '空行');
  834. continue;
  835. }
  836. $old_data_id_list = [];
  837. if ($unique_where) {
  838. if ($types == 'crm_product') {
  839. $old_data_id_list = $model->whereOr($unique_where)->where('delete_user_id', 0)->column($db_id);
  840. } else {
  841. $old_data_id_list = $dataModel->whereOr($unique_where)->column($db_id);
  842. }
  843. }
  844. $userId = '';
  845. #下次升级
  846. if ($param['pool_id']) {
  847. $userId = db('admin_user')->where('realname', $val[2])->value('id');
  848. $data['before_owner_user_id'] = 0;
  849. } else {
  850. $userId = db('admin_user')->where('realname', $val[2])->value('id');
  851. $data['owner_user_id'] = $userId ?: 0;
  852. }
  853. $owner = db('crm_customer_pool')->where(['pool_id' => $param['pool_id']])->value('user_ids');
  854. $auth = db('admin_access')->where('user_id', $param['create_user_id'])->column('group_id');
  855. // 数据重复时
  856. if ($old_data_id_list) {
  857. if ($config) {
  858. $data = array_merge($data, $default_data);
  859. $data['create_user_id'] = $param['create_user_id'];
  860. $data['update_time'] = time();
  861. $dataModel->startTrans();
  862. try {
  863. $up_success_count = 0;
  864. foreach ($old_data_id_list as $id) {
  865. if ($types == 'crm_customer' && !empty($param['pool_id'])) {
  866. if (!in_array($param['create_user_id'], trim(stringToArray($owner), ',')) && $param['create_user_id'] != 1) {
  867. if (!$dataModel->updateDataById($data, $id)) {
  868. $temp_error = $dataModel->getError();
  869. if ($temp_error == '无权操作') {
  870. $temp_error = '当前导入人员对该数据无写入权限';
  871. }
  872. $error_data_func($val, $temp_error);
  873. $dataModel->rollback();
  874. break;
  875. }
  876. break;
  877. } else {
  878. $temp_error = '当前导入人员对该公海数据,无导入权限';
  879. $error_data_func($val, $temp_error);
  880. }
  881. } else {
  882. if (!$dataModel->updateDataById($data, $id)) {
  883. $temp_error = $dataModel->getError();
  884. if ($temp_error == '无权操作') {
  885. $temp_error = '当前导入人员对该数据无写入权限';
  886. }
  887. $error_data_func($val, $temp_error);
  888. $dataModel->rollback();
  889. break;
  890. }
  891. }
  892. $up_success_count++;
  893. }
  894. // 全部更新完成
  895. if ($up_success_count === count($old_data_id_list)) {
  896. $cover++;
  897. $dataModel->commit();
  898. }
  899. } catch (\Exception $e) {
  900. $dataModel->rollback();
  901. }
  902. } else {
  903. // 重复字段标记
  904. $unique_field = [];
  905. foreach ($old_data_id_list as $id) {
  906. $old_data = $dataModel->getDataById($id);
  907. foreach ($unique_where as $k => $v) {
  908. if (trim($old_data[$k]) == $v) {
  909. $unique_field[] = $field_key_name_list[$k];
  910. }
  911. }
  912. }
  913. $unique_field = array_unique($unique_field);
  914. $error_data_func($val, implode(', ', $unique_field) . ' 根据查重规则,该条数据重复');
  915. }
  916. } else {
  917. if ($types == 'crm_customer' && !empty($param['pool_id'])) {
  918. $userLevel = isSuperAdministrators($param['create_user_id']);
  919. if (in_array($param['create_user_id'], stringToArray($owner)) || $param['create_user_id'] == 1 || $userLevel == 1) {
  920. $data = array_merge($data, $default_data);
  921. $data['excel'] = 1;
  922. if (!$resData = $dataModel->createData($data)) {
  923. $error_data_func($val, $dataModel->getError());
  924. }
  925. } else {
  926. $temp_error = '当前导入人员对该公海数据,无导入权限';
  927. $error_data_func($val, $temp_error);
  928. }
  929. } else {
  930. $data = array_merge($data, $default_data);
  931. if ($types != 'admin_user') {
  932. $data['excel'] = 1;
  933. }
  934. if (!$resData = $dataModel->createData($data)) {
  935. $error_data_func($val, $dataModel->getError());
  936. }
  937. }
  938. }
  939. }
  940. // 完成数(已导入数)
  941. $done = ($page - 1) * $page_size + count($dataList);
  942. if ($page == $max_page) {
  943. $done = $total;
  944. }
  945. // 错误数
  946. $error = $error_row - 3;
  947. // 错误数据文件保存
  948. $objWriter = \PHPExcel_IOFactory::createWriter($err_PHPExcel, 'Excel5');
  949. $objWriter->save($error_path);
  950. $this->error = [
  951. // 数据导入文件临时路径
  952. 'temp_file' => $save_name,
  953. // 错误数据文件路径
  954. 'error_file' => $error_data_file_name,
  955. // 文件总计条数
  956. 'total' => $total,
  957. // 已完成条数
  958. 'done' => $done,
  959. // 覆盖
  960. 'cover' => $cover,
  961. // 错误数据写入行号
  962. 'error' => $error,
  963. // 下次页码
  964. 'page' => $page + 1,
  965. // 导入任务ID
  966. 'import_queue_index' => $import_queue_index
  967. ];
  968. $queue->cache('last_import_cache', [
  969. 'total' => $total,
  970. 'done' => $done,
  971. 'cover' => $cover,
  972. 'error' => $error
  973. ]);
  974. // 执行完成
  975. $redis = new Redis();
  976. if ($done >= $total) {
  977. // 出队
  978. $queue->dequeue();
  979. // 错误数据文件路径
  980. $this->error['error_file_path'] = 'temp/' . $error_data_file_name;
  981. // 删除导入文件
  982. @unlink($save_path);
  983. // 没有错误数据时,删除错误文件
  984. if ($error == 0) {
  985. @unlink($error_path);
  986. }
  987. (new ImportRecord())->createData([
  988. 'type' => $types,
  989. 'total' => $total,
  990. 'done' => $done,
  991. 'cover' => $cover,
  992. 'error' => $error,
  993. 'user_id' => $user_id,
  994. 'error_data_file_path' => $error ? 'temp/' . $error_data_file_name : ''
  995. ]);
  996. Cache::rm('item');
  997. Cache::rm('excel_item');
  998. Cache::set('item', 1, config('import_cache_time'));
  999. Cache::set('excel_item', serialize($this->error), config('import_cache_time'));
  1000. } else {
  1001. $queue->dequeue();
  1002. $excelData['cover'] = $cover;
  1003. $excelData['page'] = $page + 1;
  1004. $excelData['types'] = $types;
  1005. $excelData['temp_file'] = $save_name;
  1006. $excelData['error_file'] = $error_data_file_name;
  1007. $excelData['create_user_id'] = $param['create_user_id'];
  1008. $excelData['import_queue_index'] = $import_queue_index;
  1009. $excelData['config'] = $config;
  1010. $excelData['owner_user_id'] = $user_id;
  1011. $excelData['base'] = 'batchImportData';
  1012. Cache::rm('item');
  1013. Cache::rm('excel');
  1014. Cache::set('item', 0, config('import_cache_time'));
  1015. Cache::set('excel', $excelData, config('import_cache_time'));
  1016. }
  1017. return true;
  1018. }
  1019. } else {
  1020. $this->error = '请选择导入文件';
  1021. $queue->dequeue();
  1022. return false;
  1023. }
  1024. }
  1025. /**
  1026. * 导入数据时 读取xls表格数据
  1027. *
  1028. * @param PHPExcel_Worksheet $sheet
  1029. * @param integer $start 开始行
  1030. * @param integer $end 结束行 0时表示所有
  1031. * @param array $fields 字段名称
  1032. * @return array
  1033. * @author Ymob
  1034. */
  1035. public function readSheet($sheet, $start = 1, $end = 0, $fields = [])
  1036. {
  1037. $data = [];
  1038. for ($row = $start; $row <= $end; $row++) {
  1039. $temp = [];
  1040. foreach ($fields as $key => $field) {
  1041. $col = Coordinate::stringFromColumnIndex($key);
  1042. $temp[$field] = $sheet->getCell($col . $row);
  1043. }
  1044. $data[] = $temp;
  1045. }
  1046. return $data;
  1047. }
  1048. /**
  1049. * 上传文件导入数据文件
  1050. *
  1051. * @param [type] $file
  1052. * @return mixed 上传文件路径 | 上传失败错误信息
  1053. * @author Ymob
  1054. */
  1055. public function upload($file)
  1056. {
  1057. $get_filesize_byte = get_upload_max_filesize_byte();
  1058. $info = $file->validate(['size' => $get_filesize_byte, 'ext' => 'xls'])->move(FILE_PATH . 'public' . DS . 'uploads'); //验证规则
  1059. if (!$info) {
  1060. $this->error = $file->getError();
  1061. return false;
  1062. }
  1063. $saveName = $info->getSaveName(); //保存路径
  1064. if (!$saveName) {
  1065. $this->error = '文件上传失败,请重试!';
  1066. return false;
  1067. }
  1068. return $saveName;
  1069. }
  1070. /**
  1071. * 自定义字段模块数据导入(默认2000行)
  1072. * @param $types 分类
  1073. * @param $file 导入文件
  1074. * @param $create_user_id 创建人ID
  1075. * @param $owner_user_id 负责人ID
  1076. * @return todo 导入记录
  1077. * @author Michael_xu
  1078. */
  1079. public function importExcel($file, $param, $controller = null)
  1080. {
  1081. $queue = new Queue(self::IMPORT_QUEUE, 1);
  1082. $import_queue_index = input('import_queue_index');
  1083. $user_id = $param['owner_user_id'];
  1084. if (!$import_queue_index) {
  1085. if (!$import_queue_index = $queue->makeTaskId()) {
  1086. $this->error = $queue->error;
  1087. $queue->dequeue();
  1088. return false;
  1089. }
  1090. } else {
  1091. if (!$queue->setTaskId($import_queue_index)) {
  1092. $this->error = $queue->error;
  1093. $queue->dequeue();
  1094. return false;
  1095. }
  1096. }
  1097. if ($param['page'] == -1) {
  1098. $queue->dequeue();
  1099. $this->error = [
  1100. 'msg' => '导入已取消',
  1101. 'page' => -1
  1102. ];
  1103. if ($param['error']) {
  1104. $this->error['error_file_path'] = 'temp/' . $param['error_file'];
  1105. }
  1106. return true;
  1107. }
  1108. $config = $param['config'] ?: '';
  1109. if (!empty($file) || $param['temp_file']) {
  1110. $types = $param['types'];
  1111. if (!in_array($types, $this->types_arr)) {
  1112. $this->error = '参数错误!';
  1113. $queue->dequeue();
  1114. return false;
  1115. }
  1116. // 导入初始化 上传文件
  1117. if (!empty($file)) {
  1118. $get_filesize_byte = get_upload_max_filesize_byte();
  1119. $info = $file->validate(['size' => $get_filesize_byte, 'ext' => 'xls,xlsx,csv'])->move(UPLOAD_PATH); //验证规则
  1120. if (!$info) {
  1121. $this->error = $file->getError();
  1122. $queue->dequeue();
  1123. return false;
  1124. }
  1125. $save_name = $info->getSaveName(); //保存路径
  1126. if (!$save_name) {
  1127. $this->error = '文件上传失败,请重试!';
  1128. $queue->dequeue();
  1129. return false;
  1130. }
  1131. } else {
  1132. $save_name = $param['temp_file'];
  1133. }
  1134. $ext = pathinfo($save_name, PATHINFO_EXTENSION); //文件后缀
  1135. $save_path = UPLOAD_PATH . $save_name;
  1136. if (!$queue->canExec()) {
  1137. $this->error = [
  1138. 'temp_file' => $save_name,
  1139. 'page' => -2,
  1140. 'import_queue_index' => $import_queue_index,
  1141. 'info' => $queue->error
  1142. ];
  1143. return true;
  1144. }
  1145. if ($param['error_file']) {
  1146. $error_path = TEMP_DIR . $param['error_file'];
  1147. $error_row = $param['error'] + 3;
  1148. } else {
  1149. $error_path = tempFileName($ext);
  1150. // 生成错误数据文件
  1151. $controller->excelDownload($error_path);
  1152. $error_row = 3;
  1153. }
  1154. vendor("phpexcel.PHPExcel");
  1155. vendor("phpexcel.PHPExcel.Writer.Excel5");
  1156. vendor("phpexcel.PHPExcel.Writer.Excel2007");
  1157. vendor("phpexcel.PHPExcel.IOFactory");
  1158. $err_PHPExcel = \PHPExcel_IOFactory::load($error_path);
  1159. $sheet = $err_PHPExcel->setActiveSheetIndex(0);
  1160. // 添加错误数据到临时文件
  1161. $error_data_func = function ($data, $error) use ($sheet, &$error_row) {
  1162. foreach ($data as $key => $val) {
  1163. // 第一列为错误原因 所以+1
  1164. $error_col = \PHPExcel_Cell::stringFromColumnIndex($key + 1);
  1165. $sheet->setCellValue($error_col . $error_row, $val);
  1166. }
  1167. $sheet->setCellValue('A' . $error_row, $error);
  1168. $sheet->getStyle('A' . $error_row)->getFont()->getColor()->setARGB('FF000000');
  1169. $sheet->getStyle('A' . $error_row)->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('FFFF0000');
  1170. $error_row++;
  1171. };
  1172. // 错误数据临时文件名称
  1173. $error_data_file_name = \substr($error_path, strlen(TEMP_DIR));
  1174. //实例化主文件
  1175. set_time_limit(1800);
  1176. ini_set("memory_limit", "256M");
  1177. $objPHPExcel = new Spreadsheet();
  1178. if ($ext == 'xlsx') {
  1179. $objRender = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xlsx');
  1180. // $objRender->setReadDataOnly(true);
  1181. $ExcelObj = $objRender->load($save_path);
  1182. } elseif ($ext == 'xls') {
  1183. $objRender = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xls');
  1184. // $objRender->setReadDataOnly(true);
  1185. $ExcelObj = $objRender->load($save_path);
  1186. } elseif ($ext == 'csv') {
  1187. $objWriter = new \PhpOffice\PhpSpreadsheet\Reader\Csv($objPHPExcel);
  1188. //默认输入字符集
  1189. $objWriter->setInputEncoding('UTF-8');
  1190. //默认的分隔符
  1191. $objWriter->setDelimiter(',');
  1192. //载入文件
  1193. $ExcelObj = $objWriter->load($save_path);
  1194. }
  1195. $currentSheet = $ExcelObj->getSheet(0);
  1196. $data = $currentSheet->rangeToArray('A3:C12');
  1197. //查看有几个sheet
  1198. $sheetContent = $ExcelObj->getSheet(0)->toArray();
  1199. //获取总行数
  1200. $sheetCount = $ExcelObj->getSheet(0)->getHighestRow();
  1201. // if ($sheetCount > 2002) {
  1202. // $this->error = '单文件一次最多导入2000条数据';
  1203. // return false;
  1204. // }
  1205. //读取表头
  1206. $excelHeader = $sheetContent[1];
  1207. unset($sheetContent[0]);
  1208. unset($sheetContent[1]);
  1209. //取出文件的内容描述信息,循环取出数据,写入数据库
  1210. switch ($types) {
  1211. case 'crm_leads' :
  1212. $dataModel = new \app\crm\model\Leads();
  1213. $db = 'crm_leads';
  1214. $db_id = 'leads_id';
  1215. break;
  1216. case 'crm_customer' :
  1217. $dataModel = new \app\crm\model\Customer();
  1218. $db = 'crm_customer';
  1219. $db_id = 'customer_id';
  1220. $fieldParam['form_type'] = array('not in', ['file', 'form', 'user', 'structure']);
  1221. break;
  1222. case 'crm_contacts' :
  1223. $dataModel = new \app\crm\model\Contacts();
  1224. $db = 'crm_contacts';
  1225. $db_id = 'contacts_id';
  1226. break;
  1227. case 'crm_product' :
  1228. $dataModel = new \app\crm\model\Product();
  1229. $db = 'crm_product';
  1230. $db_id = 'product_id';
  1231. break;
  1232. }
  1233. $contactsModel = new \app\crm\model\Contacts();
  1234. //自定义字段
  1235. $fieldModel = new \app\admin\model\Field();
  1236. $fieldParam['types'] = $types;
  1237. $field_list = $fieldModel->getDataList($fieldParam);
  1238. $fieldArr = [];
  1239. $uniqueField = []; //验重字段
  1240. foreach ($field_list as $k => $v) {
  1241. $fieldArr[$v['name']]['field'] = $v['field'];
  1242. $fieldArr[$v['name']]['form_type'] = $v['form_type'];
  1243. if ($v['is_unique'] == 1) {
  1244. $uniqueField[] = $v['field'];
  1245. }
  1246. }
  1247. $field_num = count($field_list);
  1248. //客户导入联系人
  1249. if ($types == 'crm_customer') {
  1250. $contacts_field_list = $fieldModel->getDataList(['types' => 'crm_contacts', 'field' => array('neq', 'customer_id')]);
  1251. $contactsFieldArr = [];
  1252. foreach ($contacts_field_list as $k => $v) {
  1253. $contactsFieldArr[$v['name']]['field'] = $v['field'];
  1254. $contactsFieldArr[$v['name']]['form_type'] = $v['form_type'];
  1255. }
  1256. }
  1257. $defaultData = []; //默认数据
  1258. $defaultData['create_user_id'] = $param['create_user_id'];
  1259. $defaultData['owner_user_id'] = $param['owner_user_id'];
  1260. $defaultData['create_time'] = time();
  1261. $defaultData['update_time'] = time();
  1262. //产品类别
  1263. if ($types == 'crm_product') {
  1264. $productCategory = db('crm_product_category')->select();
  1265. $productCategoryArr = [];
  1266. foreach ($productCategory as $v) {
  1267. $productCategoryArr[$v['name']] = $v['category_id'];
  1268. }
  1269. }
  1270. // 表头行数
  1271. $keys = 2;
  1272. // 导入错误数据
  1273. $errorMessage = [];
  1274. // 每次导入条数
  1275. $forCount = 5;
  1276. // 当前页码
  1277. $page = $param['page'] ?: 1;
  1278. // 数据总数
  1279. $total = $sheetCount - $keys;
  1280. // 总页数
  1281. $max_page = ceil($total / $forCount);
  1282. if ($page > $max_page) {
  1283. $this->error = 'page参数错误';
  1284. $queue->dequeue();
  1285. return false;
  1286. }
  1287. $_sub = array_slice($sheetContent, ($page - 1) * $forCount, $forCount);
  1288. foreach ($_sub as $kk => $val) {
  1289. $data = '';
  1290. $contactsData = '';
  1291. $k = 0;
  1292. $contacts_k = $field_num;
  1293. $resNameIds = '';
  1294. $keys++;
  1295. $name = ''; //客户、线索、联系人等名称
  1296. $contactsName = '';
  1297. $data = $defaultData; //导入数据
  1298. $contacts_data = $defaultData; //导入数据
  1299. $resWhere = ''; //验重条件
  1300. $resWhereNum = 0; //验重数
  1301. $resContacts = false; //联系人是否有数据
  1302. $resInfo = false; //Excel列是否有数据
  1303. $resData = [];
  1304. $resContactsData = [];
  1305. $row_error = false;
  1306. foreach ($excelHeader as $aa => $header) {
  1307. if (empty($header)) break;
  1308. $fieldName = trim(str_replace('*', '', $header));
  1309. $info = '';
  1310. $info = trim($val[$k]);
  1311. if ($info) $resInfo = true;
  1312. if ($types == 'crm_product' && $fieldName == '产品类别') {
  1313. $data['category_id'] = $productCategoryArr[$info] ?: 0;
  1314. $data['category_str'] = $dataModel->getPidStr($productCategoryArr[$info], '', 1);
  1315. }
  1316. //联系人
  1317. if ($types == 'crm_contacts' && $fieldName == '客户名称') {
  1318. if (!$info) {
  1319. $error_data_func($val, '客户名称必填'); // 错误数据导出
  1320. $errorMessage[] = '第' . $keys . '行导入错误,失败原因:客户名称必填';
  1321. $row_error = true;
  1322. continue;
  1323. }
  1324. $customer_id = '';
  1325. $customer_id = db('crm_customer')->where(['name' => $info])->value('customer_id');
  1326. if (!$customer_id) {
  1327. $error_data_func($val, '客户名称不存在'); // 错误数据导出
  1328. $errorMessage[] = '第' . $keys . '行导入错误,失败原因:客户名称不存在';
  1329. $row_error = true;
  1330. continue;
  1331. }
  1332. $data['customer_id'] = $customer_id;
  1333. }
  1334. if ($aa < $field_num) {
  1335. if (empty($fieldArr[$fieldName]['field'])) continue;
  1336. // if ($fieldArr[$fieldName]['field'] == 'name') $name = $info;
  1337. if (in_array($fieldArr[$fieldName]['field'], $uniqueField) && $info) {
  1338. if ($resWhereNum > 0) $resWhere .= " OR ";
  1339. $resWhere .= " `" . $fieldArr[$fieldName]['field'] . "` = '" . $info . "'";
  1340. $resWhereNum += 1;
  1341. }
  1342. $resList = [];
  1343. $resList = $this->sheetData($k, $fieldArr, $fieldName, $info);
  1344. $resData[] = $resList['data'];
  1345. $k = $resList['k'];
  1346. } else {
  1347. //联系人
  1348. if ($types == 'crm_customer' && $aa == (int)$contacts_k) {
  1349. $contactsInfo = '';
  1350. $contactsInfo = $val[$contacts_k];
  1351. if ($contactsInfo) {
  1352. $resContacts = true;
  1353. }
  1354. // if ($contactsFieldArr[$fieldName]['field'] == 'name') $contactsName = $contactsInfo;
  1355. $resContactsList = [];
  1356. $resContactsList = $this->sheetData($contacts_k, $contactsFieldArr, $fieldName, $contactsInfo);
  1357. $resContactsData[] = $resContactsList['data'];
  1358. $contacts_k = $resContactsList['k'];
  1359. }
  1360. }
  1361. }
  1362. if ($row_error) {
  1363. continue;
  1364. }
  1365. $result = $this->changeArr($resData); //二维数组转一维数组
  1366. $data = $result ? array_merge($data, $result) : [];
  1367. if ($types == 'crm_customer' && $result) {
  1368. $resultContacts = $this->changeArr($resContactsData);
  1369. $contactsData = $resultContacts ? array_merge($contacts_data, $resultContacts) : []; //联系人
  1370. }
  1371. $resWhere = $resWhere ?: '';
  1372. // $ownerWhere['owner_user_id'] = $param['owner_user_id'];
  1373. if ($uniqueField && $resWhere) {
  1374. $resNameIds = db($db)->where($resWhere)->where($ownerWhere)->column($db_id);
  1375. }
  1376. if ($resInfo == false) {
  1377. continue;
  1378. }
  1379. if ($resNameIds && $data) {
  1380. if ($config == 1 && $resNameIds) {
  1381. $data['user_id'] = $param['create_user_id'];
  1382. $data['update_time'] = time();
  1383. //覆盖数据(以名称为查重规则,如存在则覆盖原数据)
  1384. foreach ($resNameIds as $nid) {
  1385. $upRes = $dataModel->updateDataById($data, $nid);
  1386. if (!$upRes) {
  1387. $error_data_func($val, $dataModel->getError()); // 错误数据导出
  1388. $errorMessage[] = '第' . $keys . '行导入错误,失败原因:' . $dataModel->getError();
  1389. continue;
  1390. }
  1391. if ($types == 'crm_customer' && $resContacts !== false) {
  1392. $contactsData['customer_id'] = $upRes['customer_id'];
  1393. if (!$contactsData['owner_user_id']) $contactsData['owner_user_id'] = $param['create_user_id'];
  1394. if (!$resData = $contactsModel->createData($contactsData)) {
  1395. $error_data_func($val, $contactsModel->getError()); // 错误数据导出
  1396. $errorMessage[] = '第' . $keys . '行导入错误,失败原因:' . $contactsModel->getError();
  1397. continue;
  1398. }
  1399. }
  1400. }
  1401. } else {
  1402. $error_data_func($val, '跳过');
  1403. }
  1404. } else {
  1405. if (!$resData = $dataModel->createData($data)) {
  1406. $error_data_func($val, $dataModel->getError()); // 错误数据导出
  1407. $errorMessage[] = '第' . $keys . '行导入错误,失败原因:' . $dataModel->getError();
  1408. continue;
  1409. }
  1410. if ($types == 'crm_customer' && $resContacts !== false) {
  1411. $contactsData['customer_id'] = $resData['customer_id'];
  1412. if (!$contactsData['owner_user_id']) $contactsData['owner_user_id'] = $param['create_user_id'];
  1413. if (!$resData = $contactsModel->createData($contactsData)) {
  1414. $error_data_func($val, $contactsModel->getError()); // 错误数据导出
  1415. $errorMessage[] = '第' . $keys . '行导入错误,失败原因:' . $contactsModel->getError();
  1416. continue;
  1417. }
  1418. }
  1419. }
  1420. }
  1421. // 完成数
  1422. $done = ($page - 1) * $forCount + count($_sub);
  1423. // 错误数
  1424. $error = $error_row - 3;
  1425. // 错误数据暂存
  1426. $objWriter = \PHPExcel_IOFactory::createWriter($err_PHPExcel, 'Excel5');
  1427. $objWriter->save($error_path);
  1428. $this->error = [
  1429. 'temp_file' => $save_name,
  1430. 'error_file' => $error_data_file_name,
  1431. // 每行错误信息提示
  1432. // 'error' => $errorMessage,
  1433. // 文件总计条数
  1434. 'total' => $total,
  1435. // 已完成条数
  1436. 'done' => $done,
  1437. // 错误数据写入行号
  1438. 'error' => $error,
  1439. // 下次页码
  1440. 'page' => $page + 1,
  1441. 'import_queue_index' => $import_queue_index
  1442. ];
  1443. // 执行完成
  1444. if ($done >= $total) {
  1445. $queue->dequeue();
  1446. $this->error['error_file_path'] = 'temp/' . $error_data_file_name;
  1447. Cache::set('item', 1, config('import_cache_time'));
  1448. Cache::set('excel_item', serialize($this->error), config('import_cache_time'));
  1449. } else {
  1450. $excelData['page'] = $page + 1;
  1451. $excelData['types'] = $types;
  1452. $excelData['temp_file'] = $save_name;
  1453. $excelData['error_file'] = $error_data_file_name;
  1454. $excelData['create_user_id'] = $param['create_user_id'];
  1455. $excelData['import_queue_index'] = $import_queue_index;
  1456. $excelData['config'] = $config;
  1457. $excelData['owner_user_id'] = $user_id;
  1458. $excelData['base'] = 'importExcel';
  1459. Cache::set('item', 0, config('import_cache_time'));
  1460. Cache::set('excel', $excelData, config('import_cache_time'));
  1461. }
  1462. return true;
  1463. } else {
  1464. $this->error = '请选择导入文件';
  1465. $queue->dequeue();
  1466. return false;
  1467. }
  1468. }
  1469. /**
  1470. * excel数据处理
  1471. * @param $k 需处理数据开始下标
  1472. * @return
  1473. * @author Michael_xu
  1474. */
  1475. public function sheetData($k = 0, $fieldArr, $fieldName, $info)
  1476. {
  1477. if ($info) {
  1478. if ($fieldArr[$fieldName]['form_type'] == 'address') {
  1479. $address = array();
  1480. for ($i = 0; $i < 4; $i++) {
  1481. $address[] = $val[$k];
  1482. $k++;
  1483. }
  1484. $data[$fieldArr[$fieldName]['field']] = implode(chr(10), $address);
  1485. // 地址信息转地理坐标(仅处理系统初始的地址字段)
  1486. if ($fieldArr[$fieldName]['field'] == 'address') {
  1487. $address_arr = $address;
  1488. if ($address_arr['3']) {
  1489. $address_str = implode('', $address_arr);
  1490. $ret = get_lng_lat($address_str);
  1491. $data['lng'] = $ret['lng'] ?: 0;
  1492. $data['lat'] = $ret['lat'] ?: 0;
  1493. }
  1494. }
  1495. } elseif ($fieldArr[$fieldName]['form_type'] == 'date') {
  1496. $data[$fieldArr[$fieldName]['field']] = $info ? date('Y-m-d', strtotime($info)) : '';
  1497. $k++;
  1498. } elseif ($fieldArr[$fieldName]['form_type'] == 'datetime') {
  1499. $data[$fieldArr[$fieldName]['field']] = $info ? strtotime($info) : '';
  1500. $k++;
  1501. } elseif ($fieldArr[$fieldName]['form_type'] == 'customer') {
  1502. $data[$fieldArr[$fieldName]['field']] = db('crm_customer')->where(['name' => $info])->value('customer_id') ?: '';
  1503. $k++;
  1504. } elseif ($fieldArr[$fieldName]['form_type'] == 'contacts') {
  1505. $data[$fieldArr[$fieldName]['field']] = db('crm_contacts')->where(['name' => $info])->value('contacts_id') ?: '';
  1506. $k++;
  1507. } elseif ($fieldArr[$fieldName]['form_type'] == 'business') {
  1508. $data[$fieldArr[$fieldName]['field']] = db('crm_business')->where(['name' => $info])->value('business_id') ?: '';
  1509. $k++;
  1510. } elseif ($fieldArr[$fieldName]['form_type'] == 'category') {
  1511. $data[$fieldArr[$fieldName]['field']] = db('crm_product_category')->where(['name' => $info])->value('category_id') ?: '';
  1512. $k++;
  1513. } elseif ($fieldArr[$fieldName]['form_type'] == 'business_type') {
  1514. $data[$fieldArr[$fieldName]['field']] = db('crm_business_type')->where(['name' => $info])->value('type_id') ?: '';
  1515. $k++;
  1516. } elseif ($fieldArr[$fieldName]['form_type'] == 'business_status') {
  1517. $data[$fieldArr[$fieldName]['field']] = db('crm_business_status')->where(['name' => $info])->value('status_id') ?: '';
  1518. $k++;
  1519. } else {
  1520. $data[$fieldArr[$fieldName]['field']] = $info ?: '';
  1521. $k++;
  1522. }
  1523. } else {
  1524. $data[$fieldArr[$fieldName]['field']] = '';
  1525. $k++;
  1526. }
  1527. $res['data'] = $data;
  1528. $res['k'] = $k;
  1529. return $res;
  1530. }
  1531. /**
  1532. * 导入数据处理
  1533. *
  1534. * @param string $value
  1535. * @param array $field
  1536. * @return string
  1537. * @author Ymob
  1538. */
  1539. public function handleData($value, $field)
  1540. {
  1541. switch ($field['form_type']) {
  1542. case 'address':
  1543. return $value;
  1544. case 'date':
  1545. return $value ? date('Y-m-d', strtotime($value)) : null;
  1546. case 'datetime':
  1547. return strtotime($value) ?: 0;
  1548. case 'customer':
  1549. case 'contacts':
  1550. case 'business':
  1551. $temp = db('crm_' . $field['form_type'])
  1552. ->where(['name' => $value])
  1553. ->value($field['form_type'] . '_id');
  1554. return $temp ?: 0;
  1555. case 'business_type':
  1556. $temp = db('crm_business_type')
  1557. ->where(['name' => $value])
  1558. ->value('type_id');
  1559. return $temp ?: 0;
  1560. case 'business_status':
  1561. $temp = db('crm_business_status')
  1562. ->where(['name' => $value])
  1563. ->value('status_id');
  1564. return $temp ?: 0;
  1565. default:
  1566. return $value;
  1567. }
  1568. }
  1569. //二维数组转一维数组
  1570. public function changeArr($arr)
  1571. {
  1572. $newArr = [];
  1573. foreach ($arr as $v) {
  1574. if ($v && is_array($v)) {
  1575. $newArr = array_merge($newArr, $v);
  1576. } else {
  1577. continue;
  1578. }
  1579. }
  1580. return $newArr;
  1581. }
  1582. /**
  1583. * excel参数配置(备份)
  1584. * @param
  1585. * @return
  1586. * @author Michael_xu
  1587. */
  1588. public function config()
  1589. {
  1590. vendor("PHPExcel.PHPExcel.PHPExcel");
  1591. vendor("PHPExcel.PHPExcel.Writer.Excel5");
  1592. vendor("PHPExcel.PHPExcel.Writer.Excel2007");
  1593. vendor("PHPExcel.PHPExcel.IOFactory");
  1594. //实例化
  1595. $objPHPExcel = new \PHPExcel();
  1596. $objWriter = new \PHPExcel_Writer_Excel5($objPHPExcel);
  1597. $objWriter = new \PHPExcel_Writer_Excel2007($objPHPExcel);
  1598. $objProps = $objPHPExcel->getProperties(); // 设置excel文档的属性
  1599. $objProps->setCreator("snowerp"); //创建人
  1600. $objProps->setLastModifiedBy("snowerp"); //最后修改人
  1601. $objProps->setTitle("snowerp"); //标题
  1602. $objProps->setSubject("snowerp"); //题目
  1603. $objProps->setDescription("snowerp"); //描述
  1604. $objProps->setKeywords("snowerp"); //关键字
  1605. $objProps->setCategory("snowerp"); //种类
  1606. $objPHPExcel->setActiveSheetIndex(0); //设置当前的sheet
  1607. $objActSheet = $objPHPExcel->getActiveSheet();
  1608. $objActSheet->setTitle('snowerp'); //设置sheet的标题
  1609. $objPHPExcel->getActiveSheet()->getColumnDimension('A')->setWidth(20); //设置单元格宽度
  1610. $objPHPExcel->getActiveSheet()->getRowDimension($i)->setRowHeight(40); //设置单元格高度
  1611. $objPHPExcel->getActiveSheet()->mergeCells('A18:E22'); //合并单元格
  1612. $objPHPExcel->getActiveSheet()->unmergeCells('A28:B28'); //拆分单元格
  1613. //设置保护cell,保护工作表
  1614. $objPHPExcel->getActiveSheet()->getProtection()->setSheet(true);
  1615. $objPHPExcel->getActiveSheet()->protectCells('A3:E13', 'PHPExcel');
  1616. //设置格式
  1617. $objPHPExcel->getActiveSheet()->getStyle('E4')->getNumberFormat()->setFormatCode(PHPExcel_Style_NumberFormat::FORMAT_CURRENCY_EUR_SIMPLE);
  1618. $objPHPExcel->getActiveSheet()->duplicateStyle($objPHPExcel->getActiveSheet()->getStyle('E4'), 'E5:E13');
  1619. //设置加粗
  1620. $objPHPExcel->getActiveSheet()->getStyle('B1')->getFont()->setBold(true);
  1621. //设置水平对齐方式(HORIZONTAL_RIGHT,HORIZONTAL_LEFT,HORIZONTAL_CENTER,HORIZONTAL_JUSTIFY)
  1622. $objPHPExcel->getActiveSheet()->getStyle('D11')->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);
  1623. //设置垂直居中
  1624. $objPHPExcel->getActiveSheet()->getStyle('A18')->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER);
  1625. //设置字号
  1626. $objPHPExcel->getActiveSheet()->getDefaultStyle()->getFont()->setSize(10);
  1627. //设置边框
  1628. $objPHPExcel->getActiveSheet()->getStyle('A1:I20')->getBorders()->getAllBorders()->setBorderStyle(\PHPExcel_Style_Border::BORDER_THIN);
  1629. //设置边框颜色
  1630. $objPHPExcel->getActiveSheet()->getStyle('D13')->getBorders()->getLeft()->getColor()->setARGB('FF993300');
  1631. $objPHPExcel->getActiveSheet()->getStyle('D13')->getBorders()->getTop()->getColor()->setARGB('FF993300');
  1632. $objPHPExcel->getActiveSheet()->getStyle('D13')->getBorders()->getBottom()->getColor()->setARGB('FF993300');
  1633. $objPHPExcel->getActiveSheet()->getStyle('E13')->getBorders()->getTop()->getColor()->setARGB('FF993300');
  1634. $objPHPExcel->getActiveSheet()->getStyle('E13')->getBorders()->getBottom()->getColor()->setARGB('FF993300');
  1635. $objPHPExcel->getActiveSheet()->getStyle('E13')->getBorders()->getRight()->getColor()->setARGB('FF993300');
  1636. //插入图像
  1637. $objDrawing = new PHPExcel_Worksheet_Drawing();
  1638. /*设置图片路径 切记:只能是本地图片*/
  1639. $objDrawing->setPath('图像地址');
  1640. /*设置图片高度*/
  1641. $objDrawing->setHeight(180);//照片高度
  1642. $objDrawing->setWidth(150); //照片宽度
  1643. /*设置图片要插入的单元格*/
  1644. $objDrawing->setCoordinates('E2');
  1645. /*设置图片所在单元格的格式*/
  1646. $objDrawing->setOffsetX(5);
  1647. $objDrawing->setRotation(5);
  1648. $objDrawing->getShadow()->setVisible(true);
  1649. $objDrawing->getShadow()->setDirection(50);
  1650. $objDrawing->setWorksheet($objPHPExcel->getActiveSheet());
  1651. //设置单元格背景色
  1652. $objPHPExcel->getActiveSheet(0)->getStyle('A1')->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID);
  1653. $objPHPExcel->getActiveSheet(0)->getStyle('A1')->getFill()->getStartColor()->setARGB('FFCAE8EA');
  1654. //输入浏览器,导出Excel
  1655. $savename = '导出Excel示例';
  1656. $ua = $_SERVER["HTTP_USER_AGENT"];
  1657. $datetime = date('Y-m-d', time());
  1658. if (preg_match("/MSIE/", $ua)) {
  1659. $savename = urlencode($savename); //处理IE导出名称乱码
  1660. }
  1661. // excel头参数
  1662. header('Content-Type: application/vnd.ms-excel');
  1663. header('Content-Disposition: attachment;filename="' . $savename . '.xls"'); //日期为文件名后缀
  1664. header('Cache-Control: max-age=0');
  1665. $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5'); //excel5为xls格式,excel2007为xlsx格式
  1666. $objWriter->save('php://output');
  1667. }
  1668. /**
  1669. * 非自定义字段模块导出csv
  1670. * @param $file_name 导出文件名称
  1671. * @param $field_list 导出字段列表
  1672. * @param $callback 回调函数,查询需要导出的数据
  1673. * @author
  1674. **/
  1675. public function dataExportCsv($file_name, $field_list, $callback)
  1676. {
  1677. ini_set('memory_limit', '128M');
  1678. set_time_limit(0);
  1679. //调试时,先把下面这个两个header注释即可
  1680. header("Access-Control-Expose-Headers: Content-Disposition");
  1681. header("Content-type:application/vnd.ms-excel;charset=UTF-8");
  1682. header("Content-Disposition:attachment;filename=" . $file_name . ".csv");
  1683. header('Expires: 0');
  1684. header('Cache-control: private');
  1685. header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
  1686. header('Content-Description: File Transfer');
  1687. header('Content-Encoding: UTF-8');
  1688. // 加上bom头,防止用office打开时乱码
  1689. echo "\xEF\xBB\xBF"; // UTF-8 BOM
  1690. // 打开PHP文件句柄,php://output 表示直接输出到浏览器
  1691. $fp = fopen('php://output', 'a');
  1692. // 将中文标题转换编码,否则乱码
  1693. foreach ($field_list as $i => $v) {
  1694. $title_cell[$i] = $v['name'];
  1695. }
  1696. // 将标题名称通过fputcsv写到文件句柄
  1697. fputcsv($fp, $title_cell);
  1698. // $export_data = $callback(0);
  1699. foreach ($callback as $item) {
  1700. $rows = [];
  1701. foreach ($field_list as $rule) {
  1702. $rows[] = $item[$rule['field']];
  1703. }
  1704. fputcsv($fp, $rows);
  1705. }
  1706. // 将已经写到csv中的数据存储变量销毁,释放内存占用
  1707. ob_flush();
  1708. flush();
  1709. fclose($fp);
  1710. exit();
  1711. }
  1712. /**
  1713. * 分批导入文件 项目任务导入
  1714. *
  1715. * @param null|array|\think\File $file
  1716. * @param array $param
  1717. * @param Controller $controller
  1718. * @return bool
  1719. *
  1720. * @author Ymob
  1721. */
  1722. public function batchTaskImportData($file, $field_list, $param, $controller = null)
  1723. {
  1724. // 导入模块
  1725. $types = $param['types'];
  1726. if (!in_array($types, $this->types_arr)) {
  1727. $this->error = '参数错误!';
  1728. $queue->dequeue();
  1729. return false;
  1730. }
  1731. $user_id = $param['owner_user_id'];
  1732. // 采用伪队列 允许三人同时导入数据
  1733. $queue = new Queue(self::IMPORT_QUEUE, 30000);
  1734. $import_queue_index = input('import_queue_index');
  1735. // 队列任务ID
  1736. if (!$import_queue_index) {
  1737. if (!$import_queue_index = $queue->makeTaskId()) {
  1738. $this->error = $queue->error;
  1739. $queue->dequeue();
  1740. return false;
  1741. }
  1742. } else {
  1743. if (!$queue->setTaskId($import_queue_index)) {
  1744. $this->error = $queue->error;
  1745. $queue->dequeue();
  1746. return false;
  1747. }
  1748. }
  1749. // 取消导入
  1750. if ($param['page'] == -1) {
  1751. @unlink(UPLOAD_PATH . $param['temp_file']);
  1752. $this->error = [
  1753. 'msg' => '导入已取消',
  1754. 'page' => -1
  1755. ];
  1756. if ($param['error']) {
  1757. $this->error['error_file_path'] = 'temp/' . $param['error_file'];
  1758. } else {
  1759. @unlink(TEMP_DIR . $param['error_file']);
  1760. }
  1761. $temp = $queue->cache('last_import_cache');
  1762. (new ImportRecord())->createData([
  1763. 'type' => $types,
  1764. 'total' => $temp['total'],
  1765. 'done' => $temp['done'],
  1766. 'cover' => $temp['cover'],
  1767. 'error' => $temp['error'],
  1768. 'user_id' => $user_id,
  1769. 'error_data_file_path' => $temp['error'] ? 'temp/' . $error_data_file_name : ''
  1770. ]);
  1771. $queue->dequeue();
  1772. return true;
  1773. }
  1774. if (!empty($file) || $param['temp_file']) {
  1775. // 导入初始化 上传文件
  1776. if (!empty($file)) {
  1777. $save_name = $this->upload($file);
  1778. if ($save_name === false) {
  1779. $queue->dequeue();
  1780. return false;
  1781. }
  1782. } else {
  1783. $save_name = $param['temp_file'];
  1784. }
  1785. // 文件类型
  1786. $ext = pathinfo($save_name, PATHINFO_EXTENSION);
  1787. // 文件路径
  1788. $save_path = UPLOAD_PATH . $save_name;
  1789. // 队列-判断是否需要排队
  1790. if (!$queue->canExec()) {
  1791. $this->error = [
  1792. 'temp_file' => $save_name,
  1793. 'page' => -2,
  1794. 'import_queue_index' => $import_queue_index,
  1795. 'info' => $queue->error
  1796. ];
  1797. return true;
  1798. }
  1799. // 加载类库
  1800. vendor("phpexcel.PHPExcel");
  1801. vendor("phpexcel.PHPExcel.Writer.Excel5");
  1802. vendor("phpexcel.PHPExcel.Writer.Excel2007");
  1803. vendor("phpexcel.PHPExcel.IOFactory");
  1804. // 错误数据临时文件路径 错误数据开始行数
  1805. if ($param['error_file']) {
  1806. $error_path = TEMP_DIR . $param['error_file'];
  1807. $error_row = $param['error'] + 3;
  1808. $cover = $param['cover'] ?: 0;
  1809. } else {
  1810. // 生成临时文件名称
  1811. $error_path = tempFileName($ext);
  1812. // 将导入模板保存至临时路径
  1813. $controller->excelDownload($error_path);
  1814. $error_row = 3;
  1815. $cover = 0;
  1816. }
  1817. // 错误数据临时文件名称 相对于临时目录
  1818. $error_data_file_name = \substr($error_path, strlen(TEMP_DIR));
  1819. // 加载错误数据文件
  1820. $err_PHPExcel = \PHPExcel_IOFactory::load($error_path);
  1821. $error_sheet = $err_PHPExcel->setActiveSheetIndex(0);
  1822. /**
  1823. * 添加错误数据到临时文件
  1824. *
  1825. * @param array $data 原数据
  1826. * @param string $error 错误原因
  1827. * @return void
  1828. */
  1829. $error_data_func = function ($data, $error) use ($error_sheet, &$error_row) {
  1830. foreach ($data as $key => $val) {
  1831. // 第一列为错误原因 所以+1
  1832. $error_col = \PHPExcel_Cell::stringFromColumnIndex($key + 1);
  1833. $error_sheet->setCellValue($error_col . $error_row, $val);
  1834. }
  1835. $error_sheet->setCellValue('A' . $error_row, $error);
  1836. $error_sheet->getStyle('A' . $error_row)->getFont()->getColor()->setARGB('FF000000');
  1837. $error_sheet->getStyle('A' . $error_row)->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('FFFF0000');
  1838. $error_row++;
  1839. };
  1840. // 加载导入数据文件
  1841. $objRender = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xls');
  1842. $objRender->setReadDataOnly(true);
  1843. $ExcelObj = $objRender->load($save_path);
  1844. // 指定工作表
  1845. $sheet = $ExcelObj->getSheet(0);
  1846. // 总行数
  1847. $max_row = $sheet->getHighestRow();
  1848. // 最大列数
  1849. $max_col_num = count($field_list) - 1;
  1850. $max_col_num += 3 * array_count_values(array_column($field_list, 'form_type'))['map_address'];
  1851. $max_col = \PHPExcel_Cell::stringFromColumnIndex($max_col_num);
  1852. // 每次导入条数
  1853. $page_size = 100;
  1854. // 当前页码
  1855. $page = ((int)$param['page']) ?: 1;
  1856. // 数据总数
  1857. $total = $max_row - 2;
  1858. // 总页数
  1859. $max_page = ceil($total / $page_size);
  1860. if ($page > $max_page) {
  1861. // $this->error = 'page参数错误';
  1862. // @unlink($save_path);
  1863. // $queue->dequeue();
  1864. // return false;
  1865. }
  1866. // 开始行 +2 跳过表头
  1867. $start_row = ($page - 1) * $page_size + 3;
  1868. // 结束行
  1869. $end_row = $start_row + $page_size - 1;
  1870. if ($end_row > $max_row) {
  1871. $end_row = $max_row;
  1872. }
  1873. // 读取数据
  1874. $dataList = $sheet->rangeToArray("A{$start_row}:{$max_col}{$end_row}");
  1875. // 默认数据
  1876. $default_data = [
  1877. 'main_user_id' => $param['create_user_id'],
  1878. 'create_user_id' => $param['create_user_id'],
  1879. 'create_time' => time(),
  1880. 'update_time' => time(),
  1881. 'work_id' => $param['work_id'],
  1882. ];
  1883. // 开始导入数据
  1884. foreach ($dataList as $val) {
  1885. $data = [];
  1886. $empty_count = 0;
  1887. $not_null_field = [];
  1888. $fk = 0;
  1889. foreach ($field_list as $field) {
  1890. $temp_value = trim($val[$fk]);
  1891. // 特殊字段特殊处理
  1892. // $temp_value = $this->handleData($temp_value, $field);
  1893. $data[$field['field']] = $temp_value;
  1894. if ($temp_value == '') {
  1895. if ($field['is_null']) {
  1896. $not_null_field[] = $field['name'];
  1897. }
  1898. $empty_count++;
  1899. }
  1900. $fk++;
  1901. }
  1902. if (!empty($not_null_field)) {
  1903. $error_data_func($val, implode(', ', $not_null_field) . '不能为空');
  1904. continue;
  1905. }
  1906. if ($empty_count == count($field_list)) {
  1907. $error_data_func($val, '空行');
  1908. continue;
  1909. }
  1910. $classData = db('work_task_class')->where(['name' => $val[6], 'work_id' => $param['work_id']])->order('class_id', 'asc')->select();
  1911. $max_order_id = db('work_task_class')->where(['work_id' => $param['work_id'], 'status' => 1])->max('order_id');
  1912. if ($classData[0]['class_id'] != '') {
  1913. $data['class_id'] = $classData[0]['class_id'];
  1914. } else {
  1915. $item = [
  1916. 'name' => $val[6],
  1917. 'create_time' => time(),
  1918. 'create_user_id' => $param['create_user_id'],
  1919. 'order_id' => $max_order_id ? $max_order_id + 1 : 0,
  1920. 'status' => 1,
  1921. 'work_id' => $param['work_id'],
  1922. ];
  1923. $data['class_id'] = db('work_task_class')->insertGetId($item);
  1924. }
  1925. $dataModel = new \app\work\model\Task();
  1926. $data = array_merge($data, $default_data);
  1927. if (!$resData = $dataModel->createTask($data)) {
  1928. $error_data_func($val, $dataModel->getError());
  1929. }
  1930. }
  1931. // 完成数(已导入数)
  1932. $done = ($page - 1) * $page_size + count($dataList);
  1933. if ($page == $max_page) {
  1934. $done = $total;
  1935. }
  1936. // 错误数
  1937. $error = $error_row - 3;
  1938. // 错误数据文件保存
  1939. $objWriter = \PHPExcel_IOFactory::createWriter($err_PHPExcel, 'Excel5');
  1940. $objWriter->save($error_path);
  1941. $this->error = [
  1942. // 数据导入文件临时路径
  1943. 'temp_file' => $save_name,
  1944. // 错误数据文件路径
  1945. 'error_file' => $error_data_file_name,
  1946. // 文件总计条数
  1947. 'total' => $total,
  1948. // 已完成条数
  1949. 'done' => $done,
  1950. // 覆盖
  1951. 'cover' => $cover,
  1952. // 错误数据写入行号
  1953. 'error' => $error,
  1954. // 下次页码
  1955. 'page' => $page + 1,
  1956. // 导入任务ID
  1957. 'import_queue_index' => $import_queue_index
  1958. ];
  1959. $queue->cache('last_import_cache', [
  1960. 'total' => $total,
  1961. 'done' => $done,
  1962. 'cover' => $cover,
  1963. 'error' => $error
  1964. ]);
  1965. // 执行完成
  1966. if ($done >= $total) {
  1967. // 出队
  1968. $queue->dequeue();
  1969. // 错误数据文件路径
  1970. $this->error['error_file_path'] = 'temp/' . $error_data_file_name;
  1971. // 删除导入文件
  1972. @unlink($save_path);
  1973. // 没有错误数据时,删除错误文件
  1974. if ($error == 0) {
  1975. @unlink($error_path);
  1976. }
  1977. (new ImportRecord())->createData([
  1978. 'type' => $types,
  1979. 'total' => $total,
  1980. 'done' => $done,
  1981. 'cover' => $cover,
  1982. 'error' => $error,
  1983. 'user_id' => $user_id,
  1984. 'error_data_file_path' => $error ? 'temp/' . $error_data_file_name : ''
  1985. ]);
  1986. Cache::set('item', 1, config('import_cache_time'));
  1987. Cache::set('excel_item', serialize($this->error), config('import_cache_time'));
  1988. } else {
  1989. $excelData['cover'] = $cover;
  1990. $excelData['page'] = $page + 1;
  1991. $excelData['types'] = $types;
  1992. $excelData['temp_file'] = $save_name;
  1993. $excelData['error_file'] = $error_data_file_name;
  1994. $excelData['create_user_id'] = $param['create_user_id'];
  1995. $excelData['import_queue_index'] = $import_queue_index;
  1996. $excelData['owner_user_id'] = $user_id;
  1997. $excelData['total'] = $total;
  1998. $excelData['done'] = $done;
  1999. $excelData['error'] = $error;
  2000. $excelData['base'] = 'batchTaskImportData';
  2001. Cache::set('item', 0, config('import_cache_time'));
  2002. Cache::set('excel', $excelData, config('import_cache_time'));
  2003. }
  2004. return true;
  2005. } else {
  2006. $this->error = '请选择导入文件';
  2007. $queue->dequeue();
  2008. return false;
  2009. }
  2010. }
  2011. /**
  2012. * task模块导出csv
  2013. * @param $title 导出文件头
  2014. * @param $file_name 导出文件名称
  2015. * @param $field_list 导出字段列表
  2016. * @param $callback 回调函数,查询需要导出的数据
  2017. * @author
  2018. **/
  2019. public function taskExportCsv($file_name, $field_list, $title, $callback)
  2020. {
  2021. ini_set('memory_limit', '128M');
  2022. set_time_limit(0);
  2023. vendor("PHPExcel.PHPExcel.PHPExcel");
  2024. vendor("PHPExcel.PHPExcel.Writer.Excel5");
  2025. vendor("PHPExcel.PHPExcel.Writer.Excel2007");
  2026. vendor("PHPExcel.PHPExcel.IOFactory");
  2027. //实例化
  2028. $objPHPExcel = new \PHPExcel();
  2029. //定义配置
  2030. $topNumber = 2;//表头有几行占用
  2031. $xlsTitle = iconv('utf-8', 'gb2312', $title);//文件名称
  2032. $cellKey = array(
  2033. 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
  2034. 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  2035. 'AA', 'AB', 'AC', 'AD', 'AE', 'AF', 'AG', 'AH', 'AI', 'AJ', 'AK', 'AL', 'AM',
  2036. 'AN', 'AO', 'AP', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AV', 'AW', 'AX', 'AY', 'AZ'
  2037. );
  2038. //处理表头标题
  2039. $objActSheet = $objPHPExcel->getActiveSheet(0);
  2040. $objPHPExcel->getActiveSheet()->mergeCells('A1:' . $cellKey[count($field_list) - 1] . '1');//合并单元格(如果要拆分单元格是需要先合并再拆分的,否则程序会报错)
  2041. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('A1', $title);
  2042. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setName('宋体');
  2043. $objPHPExcel->getActiveSheet()->getDefaultRowDimension()->setRowHeight(18);//所有单元格(行)默认高度
  2044. $objPHPExcel->getActiveSheet()->getDefaultColumnDimension()->setWidth(18);//所有单元格(列)默认宽度
  2045. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setBold(true);
  2046. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setSize(11);
  2047. $objPHPExcel->getActiveSheet()->getStyle('A1:' . $cellKey[count($field_list) - 1] . '1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  2048. $objPHPExcel->getActiveSheet()->getStyle('A1:' . $cellKey[count($field_list) - 1] . '1')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  2049. $objActSheet->getStyle('A1:' . $cellKey[count($field_list) - 1] . '1')->getFont()->getColor()->setARGB('FF000000');
  2050. $objActSheet->getStyle('A1:' . $cellKey[count($field_list) - 1] . '1')->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('##00BFFF');
  2051. //处理表头
  2052. foreach ($field_list as $k => $v) {
  2053. $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellKey[$k] . $topNumber, $v['name']);//设置表头数据
  2054. $objPHPExcel->getActiveSheet()->freezePane($cellKey[$k] . ($topNumber + 1));//冻结窗口
  2055. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setName('宋体');
  2056. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setSize(11);
  2057. $objPHPExcel->getActiveSheet()->getStyle($cellKey[$k] . $topNumber)->getFont()->setBold(true);//设置是否加粗
  2058. $objPHPExcel->getActiveSheet()->getStyle($cellKey[$k] . $topNumber)->getAlignment()->setVertical(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);//垂直居中
  2059. $objPHPExcel->getActiveSheet()->getStyle('A2:' . $cellKey[count($field_list) - 1] . '2')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  2060. $objPHPExcel->getActiveSheet()->getStyle('A2:' . $cellKey[count($field_list) - 1] . '2')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  2061. if ($v[3] > 0)//大于0表示需要设置宽度
  2062. {
  2063. $objPHPExcel->getActiveSheet()->getColumnDimension($cellKey[$k])->setWidth($v[3]);//设置列宽度
  2064. }
  2065. }
  2066. $objActSheet->getStyle('A2:' . $cellKey[count($field_list) - 1] . '2')->getFont()->getColor()->setARGB('FF000000');
  2067. $objActSheet->getStyle('A2:' . $cellKey[count($field_list) - 1] . '2')->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('##00BFFF');
  2068. //边框
  2069. // $callCount = count($callback) + 2;
  2070. // $style_array = array(
  2071. // 'borders' => array(
  2072. // 'allborders' => array(
  2073. // 'style' => \PHPExcel_Style_Border::BORDER_THIN
  2074. // )
  2075. // ));
  2076. // $objActSheet->getStyle('A1:' . $cellKey[count($field_list) - 1] . $callCount)->applyFromArray($style_array);
  2077. foreach ($callback as $k => $item) {
  2078. foreach ($field_list as $key => $rule) {
  2079. $objPHPExcel->getActiveSheet()->getStyle($cellKey[$key] . ($k + 1 + $topNumber) . ':' . $cellKey[count($field_list) - 1] . ($k + 1 + $topNumber))->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  2080. $objPHPExcel->getActiveSheet()->getStyle($cellKey[$key] . ($k + 1 + $topNumber) . ':' . $cellKey[count($field_list) - 1] . ($k + 1 + $topNumber))->getAlignment()->setVertical(\PHPExcel_Style_Alignment::VERTICAL_CENTER);
  2081. $objPHPExcel->getActiveSheet()->setCellValue($cellKey[$key] . ($k + 1 + $topNumber), $item[$rule['field']]);
  2082. }
  2083. }
  2084. // excel头参数
  2085. header('Content-Type: application/vnd.ms-excel');
  2086. header('Content-Disposition: attachment;filename="' . $file_name . '.xls"'); //日期为文件名后缀
  2087. header('Cache-Control: max-age=0');
  2088. $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5'); //excel5为xls格式,excel2007为xlsx格式
  2089. $objWriter->save('php://output');
  2090. }
  2091. /**
  2092. * 商业智能模块导出csv
  2093. * @param $file_name 导出文件名称
  2094. * @param $field_list 导出字段列表
  2095. * @param $callback 回调函数,查询需要导出的数据
  2096. * @author
  2097. **/
  2098. public function biExportExcel($file_name, $field_list, $title, $callback)
  2099. {
  2100. ini_set('memory_limit', '128M');
  2101. set_time_limit(0);
  2102. // 加载类库
  2103. vendor("phpexcel.PHPExcel");
  2104. vendor("phpexcel.PHPExcel.Writer.Excel5");
  2105. vendor("phpexcel.PHPExcel.Writer.Excel2007");
  2106. vendor("phpexcel.PHPExcel.IOFactory");
  2107. $objPHPExcel = new \PHPExcel();
  2108. //定义配置
  2109. $topNumber = 2;//表头有几行占用
  2110. $xlsTitle = iconv('utf-8', 'gb2312', $title);//文件名称
  2111. $cellKey = array(
  2112. 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
  2113. 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  2114. 'AA', 'AB', 'AC', 'AD', 'AE', 'AF', 'AG', 'AH', 'AI', 'AJ', 'AK', 'AL', 'AM',
  2115. 'AN', 'AO', 'AP', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AV', 'AW', 'AX', 'AY', 'AZ'
  2116. );
  2117. //处理表头标题
  2118. $objActSheet = $objPHPExcel->getActiveSheet(0);
  2119. $objPHPExcel->getActiveSheet()->mergeCells('A1:' . $cellKey[count($field_list) - 1] . '1');//合并单元格(如果要拆分单元格是需要先合并再拆分的,否则程序会报错)
  2120. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('A1', $title);
  2121. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setName('宋体');
  2122. $objPHPExcel->getActiveSheet()->getDefaultRowDimension()->setRowHeight(18);//所有单元格(行)默认高度
  2123. $objPHPExcel->getActiveSheet()->getDefaultColumnDimension()->setWidth(18);//所有单元格(列)默认宽度
  2124. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setBold(true);
  2125. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setSize(11);
  2126. $objPHPExcel->getActiveSheet()->getStyle('A1:' . $cellKey[count($field_list) - 1] . '1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  2127. $objPHPExcel->getActiveSheet()->getStyle('A1:' . $cellKey[count($field_list) - 1] . '1')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  2128. $objActSheet->getStyle('A1:' . $cellKey[count($field_list) - 1] . '1')->getFont()->getColor()->setARGB('FF000000');
  2129. $objActSheet->getStyle('A1:' . $cellKey[count($field_list) - 1] . '1')->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('##00BFFF');
  2130. //处理表头
  2131. foreach ($field_list as $k => $v) {
  2132. $objPHPExcel->setActiveSheetIndex(0)->setCellValue($cellKey[$k] . $topNumber, $v['name']);//设置表头数据
  2133. $objPHPExcel->getActiveSheet()->freezePane($cellKey[$k] . ($topNumber + 1));//冻结窗口
  2134. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setName('宋体');
  2135. $objPHPExcel->getActiveSheet()->getStyle($cellKey[$k] . $topNumber)->getFont()->setBold(true);//设置是否加粗
  2136. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setSize(11);
  2137. $objPHPExcel->getActiveSheet()->getStyle($cellKey[$k] . $topNumber)->getAlignment()->setVertical(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);//垂直居中
  2138. $objPHPExcel->getActiveSheet()->getStyle('A2:' . $cellKey[count($field_list) - 1] . '2')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  2139. $objPHPExcel->getActiveSheet()->getStyle('A2:' . $cellKey[count($field_list) - 1] . '2')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::HORIZONTAL_CENTER);
  2140. if ($v[3] > 0)//大于0表示需要设置宽度
  2141. {
  2142. $objPHPExcel->getActiveSheet()->getColumnDimension($cellKey[$k])->setWidth($v[3]);//设置列宽度
  2143. }
  2144. }
  2145. $objActSheet->getStyle('A2:' . $cellKey[count($field_list) - 1] . '2')->getFont()->getColor()->setARGB('FF000000');
  2146. $objActSheet->getStyle('A2:' . $cellKey[count($field_list) - 1] . '2')->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('##00BFFF');
  2147. $callCount = count($callback) + 2;
  2148. $style_array = array(
  2149. 'borders' => array(
  2150. 'allborders' => array(
  2151. 'style' => \PHPExcel_Style_Border::BORDER_THIN
  2152. )
  2153. ));
  2154. $objActSheet->getStyle('A1:' . $cellKey[count($field_list) - 1] . $callCount)->applyFromArray($style_array);
  2155. foreach ($callback as $k => $item) {
  2156. foreach ($field_list as $key => $rule) {
  2157. $objPHPExcel->getActiveSheet()->getStyle($cellKey[$key] . ($k + 1 + $topNumber) . ':' . $cellKey[count($field_list) - 1] . ($k + 1 + $topNumber))->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  2158. $objPHPExcel->getActiveSheet()->getStyle($cellKey[$key] . ($k + 1 + $topNumber) . ':' . $cellKey[count($field_list) - 1] . ($k + 1 + $topNumber))->getAlignment()->setVertical(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  2159. $objPHPExcel->getActiveSheet()->setCellValue($cellKey[$key] . ($k + 1 + $topNumber), $item[$rule['field']]);
  2160. }
  2161. }
  2162. // excel头参数
  2163. header('Content-Type: application/vnd.ms-excel');
  2164. header('Content-Disposition: attachment;filename="' . $file_name . '.xls"'); //日期为文件名后缀
  2165. header('Cache-Control: max-age=0');
  2166. $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5'); //excel5为xls格式,excel2007为xlsx格式
  2167. $objWriter->save('php://output');
  2168. }
  2169. /**
  2170. * bi 员工业绩导出
  2171. * @param $file_name
  2172. * @param $field_list
  2173. * @param $param
  2174. * @param $title
  2175. * @throws \PHPExcel_Reader_Exception
  2176. * @throws \PHPExcel_Writer_Exception
  2177. */
  2178. public function template_download($file_name, $field_list, $title, $callback)
  2179. {
  2180. // 加载类库
  2181. vendor("phpexcel.PHPExcel");
  2182. vendor("phpexcel.PHPExcel.Writer.Excel5");
  2183. vendor("phpexcel.PHPExcel.Writer.Excel2007");
  2184. vendor("phpexcel.PHPExcel.IOFactory");
  2185. $objPHPExcel = new \PHPExcel();
  2186. //定义配置
  2187. $topNumber = 2;//表头有几行占用
  2188. $xlsTitle = iconv('utf-8', 'gb2312', $title);//文件名称
  2189. $cellKey = array(
  2190. 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
  2191. 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  2192. 'AA', 'AB', 'AC', 'AD', 'AE', 'AF', 'AG', 'AH', 'AI', 'AJ', 'AK', 'AL', 'AM',
  2193. 'AN', 'AO', 'AP', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AV', 'AW', 'AX', 'AY', 'AZ'
  2194. );
  2195. //处理表头标题
  2196. $objActSheet = $objPHPExcel->getActiveSheet(0);
  2197. $objPHPExcel->getActiveSheet()->mergeCells('A1:M1');//合并单元格(如果要拆分单元格是需要先合并再拆分的,否则程序会报错)
  2198. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('A1', $title);
  2199. $objPHPExcel->getActiveSheet()->getDefaultRowDimension()->setRowHeight(18);//所有单元格(行)默认高度
  2200. $objPHPExcel->getActiveSheet()->getDefaultColumnDimension()->setWidth(18);//所有单元格(列)默认宽度
  2201. // 设置字体
  2202. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setName('宋体');
  2203. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setBold(true);
  2204. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setSize(11);
  2205. $objPHPExcel->getActiveSheet()->getStyle('A1:M1')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  2206. $objPHPExcel->getActiveSheet()->getStyle('A1:M1')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  2207. $objActSheet->getStyle('A1:M1')->getFont()->getColor()->setARGB('FF000000');
  2208. $objActSheet->getStyle('A1:M1')->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('##00BFFF');
  2209. //处理表头
  2210. $objPHPExcel->getActiveSheet()->freezePane('A2');//冻结窗口
  2211. // 设置字体为
  2212. $objPHPExcel->getActiveSheet()->getStyle('A1')->getFont()->setName('宋体');
  2213. $objPHPExcel->getActiveSheet()->getStyle('A2')->getFont()->setBold(true);//设置是否加粗
  2214. $objPHPExcel->getActiveSheet()->getStyle('A2')->getFont()->setSize(11);
  2215. $objPHPExcel->getActiveSheet()->getStyle('A2')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);//垂直居中
  2216. $objPHPExcel->getActiveSheet()->getStyle('A2:M2')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  2217. $objPHPExcel->getActiveSheet()->getStyle('A2:M2')->getAlignment()->setVertical(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);
  2218. $objPHPExcel->getActiveSheet()->getStyle('A2:M2')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);//文字居中
  2219. $objActSheet->getStyle('A2:M2')->getFont()->getColor()->setARGB('FF000000');
  2220. $objActSheet->getStyle('A2:M2')->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('##00BFFF');
  2221. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('A2', '日期');
  2222. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('A3', '当月回款金额(元)');
  2223. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('A4', '环比增长(%)');
  2224. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('A5', '同比增长(%)');
  2225. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('B2', '202001');
  2226. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('C2', '202002');
  2227. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('D2', '202003');
  2228. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('E2', '202004');
  2229. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('F2', '202005');
  2230. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('G2', '202006');
  2231. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('H2', '202007');
  2232. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('I2', '202008');
  2233. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('J2', '202009');
  2234. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('K2', '202010');
  2235. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('L2', '202011');
  2236. $objPHPExcel->setActiveSheetIndex(0)->setCellValue('M2', '202012');
  2237. $baseRow = 3; //数据从N-1行开始往下输出 这里是避免头信息被覆盖
  2238. $callCount = count($callback) + 2;
  2239. $style_array = array(
  2240. 'borders' => array(
  2241. 'allborders' => array(
  2242. 'style' => \PHPExcel_Style_Border::BORDER_THIN
  2243. )
  2244. ));
  2245. $objActSheet->getStyle('A1:M5')->applyFromArray($style_array);
  2246. foreach ($callback as $key => $value) {
  2247. $k = $key + 1;
  2248. $objPHPExcel->getActiveSheet()->setCellValue($cellKey[$k] . '3', $value['thisMonth']);
  2249. $objPHPExcel->getActiveSheet()->setCellValue($cellKey[$k] . '4', $value['chain_ratio']);
  2250. $objPHPExcel->getActiveSheet()->setCellValue($cellKey[$k] . '5', $value['year_on_year']);
  2251. }
  2252. // excel头参数
  2253. header('Content-Type: application/vnd.ms-excel');
  2254. header('Content-Disposition: attachment;filename="' . $file_name . '.xls"'); //日期为文件名后缀
  2255. header('Cache-Control: max-age=0');
  2256. $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5'); //excel5为xls格式,excel2007为xlsx格式
  2257. $objWriter->save('php://output');
  2258. }
  2259. /**
  2260. * 运行中
  2261. * @param $param
  2262. * @return int|null
  2263. */
  2264. public function importNum()
  2265. {
  2266. $param = Cache::pull('item');
  2267. $excelData = Cache::pull('excel');
  2268. $base = $excelData['base'];
  2269. if ($param == 0) {
  2270. if ($base == 'batchTaskImportData') {
  2271. $this->batchTaskImportData('', $excelData);
  2272. } elseif ($base == 'ActivityImport') {
  2273. $this->ActivityImport('', $excelData);
  2274. } elseif($base == 'batchImportData') {
  2275. $this->batchImportData('', $excelData);
  2276. }
  2277. $data = 0;
  2278. } elseif ($param == 1) {
  2279. $data = '';
  2280. }
  2281. return $data;
  2282. }
  2283. /**
  2284. * 结果
  2285. * @param $param
  2286. * @return int|null
  2287. */
  2288. public function importInfo()
  2289. {
  2290. $param = Cache::pull('excel_item');
  2291. $param = unserialize($param);
  2292. return $param;
  2293. }
  2294. /**
  2295. * 导入记录
  2296. * @param $param
  2297. * @return array
  2298. */
  2299. public function importList($param)
  2300. {
  2301. $list = db('admin_import_record')->alias('i')
  2302. ->join('admin_user user', 'i.user_id=user.id')
  2303. ->where(['i.type' => $param['type'], 'i.user_id' => $param['user_id']])->page($param['page'], $param['limit'])
  2304. ->field('i.*,user.realname as user_name')->order('create_time desc')->select();
  2305. $dataCount = db('admin_import_record')->where('type', $param['type'])->count();
  2306. $week = strtotime(date("Y-m-d H:i:s", strtotime("+7 day")));
  2307. $time = time();
  2308. foreach ($list as $k => $v) {
  2309. $week = strtotime("+7 day", $v['create_time']);
  2310. if ($time > (int)$week) {
  2311. $list[$k]['valid'] = 0;
  2312. } else {
  2313. $list[$k]['valid'] = 1;
  2314. }
  2315. if ($v['error_data_file_path'] == '') {
  2316. $list[$k]['valid'] = -1;
  2317. }
  2318. $list[$k]['create_time'] = date('Y-m-d', $v['create_time']);
  2319. }
  2320. $data = [];
  2321. $data['list'] = $list;
  2322. $data['dataCount'] = $dataCount ?: 0;
  2323. if ($param['page'] != 1 && ($param['page'] * $param['limit']) >= $dataCount) {
  2324. $data['firstPage'] = false;
  2325. $data['lastPage'] = true;
  2326. } else if ($param['page'] != 1 && (int)($param['page'] * $param['limit']) < $dataCount) {
  2327. $data['firstPage'] = false;
  2328. $data['lastPage'] = false;
  2329. } else if ($param['page'] == 1 && (int)($param['page'] * $param['limit']) < $dataCount) {
  2330. $data['firstPage'] = true;
  2331. $data['lastPage'] = false;
  2332. }
  2333. return $data;
  2334. }
  2335. /**
  2336. * 跟进记录导入
  2337. * @param $file 文件
  2338. * @param $param 数据
  2339. * @param $field_list 导入字段
  2340. *
  2341. * @author alvin guogaobo
  2342. * @version 1.0 版本号
  2343. * @since 2021/4/9 0009 16:31
  2344. */
  2345. public function ActivityImport($file, $field_list, $param, $controller = null)
  2346. {
  2347. // 导入模块
  2348. $types = $param['types'];
  2349. if (!in_array($types, $this->types_arr)) {
  2350. $this->error = '参数错误!';
  2351. $queue->dequeue();
  2352. return false;
  2353. }
  2354. $user_id = $param['user_id'];
  2355. // 采用伪队列 允许三人同时导入数据
  2356. $queue = new Queue(self::IMPORT_QUEUE, 30000);
  2357. $import_queue_index = input('import_queue_index');
  2358. // 队列任务ID
  2359. if (!$import_queue_index) {
  2360. if (!$import_queue_index = $queue->makeTaskId()) {
  2361. $this->error = $queue->error;
  2362. $queue->dequeue();
  2363. return false;
  2364. }
  2365. } else {
  2366. if (!$queue->setTaskId($import_queue_index)) {
  2367. $this->error = $queue->error;
  2368. $queue->dequeue();
  2369. return false;
  2370. }
  2371. }
  2372. // 取消导入
  2373. if ($param['page'] == -1) {
  2374. @unlink(UPLOAD_PATH . $param['temp_file']);
  2375. $this->error = [
  2376. 'msg' => '导入已取消',
  2377. 'page' => -1
  2378. ];
  2379. if ($param['error']) {
  2380. $this->error['error_file_path'] = 'temp/' . $param['error_file'];
  2381. } else {
  2382. @unlink(TEMP_DIR . $param['error_file']);
  2383. }
  2384. $temp = $queue->cache('last_import_cache');
  2385. (new ImportRecord())->createData([
  2386. 'type' => $types,
  2387. 'total' => $temp['total'],
  2388. 'done' => $temp['done'],
  2389. 'cover' => $temp['cover'],
  2390. 'error' => $temp['error'],
  2391. 'user_id' => $user_id,
  2392. 'error_data_file_path' => $temp['error'] ? 'temp/' . $error_data_file_name : ''
  2393. ]);
  2394. $queue->dequeue();
  2395. return true;
  2396. }
  2397. if (!empty($file) || $param['temp_file']) {
  2398. // 导入初始化 上传文件
  2399. if (!empty($file)) {
  2400. $save_name = $this->upload($file);
  2401. if ($save_name === false) {
  2402. $queue->dequeue();
  2403. return false;
  2404. }
  2405. } else {
  2406. $save_name = $param['temp_file'];
  2407. }
  2408. // 文件类型
  2409. $ext = pathinfo($save_name, PATHINFO_EXTENSION);
  2410. // 文件路径
  2411. $save_path = UPLOAD_PATH . $save_name;
  2412. // 队列-判断是否需要排队
  2413. if (!$queue->canExec()) {
  2414. $this->error = [
  2415. 'temp_file' => $save_name,
  2416. 'page' => -2,
  2417. 'import_queue_index' => $import_queue_index,
  2418. 'info' => $queue->error
  2419. ];
  2420. return true;
  2421. }
  2422. // 加载类库
  2423. vendor("phpexcel.PHPExcel");
  2424. vendor("phpexcel.PHPExcel.Writer.Excel5");
  2425. vendor("phpexcel.PHPExcel.Writer.Excel2007");
  2426. vendor("phpexcel.PHPExcel.IOFactory");
  2427. // 错误数据临时文件路径 错误数据开始行数
  2428. if ($param['error_file']) {
  2429. $error_path = TEMP_DIR . $param['error_file'];
  2430. $error_row = $param['error'] + 3;
  2431. $cover = $param['cover'] ?: 0;
  2432. } else {
  2433. // 生成临时文件名称
  2434. $error_path = tempFileName($ext);
  2435. // 将导入模板保存至临时路径
  2436. $controller->excelDownload($error_path);
  2437. $error_row = 3;
  2438. $cover = 0;
  2439. }
  2440. // 错误数据临时文件名称 相对于临时目录
  2441. $error_data_file_name = \substr($error_path, strlen(TEMP_DIR));
  2442. // 加载错误数据文件
  2443. $err_PHPExcel = \PHPExcel_IOFactory::load($error_path);
  2444. $error_sheet = $err_PHPExcel->setActiveSheetIndex(0);
  2445. /**
  2446. * 添加错误数据到临时文件
  2447. *
  2448. * @param array $data 原数据
  2449. * @param string $error 错误原因
  2450. * @return void
  2451. */
  2452. $error_data_func = function ($data, $error) use ($error_sheet, &$error_row) {
  2453. foreach ($data as $key => $val) {
  2454. // 第一列为错误原因 所以+1
  2455. $error_col = \PHPExcel_Cell::stringFromColumnIndex($key + 1);
  2456. $error_sheet->setCellValue($error_col . $error_row, $val);
  2457. }
  2458. $error_sheet->setCellValue('A' . $error_row, $error);
  2459. $error_sheet->getStyle('A' . $error_row)->getFont()->getColor()->setARGB('FF000000');
  2460. $error_sheet->getStyle('A' . $error_row)->getFill()->setFillType(\PHPExcel_Style_Fill::FILL_SOLID)->getStartColor()->setARGB('FFFF0000');
  2461. $error_row++;
  2462. };
  2463. // 加载导入数据文件
  2464. $objRender = \PhpOffice\PhpSpreadsheet\IOFactory::createReader('Xls');
  2465. $objRender->setReadDataOnly(true);
  2466. $ExcelObj = $objRender->load($save_path);
  2467. // 指定工作表
  2468. $sheet = $ExcelObj->getSheet(0);
  2469. // 总行数
  2470. $max_row = $sheet->getHighestRow();
  2471. // 最大列数
  2472. $max_col_num = count($field_list) - 1;
  2473. $max_col_num += 3 * array_count_values(array_column($field_list, 'form_type'))['map_address'];
  2474. $max_col = \PHPExcel_Cell::stringFromColumnIndex($max_col_num);
  2475. // 每次导入条数
  2476. $page_size = 100;
  2477. // 当前页码
  2478. $page = ((int)$param['page']) ?: 1;
  2479. // 数据总数
  2480. $total = $max_row - 2;
  2481. // 总页数
  2482. $max_page = ceil($total / $page_size);
  2483. if ($page > $max_page) {
  2484. // $this->error = 'page参数错误';
  2485. // @unlink($save_path);
  2486. // $queue->dequeue();
  2487. // return false;
  2488. }
  2489. // 开始行 +3 跳过表头
  2490. $start_row = ($page - 1) * $page_size + 3;
  2491. // 结束行
  2492. $end_row = $start_row + $page_size - 1;
  2493. if ($end_row > $max_row) {
  2494. $end_row = $max_row;
  2495. }
  2496. // 读取数据
  2497. $dataList = $sheet->rangeToArray("A{$start_row}:{$max_col}{$end_row}");
  2498. switch ($types) {
  2499. case 'crm_business':
  2500. $dataModel = new \app\crm\model\Business();
  2501. $db = db('crm_business');
  2502. $db_id = 'business_id';
  2503. $activity_type = 5;
  2504. $activity_name='所属商机';
  2505. break;
  2506. case 'crm_contract':
  2507. $db = db('crm_contract');
  2508. $db_id = 'contract_id';
  2509. $activity_type = 6;
  2510. $activity_name='所属合同';
  2511. break;
  2512. case 'crm_leads' :
  2513. $dataModel = new \app\crm\model\Leads();
  2514. $db = db('crm_leads');
  2515. $db_id = 'leads_id';
  2516. $activity_type = 1;
  2517. $activity_name='所属线索';
  2518. break;
  2519. case 'crm_customer' :
  2520. $dataModel = new \app\crm\model\Customer();
  2521. $db = db('crm_customer');
  2522. $db_id = 'customer_id';
  2523. $fieldParam['form_type'] = ['not in', ['file', 'form', 'user', 'structure']];
  2524. $activity_type = 2;
  2525. $activity_name='所属客户';
  2526. break;
  2527. case 'crm_contacts' :
  2528. $dataModel = new \app\crm\model\Contacts();
  2529. $db = db('crm_contacts');
  2530. $db_id = 'contacts_id';
  2531. $activity_type = 3;
  2532. $activity_name='所属联系人';
  2533. break;
  2534. }
  2535. // 开始导入数据
  2536. foreach ($dataList as $val) {
  2537. $fk = 0;
  2538. $data = [];
  2539. foreach ($field_list as $field) {
  2540. $temp_value = trim($val[$fk]);
  2541. // 特殊字段特殊处理
  2542. // $temp_value = $this->handleData($temp_value, $field);
  2543. $data[$field['field']] = $temp_value;
  2544. if ($temp_value == '') {
  2545. if ($field['is_null']) {
  2546. $not_null_field[] = $field['name'];
  2547. }
  2548. $empty_count++;
  2549. }
  2550. $fk++;
  2551. }
  2552. if (!empty($not_null_field)) {
  2553. $error_data_func($val, implode(', ', $not_null_field) . '不能为空');
  2554. continue;
  2555. }
  2556. if ($empty_count == count($field_list)) {
  2557. $error_data_func($val, '空行');
  2558. continue;
  2559. }
  2560. $activityLogic = new \app\crm\logic\ActivityLogic();
  2561. $userData = db('admin_user')->where(['realname' => $val[1], 'status' => ['neq', 0]])->value('id');
  2562. $customerData = $db->where('name', $val[2])->value($db_id);
  2563. $classData = db('crm_activity')->where(['content' => $val[0], 'activity_type' => $param['activity_type'], 'activity_type_id' => $customerData])->order('activity_id', 'asc')->select();
  2564. if (empty($customerData)) {
  2565. $error_data_func($val, $activity_name . $val[2] . '不存在');
  2566. continue;
  2567. }
  2568. if (empty($userData)) {
  2569. $error_data_func($val, '管理员' . $val[1] . '不存在');
  2570. continue;
  2571. }
  2572. if ($types == 'crm_customer' && (!empty($val[5]) || !empty($val[6]))) {
  2573. if(strstr($val[5], '/')||strstr($val[6], '/')){
  2574. $val[5]= explode ( '/' , $val[5]);
  2575. $val[6]= explode ( '/' , $val[6]);
  2576. }
  2577. $contactsData = db('crm_contacts')->where('name', ['in',arrayToString($val[5])])->column('contacts_id');
  2578. $businessData = db('crm_business')->where('name', ['in',arrayToString($val[6])] )->column('business_id');
  2579. $data['business_ids']=arrayToString($businessData);
  2580. $data['contacts_ids']=arrayToString($contactsData);
  2581. }
  2582. // if ($classData && $classData[0]['activity_id'] != '') {
  2583. // $data['activity_id'] = $classData[0]['activity_id'];
  2584. // $data['activity_type_id'] = $customerData;
  2585. // $data['activity_type'] = $activity_type;
  2586. // $data['create_user_id'] = $user_id;
  2587. // if (!$resData = $activityLogic->update($data)) {
  2588. // $error_data_func($val, $dataModel->getError());
  2589. // }
  2590. // } else { }
  2591. $data['user_id'] = $userData;
  2592. $data['activity_type_id'] = $customerData;
  2593. $data['activity_type'] = $activity_type;
  2594. $data['excel'] = 1;
  2595. $data['create_user_id'] = $user_id;
  2596. unset($data['create_user_id']);
  2597. if (!$resData = $activityLogic->save($data)) {
  2598. $error_data_func($val, $dataModel->getError());
  2599. }
  2600. }
  2601. // 完成数(已导入数)
  2602. $done = ($page - 1) * $page_size + count($dataList);
  2603. if ($page == $max_page) {
  2604. $done = $total;
  2605. }
  2606. // 错误数
  2607. $error = $error_row - 3;
  2608. // 错误数据文件保存
  2609. $objWriter = \PHPExcel_IOFactory::createWriter($err_PHPExcel, 'Excel5');
  2610. $objWriter->save($error_path);
  2611. $this->error = [
  2612. // 数据导入文件临时路径
  2613. 'temp_file' => $save_name,
  2614. // 错误数据文件路径
  2615. 'error_file' => $error_data_file_name,
  2616. // 文件总计条数
  2617. 'total' => $total,
  2618. // 已完成条数
  2619. 'done' => $done,
  2620. // 覆盖
  2621. 'cover' => $cover,
  2622. // 错误数据写入行号
  2623. 'error' => $error,
  2624. // 下次页码
  2625. 'page' => $page + 1,
  2626. // 导入任务ID
  2627. 'import_queue_index' => $import_queue_index
  2628. ];
  2629. $queue->cache('last_import_cache', [
  2630. 'total' => $total,
  2631. 'done' => $done,
  2632. 'cover' => $cover,
  2633. 'error' => $error
  2634. ]);
  2635. // 执行完成
  2636. if ($done >= $total) {
  2637. // 出队
  2638. $queue->dequeue();
  2639. // 错误数据文件路径
  2640. $this->error['error_file_path'] = 'temp/' . $error_data_file_name;
  2641. // 删除导入文件
  2642. @unlink($save_path);
  2643. // 没有错误数据时,删除错误文件
  2644. if ($error == 0) {
  2645. @unlink($error_path);
  2646. }
  2647. (new ImportRecord())->createData([
  2648. 'type' => $types,
  2649. 'total' => $total,
  2650. 'done' => $done,
  2651. 'cover' => $cover,
  2652. 'error' => $error,
  2653. 'user_id' => $user_id,
  2654. 'error_data_file_path' => $error ? 'temp/' . $error_data_file_name : ''
  2655. ]);
  2656. Cache::set('item', 1, config('import_cache_time'));
  2657. Cache::set('excel_item', serialize($this->error), config('import_cache_time'));
  2658. } else {
  2659. $excelData['cover'] = $cover;
  2660. $excelData['page'] = $page + 1;
  2661. $excelData['types'] = $types;
  2662. $excelData['temp_file'] = $save_name;
  2663. $excelData['error_file'] = $error_data_file_name;
  2664. $excelData['create_user_id'] = $param['create_user_id'];
  2665. $excelData['import_queue_index'] = $import_queue_index;
  2666. $excelData['total'] = $total;
  2667. $excelData['done'] = $done;
  2668. $excelData['error'] = $error;
  2669. $excelData['base'] = 'ActivityImport';
  2670. Cache::set('item', 0, config('import_cache_time'));
  2671. Cache::set('excel', $excelData, config('import_cache_time'));
  2672. }
  2673. return true;
  2674. } else {
  2675. $this->error = '请选择导入文件';
  2676. $queue->dequeue();
  2677. return false;
  2678. }
  2679. }
  2680. }