Files
jsasg 3545b82015
All checks were successful
Gitea Actions Official-website / deploy-dev (push) Successful in 5s
feat: 新增产品兼容数据、产品兼容数据配件类型相关接口
2026-07-08 16:59:47 +08:00

313 lines
9.8 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare (strict_types = 1);
namespace app\admin\controller\v1;
use app\admin\model\v1\ProductCompatModel;
use app\admin\model\v1\ProductCompatPartsModel;
use app\admin\validate\v1\ProductCompatValidate;
class ProductCompat
{
/**
* 产品兼容性数据列表
*/
public function index()
{
$params = request()->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("操作成功");
}
}