Files
stemcell/app/code/Magezon/QuickOrder/Controller/Index/BatchImport.php
T
2026-09-15 10:26:34 +08:00

249 lines
9.4 KiB
PHP

<?php
namespace Magezon\QuickOrder\Controller\Index;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Checkout\Model\Cart as CustomerCart;
use Magento\Framework\App\Action\Context;
use Magento\Framework\App\Action\HttpPostActionInterface;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Filesystem;
use Magento\Framework\Filesystem\Directory\WriteInterface;
use Magento\MediaStorage\Model\File\Uploader;
use Magento\MediaStorage\Model\File\UploaderFactory;
use Magezon\QuickOrder\Model\ProductManagement;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use Psr\Log\LoggerInterface;
use Stemcell\Customer\Model\LabRepository;
class BatchImport extends \Magento\Framework\App\Action\Action implements HttpPostActionInterface
{
private const UPLOAD_DIRECTORY = 'import/quickorder';
private const MAX_UPLOAD_SIZE = 10485760;
private const ALLOWED_MIME_TYPES = [
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/zip',
'application/x-zip-compressed',
'application/x-ole-storage',
'application/octet-stream',
];
private WriteInterface $mediaDirectory;
public function __construct(
Context $context,
protected ProductRepositoryInterface $productRepository,
protected LabRepository $labRepository,
protected ProductManagement $productManagement,
protected LoggerInterface $logger,
protected CustomerCart $cart,
protected UploaderFactory $uploaderFactory,
Filesystem $filesystem
) {
parent::__construct($context);
$this->mediaDirectory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA);
}
public function execute()
{
try {
$uploader = $this->getValidatedUploader();
$uploadData = $uploader->validateFile();
$spreadSheet = IOFactory::load($uploadData['tmp_name']);
$data = $spreadSheet->getActiveSheet()->toArray('');
$quote = $this->cart->getQuote();
if (!$quote->getId()) {
// A persisted quote ID is required for the archive file name.
$this->cart->save();
$quote = $this->cart->getQuote();
}
if (!$quote->getId()) {
throw new LocalizedException(__('Unable to create a cart for the uploaded file.'));
}
$this->saveUploadedFile(
$spreadSheet,
(int) $quote->getId(),
strtolower($uploader->getFileExtension())
);
} catch (\Throwable $e) {
$this->logger->error('货号直购保存上传的 Excel 失败:' . $e->getMessage(), ['exception' => $e]);
$message = $e instanceof LocalizedException
? $e->getMessage()
: __('The Excel file could not be uploaded. Please check the file and try again.');
$this->messageManager->addErrorMessage($message);
$this->getResponse()->representJson(json_encode(['result' => false]));
return;
}
array_shift($data);
$salesperson = [];
$firstLab = $canSave = false;
$orderRemark = '';
$errors = [];
foreach ($data as $index => $row) {
if (!$row['0']) {
continue;
}
try {
$product = $this->productRepository->get($row['0'], false, null, true);
$productData = $this->productManagement->getProductData($product);
$params = [];
if ($row[8] && $orderRemark == '') {
$orderRemark = $row[8];
}
if (!empty($productData['children'])) {
parse_str($productData['options'], $params);
}
$qty = isset($row['1']) ? intval($row['1']) : 1;
$other = ['qty' => $qty, 'cd' => $row[4], 'quote' => $row[5], 'customer_discount_percent' => $row[6], 'note' => $row[7]];
$params = array_merge($params, $other);
$product = $this->productRepository->getById($productData['id'], false, null, true);
if (isset($row['3'])) {
try {
$lab = $this->labRepository->getByLabCode($row['3']);
if ($lab->getLabId()) {
$params['lab_id'] = $lab->getLabId();
if ($lab->getSalespersonId()) {
$firstLab = $lab;
$salesperson[] = $lab->getSalespersonId();
}
}
} catch (LocalizedException $e) {
$this->logger->warning(
sprintf('货号直购设置lab时异常,lab_code: %s, 原因:%s', $row['3'], $e->getMessage()),
$e->getTrace()
);
}
}
$params['item_sort'] = $index+1;
$this->cart->addProduct($product, $params);
$canSave = true;
} catch (\Exception $e) {
$this->logger->critical(
sprintf('货号直购通过上传excel加入购物车失败,原因是:%s请求数据:', $e->getMessage()), $row
);
$fields = ['产品号'=>0, 'Lab Id'=>3, 'CD No.' => 4];
$string = '';
foreach ($fields as $label => $index) {
$string .= $label . ':' . $row[$index] . ', ';
}
$errors[] = $string .' 加入购物车失败, 原因:'.$e->getMessage();
}
}
if (count(array_unique($salesperson)) > 1) {
$errors[] = '请注意,您使用错误的销售信息,请检查您的数据';
}
if ($firstLab && count(array_unique($salesperson)) == 1) {
$this->cart->getQuote()->setData('salesperson_id', $firstLab->getSalespersonId());
}
if ($orderRemark) {
$this->cart->getQuote()->setData('customer_note', $orderRemark);
}
foreach ($errors as $error) {
$this->messageManager->addError($error);
}
if ($canSave) {
$this->cart->save();
$this->cart->getQuote()->setTotalsCollectedFlag(false)->collectTotals();
}
$this->getResponse()->representJson(
json_encode(['result' => $canSave])
);
}
/**
* Validate the upload before PhpSpreadsheet opens it.
*/
private function getValidatedUploader(): Uploader
{
$uploader = $this->uploaderFactory->create(['fileId' => 'file']);
$uploader->setAllowedExtensions(['xls', 'xlsx']);
$uploader->setAllowCreateFolders(true);
$uploader->setAllowRenameFiles(false);
$uploader->setFilesDispersion(false);
$uploader->validateFile();
if ($uploader->getFileSize() > self::MAX_UPLOAD_SIZE) {
throw new LocalizedException(__('The Excel file must not exceed 10 MB.'));
}
if (!$uploader->checkMimeType(self::ALLOWED_MIME_TYPES)) {
throw new LocalizedException(__('The uploaded file is not a valid Excel file.'));
}
return $uploader;
}
/**
* Save as {quote_id}.{extension}, then {quote_id}-{n}.{extension}.
*/
private function saveUploadedFile(Spreadsheet $spreadSheet, int $quoteId, string $extension): string
{
$this->mediaDirectory->create(self::UPLOAD_DIRECTORY);
$lock = $this->mediaDirectory->openFile(self::UPLOAD_DIRECTORY . '/.upload.lock', 'a+');
if (!$lock->lock()) {
$lock->close();
throw new LocalizedException(__('The uploaded Excel file could not be locked for saving.'));
}
try {
$sequence = 0;
do {
$stem = (string) $quoteId . ($sequence ? '-' . $sequence : '');
$fileName = $stem . '.' . $extension;
$sequence++;
} while ($this->archiveNameExists($stem));
$spreadSheet->getProperties()->setCreated(time());
$writer = IOFactory::createWriter($spreadSheet, $extension === 'xlsx' ? 'Xlsx' : 'Xls');
$relativeFilePath = self::UPLOAD_DIRECTORY . '/' . $fileName;
try {
$writer->save($this->mediaDirectory->getAbsolutePath($relativeFilePath));
} catch (\Throwable $e) {
if ($this->mediaDirectory->isExist($relativeFilePath)) {
$this->mediaDirectory->delete($relativeFilePath);
}
throw $e;
}
$this->mediaDirectory->changePermissions(
$relativeFilePath,
0640
);
} finally {
$lock->unlock();
$lock->close();
}
return $fileName;
}
/**
* Treat .xls and .xlsx files with the same stem as the same upload sequence.
*/
private function archiveNameExists(string $stem): bool
{
foreach (['xls', 'xlsx'] as $extension) {
if ($this->mediaDirectory->isExist(self::UPLOAD_DIRECTORY . '/' . $stem . '.' . $extension)) {
return true;
}
}
return false;
}
}