| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- <?php
- namespace app\admin\controller;
- use app\common\controller\Backend;
- use think\Db;
- /**
- *
- *
- * @icon fa fa-circle-o
- */
- class Records extends Backend
- {
- /**
- * Records模型对象
- * @var \app\admin\model\Records
- */
- protected $model = null;
- public function _initialize()
- {
- parent::_initialize();
- $this->model = new \app\admin\model\Records;
- }
- /**
- * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
- * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
- * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
- */
- /**
- * 处理Excel文件上传并获取数据,然后保存到数据库
- */
- public function getxlsxdata(){
- // 处理POST请求
- if (!$this->request->isPost()) {
- return json(['code' => 0, 'msg' => '请求方式错误,需要POST请求']);
- }
-
- // 获取上传的文件
- $file = $this->request->file('file');
- if (!$file) {
- return json(['code' => 0, 'msg' => '请选择要上传的文件']);
- }
-
- try {
- // 验证文件类型
- $info = $file->validate(['ext' => 'xlsx,xls'])->move(ROOT_PATH . 'public/uploads/excel/');
- if (!$info) {
- return json(['code' => 0, 'msg' => '文件验证失败: ' . $file->getError()]);
- }
-
- // 获取文件路径
- $fullPath = ROOT_PATH . 'public/uploads/excel/' . $info->getSaveName();
- // 读取Excel文件内容
- $excelData = [];
- // 根据文件扩展名选择相应的读取方法
- $ext = strtolower(pathinfo($fullPath, PATHINFO_EXTENSION));
-
- try {
- // 检查是否安装了PhpSpreadsheet
- if (class_exists('\PhpOffice\PhpSpreadsheet\IOFactory')) {
- $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader(ucfirst($ext));
- $spreadsheet = $reader->load($fullPath);
- $sheet = $spreadsheet->getActiveSheet();
- $excelData = $sheet->toArray();
- }
- // 检查是否安装了PHPExcel
- else if (class_exists('\PHPExcel_IOFactory')) {
- $reader = \PHPExcel_IOFactory::createReader(ucfirst($ext));
- $excel = $reader->load($fullPath);
- $sheet = $excel->getActiveSheet();
- $excelData = $sheet->toArray();
- }
- // 如果都没有,返回文件已上传但无法解析的信息
- else {
- return json([
- 'code' => 0,
- 'msg' => '文件上传成功,但服务器缺少Excel解析库',
- 'data' => ['file_name' => $info->getFilename()]
- ]);
- }
- } catch (\Exception $e) {
- // 清理上传的文件
- @unlink($fullPath);
- return json(['code' => 0, 'msg' => '解析Excel文件时出错:' . $e->getMessage()]);
- }
-
- // 清理上传的临时文件
- @unlink($fullPath);
- // 检查数据是否为空
- if (count($excelData) < 2) {
- return json(['code' => 0, 'msg' => 'Excel文件数据为空或只有表头', 'data' => ['file_name' => $info->getFilename()]]);
- }
- // 获取表头(第一行)
- $headers = $excelData[0];
-
- // 定义表头与数据库字段的映射关系
- $fieldMapping = [
- '电子耳标号' => 'rfid',
- '国标号' => 'number',
- '出生日期' => 'birthDate',
- '品类' => 'category',
- '性别' => 'gender',
- '日龄' => 'dayage'
- ];
-
- // 准备要插入数据库的数据
- $insertData = [];
- $successCount = 0;
- $errorCount = 0;
- $errorMessages = [];
-
- // 处理数据行(从第二行开始)
- for ($i = 1; $i < count($excelData); $i++) {
- $rowData = $excelData[$i];
- $record = [];
- $hasData = false;
-
- // 构建关联数组
- for ($j = 0; $j < count($headers); $j++) {
- $header = $headers[$j];
- $value = isset($rowData[$j]) ? trim($rowData[$j]) : '';
- // 如果表头在映射中存在,添加到记录中
- if (isset($fieldMapping[$header])) {
- $field = $fieldMapping[$header];
- // 特殊字段处理
- if ($field === 'birthDate' && !empty($value)) {
- // 尝试转换日期格式,只保留日期部分
- $timestamp = strtotime($value);
- if ($timestamp) {
- $record[$field] = date('Y-m-d', $timestamp);
- } else {
- $record[$field] = $value; // 保持原样
- }
- } else {
- $record[$field] = $value;
- }
-
- if (!empty($value)) {
- $hasData = true;
- }
- }
- }
- // 如果记录有数据,添加创建时间和更新时间
- if ($hasData) {
- // $record['createtime'] = time();
- // $record['updatetime'] = time();
- $insertData[] = $record;
- $successCount++;
- } else {
- $errorCount++;
- $errorMessages[] = "第" . ($i + 1) . "行数据为空";
- }
- }
-
- // 准备导入进度数据
- $totalRows = count($excelData) - 1; // 减去表头
- $totalInsertData = count($insertData);
- $insertedCount = 0;
- $batchSize = 50; // 每批插入的数据量
- $duplicateCount = 0; // 重复数据计数
-
- // 打印要插入的数据(仅开发环境使用)
- if (!empty($insertData)) {
- // 打印数据
- // echo "<pre>";
- // echo "要插入的数据总量: " . count($insertData) . " 条\n";
- // print_r(array_slice($insertData, 0, 5));
- // echo "</pre>";
-
- // 提取所有rfid值用于检查重复
- $rfidValues = array_column($insertData, 'rfid');
-
- // 查询数据库中已存在的rfid值
- $existingRfids = Db::name('new_records')
- ->where('rfid', 'in', $rfidValues)
- ->column('rfid');
-
- // 过滤掉重复数据,只保留不存在的记录
- $uniqueData = [];
- foreach ($insertData as $record) {
- if (!in_array($record['rfid'], $existingRfids)) {
- $uniqueData[] = $record;
- } else {
- $duplicateCount++;
- $errorMessages[] = "重复数据,rfid: " . $record['rfid'] . " 已存在,跳过插入";
- }
- }
-
- // 如果有唯一数据需要插入
- if (!empty($uniqueData)) {
- // 将数据分成多批
- $batches = array_chunk($uniqueData, $batchSize);
- foreach ($batches as $batch) {
- try {
- $result = Db::name('new_records')->insertAll($batch);
- $insertedCount += $result;
- } catch (\Exception $e) {
- // 记录错误
- $errorCount++;
- $errorMessages[] = "插入数据时出错:" . $e->getMessage();
- }
- }
- }
- }
-
- // 返回成功信息
- return json([
- 'code' => 1,
- 'msg' => '文件上传并保存成功!',
- 'data' => [
- 'file_name' => $info->getFilename(),
- 'total_rows' => $totalRows,
- 'total_data_rows' => $totalInsertData,
- 'inserted_count' => $insertedCount,
- 'success_count' => $insertedCount,
- 'duplicate_count' => $duplicateCount,
- 'error_count' => $errorCount,
- 'error_messages' => $errorMessages
- ]
- ]);
-
- } catch (\Exception $e) {
- return json(['code' => 0, 'msg' => '处理文件时出错:' . $e->getMessage()]);
- }
- }
-
- }
|