param([ 'label_name', 'model', 'page/d' => 1, 'size/d' => 10 ]); $list = ProductCompatModel::withoutField([ 'language_id', 'deleted_at' ]) ->with(['parts' => function ($query) { $query->field(['id', 'name']); }]) ->language(request()->lang_id) ->withSearch(['label_name', 'model'], [ 'label_name' => $params['label_name'] ?? null, 'model' => $params['model'] ?? null, ]) ->order(['updated_at' => 'desc']) ->paginate([ 'list_rows' => $params['size'], 'page' => $params['page'], ]) ?->bindAttr('parts', ['part_name' => 'name']) ?->hidden(['parts']); return success("获取成功", $list); } /** * 导入产品兼容性数据 */ public function import() { // 获取上传文件 $file = request()->file('file'); if (empty($file)) { return error('请上传文件'); } $lang_id = request()->lang_id; // 读取文件 $keys_map = [ 'A' => 'label_name', 'B' => 'part_name', 'C' => 'brand_name', 'D' => 'level_name', 'E' => 'class_name', 'F' => 'series_name', 'G' => 'model', 'H' => 'capacity', 'I' => 'interface_type', 'J' => 'compat_status', ]; $chunk = 500; // 每批次处理的条数 $items = 0; // 已处理的条数 $xlsx_data = []; $xlsx_reader = xlsx_stream_reader($file->getRealPath(), 2, $keys_map, true); \think\facade\Db::startTrans(); try { foreach ($xlsx_reader as $row) { $items++; $row['seq_no'] = $items; // 记录行序号,防止后续顺序打乱 $xlsx_data[] = $row; if ($items % $chunk == 0) { // 每500条,为一批次进行处理 $this->handleImport($xlsx_data, $lang_id); $xlsx_data = []; } } if (!empty($xlsx_data)) { // 处理剩余的 $this->handleImport($xlsx_data, $lang_id); } } catch (\Throwable $th) { \think\facade\Db::rollback(); return error($th->getMessage()); } \think\facade\Db::commit(); return success('操作成功'); } private function handleImport(array $xlsx_data, int $lang_id): void { $compat_datas = $this->matchExistCompatData($xlsx_data, $lang_id); // 校验数据并组装sql语句 $raw_sql = $this->validateAndBuildSql($compat_datas); if (false === \think\facade\Db::execute($raw_sql)) { throw new \Exception(sprintf('第【%s】行执行导入 SQL 失败', implode(',', array_column($xlsx_data, 'seq_no')))); } } private function matchExistCompatData(array $datas, int $lang_id) { $models = array_column($datas, 'model'); $compat_map = ProductCompatModel::language($lang_id) ->modelName($models) ->column('id', 'model'); $part_names = array_unique(array_column($datas, 'part_name')); $part_map = ProductCompatPartsModel::language($lang_id) ->partName($part_names) ->column('id', 'name'); // 匹配已存在数据的主键、配件id foreach ($datas as &$d) { $d['id'] = $compat_map[$d['model']] ?? null; $d['language_id'] = $lang_id; $d['part_id'] = $part_map[$d['part_name']] ?? 0; } unset($d); return $datas; } private function validateAndBuildSql(array $compat_datas) { $sql_values = []; $validate = new ProductCompatValidate; foreach ($compat_datas as $compat) { // 校验数据 $scene = is_null($compat['id']) ? 'add' : 'edit'; if (!$validate->scene($scene)->check($compat)) { throw new \Exception(sprintf('第【%s】行%s', $compat['seq_no'], $validate->getError())); } // 组装 sql values $sql_values[] = $this->buildSqlValue($compat); } return $this->buildRawSql($sql_values); } private function buildSqlValue(array $compat) { $pdo = \think\facade\Db::connect()->getPdo(); return sprintf( '(%d, %d, %d, %s, %s, %s, %s, %s, %s, %s, %s, %s)', $compat['id'], $compat['language_id'], $compat['part_id'], is_string($compat['label_name']) ? $pdo->quote($compat['label_name']) : $compat['label_name'], is_string($compat['brand_name']) ? $pdo->quote($compat['brand_name']) : $compat['brand_name'], is_string($compat['level_name']) ? $pdo->quote($compat['level_name']) : $compat['level_name'], is_string($compat['class_name']) ? $pdo->quote($compat['class_name']) : $compat['class_name'], is_string($compat['series_name']) ? $pdo->quote($compat['series_name']) : $compat['series_name'], is_string($compat['model']) ? $pdo->quote($compat['model']) : $compat['model'], is_string($compat['capacity']) ? $pdo->quote($compat['capacity']) : $compat['capacity'], is_string($compat['interface_type']) ? $pdo->quote($compat['interface_type']) : $compat['interface_type'], is_string($compat['compat_status']) ? $pdo->quote($compat['compat_status']) : $compat['compat_status'] ); } private function buildRawSql(array $values) { return sprintf( 'INSERT INTO %s ( `id`, `language_id`, `part_id`, `label_name`, `brand_name`, `level_name`, `class_name`, `series_name`, `model`, `capacity`, `interface_type`, `compat_status` ) VALUES %s ON DUPLICATE KEY UPDATE `part_id` = VALUES(`part_id`), `label_name` = VALUES(`label_name`), `brand_name` = VALUES(`brand_name`), `level_name` = VALUES(`level_name`), `class_name` = VALUES(`class_name`), `series_name` = VALUES(`series_name`), `model` = VALUES(`model`), `capacity` = VALUES(`capacity`), `interface_type` = VALUES(`interface_type`), `compat_status` = VALUES(`compat_status`) ', (new ProductCompatModel)->getTable(), implode(',', $values) ); } /** * 产品兼容性数据添加 */ public function save() { $post = request()->post([ 'part_id', 'label_name', 'brand_name', 'level_name', 'class_name', 'series_name', 'model', 'capacity', 'interface_type', 'compat_status', 'sort', 'disabled' ]); $data = array_merge($post, ['language_id' => request()->lang_id]); // 校验输入 $validate = new ProductCompatValidate; if (!$validate->scene('add')->check($data)) { return error($validate->getError()); } $compat = ProductCompatModel::create($data); if ($compat->isEmpty()) { return error('操作失败'); } return success('操作成功'); } /** * 产品兼容性数据详细 */ public function read() { $id = request()->param('id'); $data = ProductCompatModel::withoutField(['language_id', 'deleted_at']) ->bypk($id) ->find(); if (empty($data)) { return error('获取失败'); } return success("获取成功", $data); } /** * 产品兼容性数据更新 */ public function update() { $id = request()->param('id'); $put = request()->put([ 'part_id', 'label_name', 'brand_name', 'level_name', 'class_name', 'series_name', 'model', 'capacity', 'interface_type', 'compat_status', 'sort', 'disabled' ]); $data = array_merge($put, ['id' => $id, 'language_id' => request()->lang_id]); // 校验输入 $validate = new ProductCompatValidate; if (!$validate->scene('edit')->check($data)) { return error($validate->getError()); } $compat = ProductCompatModel::bypk($id)->find(); if (empty($compat)) { return error('请确认操作对象是否正确'); } if (!$compat->save($data)) { return error('操作失败'); } return success('操作成功'); } /** * 产品兼容性数据删除 */ public function delete() { $id = request()->param('id'); $data = ProductCompatModel::bypk($id)->find(); if (empty($data)) { return error("请确认操作对象是否正确"); } if (!$data->delete()) { return error("操作失败"); } return success("操作成功"); } }