Records.php 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. <?php
  2. namespace app\admin\controller;
  3. use app\common\controller\Backend;
  4. use think\Db;
  5. /**
  6. *
  7. *
  8. * @icon fa fa-circle-o
  9. */
  10. class Records extends Backend
  11. {
  12. /**
  13. * Records模型对象
  14. * @var \app\admin\model\Records
  15. */
  16. protected $model = null;
  17. public function _initialize()
  18. {
  19. parent::_initialize();
  20. $this->model = new \app\admin\model\Records;
  21. }
  22. /**
  23. * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
  24. * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
  25. * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
  26. */
  27. /**
  28. * 处理Excel文件上传并获取数据,然后保存到数据库
  29. */
  30. public function getxlsxdata(){
  31. // 处理POST请求
  32. if (!$this->request->isPost()) {
  33. return json(['code' => 0, 'msg' => '请求方式错误,需要POST请求']);
  34. }
  35. // 获取上传的文件
  36. $file = $this->request->file('file');
  37. if (!$file) {
  38. return json(['code' => 0, 'msg' => '请选择要上传的文件']);
  39. }
  40. try {
  41. // 验证文件类型
  42. $info = $file->validate(['ext' => 'xlsx,xls'])->move(ROOT_PATH . 'public/uploads/excel/');
  43. if (!$info) {
  44. return json(['code' => 0, 'msg' => '文件验证失败: ' . $file->getError()]);
  45. }
  46. // 获取文件路径
  47. $fullPath = ROOT_PATH . 'public/uploads/excel/' . $info->getSaveName();
  48. // 读取Excel文件内容
  49. $excelData = [];
  50. // 根据文件扩展名选择相应的读取方法
  51. $ext = strtolower(pathinfo($fullPath, PATHINFO_EXTENSION));
  52. try {
  53. // 检查是否安装了PhpSpreadsheet
  54. if (class_exists('\PhpOffice\PhpSpreadsheet\IOFactory')) {
  55. $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader(ucfirst($ext));
  56. $spreadsheet = $reader->load($fullPath);
  57. $sheet = $spreadsheet->getActiveSheet();
  58. $excelData = $sheet->toArray();
  59. }
  60. // 检查是否安装了PHPExcel
  61. else if (class_exists('\PHPExcel_IOFactory')) {
  62. $reader = \PHPExcel_IOFactory::createReader(ucfirst($ext));
  63. $excel = $reader->load($fullPath);
  64. $sheet = $excel->getActiveSheet();
  65. $excelData = $sheet->toArray();
  66. }
  67. // 如果都没有,返回文件已上传但无法解析的信息
  68. else {
  69. return json([
  70. 'code' => 0,
  71. 'msg' => '文件上传成功,但服务器缺少Excel解析库',
  72. 'data' => ['file_name' => $info->getFilename()]
  73. ]);
  74. }
  75. } catch (\Exception $e) {
  76. // 清理上传的文件
  77. @unlink($fullPath);
  78. return json(['code' => 0, 'msg' => '解析Excel文件时出错:' . $e->getMessage()]);
  79. }
  80. // 清理上传的临时文件
  81. @unlink($fullPath);
  82. // 检查数据是否为空
  83. if (count($excelData) < 2) {
  84. return json(['code' => 0, 'msg' => 'Excel文件数据为空或只有表头', 'data' => ['file_name' => $info->getFilename()]]);
  85. }
  86. // 获取表头(第一行)
  87. $headers = $excelData[0];
  88. // 定义表头与数据库字段的映射关系
  89. $fieldMapping = [
  90. '电子耳标号' => 'rfid',
  91. '国标号' => 'number',
  92. '出生日期' => 'birthDate',
  93. '品类' => 'category',
  94. '性别' => 'gender',
  95. '日龄' => 'dayage'
  96. ];
  97. // 准备要插入数据库的数据
  98. $insertData = [];
  99. $successCount = 0;
  100. $errorCount = 0;
  101. $errorMessages = [];
  102. // 处理数据行(从第二行开始)
  103. for ($i = 1; $i < count($excelData); $i++) {
  104. $rowData = $excelData[$i];
  105. $record = [];
  106. $hasData = false;
  107. // 构建关联数组
  108. for ($j = 0; $j < count($headers); $j++) {
  109. $header = $headers[$j];
  110. $value = isset($rowData[$j]) ? trim($rowData[$j]) : '';
  111. // 如果表头在映射中存在,添加到记录中
  112. if (isset($fieldMapping[$header])) {
  113. $field = $fieldMapping[$header];
  114. // 特殊字段处理
  115. if ($field === 'birthDate' && !empty($value)) {
  116. // 尝试转换日期格式,只保留日期部分
  117. $timestamp = strtotime($value);
  118. if ($timestamp) {
  119. $record[$field] = date('Y-m-d', $timestamp);
  120. } else {
  121. $record[$field] = $value; // 保持原样
  122. }
  123. } else {
  124. $record[$field] = $value;
  125. }
  126. if (!empty($value)) {
  127. $hasData = true;
  128. }
  129. }
  130. }
  131. // 如果记录有数据,添加创建时间和更新时间
  132. if ($hasData) {
  133. // $record['createtime'] = time();
  134. // $record['updatetime'] = time();
  135. $insertData[] = $record;
  136. $successCount++;
  137. } else {
  138. $errorCount++;
  139. $errorMessages[] = "第" . ($i + 1) . "行数据为空";
  140. }
  141. }
  142. // 准备导入进度数据
  143. $totalRows = count($excelData) - 1; // 减去表头
  144. $totalInsertData = count($insertData);
  145. $insertedCount = 0;
  146. $batchSize = 50; // 每批插入的数据量
  147. $duplicateCount = 0; // 重复数据计数
  148. // 打印要插入的数据(仅开发环境使用)
  149. if (!empty($insertData)) {
  150. // 打印数据
  151. // echo "<pre>";
  152. // echo "要插入的数据总量: " . count($insertData) . " 条\n";
  153. // print_r(array_slice($insertData, 0, 5));
  154. // echo "</pre>";
  155. // 提取所有rfid值用于检查重复
  156. $rfidValues = array_column($insertData, 'rfid');
  157. // 查询数据库中已存在的rfid值
  158. $existingRfids = Db::name('new_records')
  159. ->where('rfid', 'in', $rfidValues)
  160. ->column('rfid');
  161. // 过滤掉重复数据,只保留不存在的记录
  162. $uniqueData = [];
  163. foreach ($insertData as $record) {
  164. if (!in_array($record['rfid'], $existingRfids)) {
  165. $uniqueData[] = $record;
  166. } else {
  167. $duplicateCount++;
  168. $errorMessages[] = "重复数据,rfid: " . $record['rfid'] . " 已存在,跳过插入";
  169. }
  170. }
  171. // 如果有唯一数据需要插入
  172. if (!empty($uniqueData)) {
  173. // 将数据分成多批
  174. $batches = array_chunk($uniqueData, $batchSize);
  175. foreach ($batches as $batch) {
  176. try {
  177. $result = Db::name('new_records')->insertAll($batch);
  178. $insertedCount += $result;
  179. } catch (\Exception $e) {
  180. // 记录错误
  181. $errorCount++;
  182. $errorMessages[] = "插入数据时出错:" . $e->getMessage();
  183. }
  184. }
  185. }
  186. }
  187. // 返回成功信息
  188. return json([
  189. 'code' => 1,
  190. 'msg' => '文件上传并保存成功!',
  191. 'data' => [
  192. 'file_name' => $info->getFilename(),
  193. 'total_rows' => $totalRows,
  194. 'total_data_rows' => $totalInsertData,
  195. 'inserted_count' => $insertedCount,
  196. 'success_count' => $insertedCount,
  197. 'duplicate_count' => $duplicateCount,
  198. 'error_count' => $errorCount,
  199. 'error_messages' => $errorMessages
  200. ]
  201. ]);
  202. } catch (\Exception $e) {
  203. return json(['code' => 0, 'msg' => '处理文件时出错:' . $e->getMessage()]);
  204. }
  205. }
  206. }