123 lines
5.9 KiB
PHP
123 lines
5.9 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Esun\Reward\Model\Import;
|
|
|
|
use Esun\Reward\Model\BalanceManager;
|
|
use Magento\Framework\App\Config\ScopeConfigInterface;
|
|
use Magento\Framework\App\ResourceConnection;
|
|
use Magento\Framework\Serialize\Serializer\Json;
|
|
use Magento\Framework\Stdlib\StringUtils;
|
|
use Magento\ImportExport\Model\Import as ImportModel;
|
|
use Magento\ImportExport\Model\Import\AbstractEntity;
|
|
use Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface;
|
|
use Magento\ImportExport\Model\ImportFactory;
|
|
use Magento\ImportExport\Model\ResourceModel\Helper;
|
|
|
|
class Reward extends AbstractEntity
|
|
{
|
|
public const ENTITY_CODE = 'esun_reward';
|
|
public const COL_EMAIL = 'email';
|
|
public const COL_TIER = 'member_tier';
|
|
public const COL_POINTS = 'points';
|
|
private const ERROR_EMAIL = 'rewardInvalidEmail';
|
|
private const ERROR_TIER = 'rewardInvalidTier';
|
|
private const ERROR_POINTS = 'rewardInvalidPoints';
|
|
private const ERROR_NOT_FOUND = 'rewardCustomerNotFound';
|
|
private const ERROR_MULTIPLE = 'rewardCustomerMultipleWebsites';
|
|
private const ERROR_DUPLICATE = 'rewardDuplicateEmail';
|
|
private const TIER_MAP = [
|
|
'VIP I' => 'bronze', 'BRONZE MAKER' => 'bronze',
|
|
'VIP II' => 'silver', 'SILVER MAKER' => 'silver',
|
|
'VIP III' => 'gold', 'GOLD MAKER' => 'gold',
|
|
];
|
|
|
|
protected $needColumnCheck = true;
|
|
protected $validColumnNames = [self::COL_EMAIL, self::COL_TIER, self::COL_POINTS];
|
|
protected $_availableBehaviors = [ImportModel::BEHAVIOR_APPEND];
|
|
protected $masterAttributeCode = self::COL_EMAIL;
|
|
private array $seenEmails = [];
|
|
|
|
public function __construct(
|
|
StringUtils $string,
|
|
ScopeConfigInterface $scopeConfig,
|
|
ImportFactory $importFactory,
|
|
Helper $resourceHelper,
|
|
ResourceConnection $resource,
|
|
ProcessingErrorAggregatorInterface $errorAggregator,
|
|
private readonly ResourceConnection $rewardResource,
|
|
private readonly BalanceManager $balances,
|
|
array $data = [],
|
|
?Json $serializer = null
|
|
) {
|
|
parent::__construct($string, $scopeConfig, $importFactory, $resourceHelper, $resource, $errorAggregator, $data, $serializer);
|
|
$this->addMessageTemplate(self::ERROR_EMAIL, 'Email format is invalid');
|
|
$this->addMessageTemplate(self::ERROR_TIER, 'Member tier is invalid; use VIP I/II/III or Bronze/Silver/Gold Maker');
|
|
$this->addMessageTemplate(self::ERROR_POINTS, 'Points must be a non-negative integer');
|
|
$this->addMessageTemplate(self::ERROR_NOT_FOUND, '未找到该邮箱对应的客户');
|
|
$this->addMessageTemplate(self::ERROR_MULTIPLE, '该邮箱存在于多个 Website,请人工确认归属');
|
|
$this->addMessageTemplate(self::ERROR_DUPLICATE, 'CSV 中邮箱重复');
|
|
}
|
|
|
|
public function getEntityTypeCode(): string { return self::ENTITY_CODE; }
|
|
|
|
public function validateRow(array $rowData, $rowNumber): bool
|
|
{
|
|
if (isset($this->_validatedRows[$rowNumber])) {
|
|
return !$this->getErrorAggregator()->isRowInvalid($rowNumber);
|
|
}
|
|
$this->_validatedRows[$rowNumber] = true;
|
|
$email = strtolower(trim((string)($rowData[self::COL_EMAIL] ?? '')));
|
|
$tier = $this->normalizeTier((string)($rowData[self::COL_TIER] ?? ''));
|
|
$points = trim((string)($rowData[self::COL_POINTS] ?? ''));
|
|
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { $this->addRowError(self::ERROR_EMAIL, $rowNumber, self::COL_EMAIL); }
|
|
if ($tier === null) { $this->addRowError(self::ERROR_TIER, $rowNumber, self::COL_TIER); }
|
|
if (!preg_match('/^\d+$/', $points)) { $this->addRowError(self::ERROR_POINTS, $rowNumber, self::COL_POINTS); }
|
|
if ($email !== '') {
|
|
if (isset($this->seenEmails[$email])) { $this->addRowError(self::ERROR_DUPLICATE, $rowNumber, self::COL_EMAIL); }
|
|
$this->seenEmails[$email] = true;
|
|
$count = count($this->findCustomers($email));
|
|
if ($count === 0) { $this->addRowError(self::ERROR_NOT_FOUND, $rowNumber, self::COL_EMAIL); }
|
|
if ($count > 1) { $this->addRowError(self::ERROR_MULTIPLE, $rowNumber, self::COL_EMAIL); }
|
|
}
|
|
return !$this->getErrorAggregator()->isRowInvalid($rowNumber);
|
|
}
|
|
|
|
protected function _importData()
|
|
{
|
|
$batchId = 'admin-import-' . gmdate('YmdHis') . '-' . bin2hex(random_bytes(4));
|
|
while ($bunch = $this->_dataSourceModel->getNextUniqueBunch($this->getIds())) {
|
|
foreach ($bunch as $rowNumber => $rowData) {
|
|
if (!$this->validateRow($rowData, $rowNumber)) { continue; }
|
|
$email = strtolower(trim((string)$rowData[self::COL_EMAIL]));
|
|
$customers = $this->findCustomers($email);
|
|
$customer = reset($customers);
|
|
$this->balances->importLegacySnapshot(
|
|
(int)$customer['entity_id'], (int)$customer['website_id'], (int)$rowData[self::COL_POINTS],
|
|
(string)$this->normalizeTier((string)$rowData[self::COL_TIER]), $batchId,
|
|
['source' => 'admin_import', 'source_row' => $rowNumber, 'source_email' => $email]
|
|
);
|
|
$this->countItemsUpdated++;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private function normalizeTier(string $value): ?string
|
|
{
|
|
$label = strtoupper((string)preg_replace('/\s+/', ' ', trim($value)));
|
|
return self::TIER_MAP[$label] ?? null;
|
|
}
|
|
|
|
/** @return array<int,array{entity_id:string,website_id:string}> */
|
|
private function findCustomers(string $email): array
|
|
{
|
|
$table = $this->rewardResource->getTableName('customer_entity');
|
|
return $this->rewardResource->getConnection()->fetchAll(
|
|
$this->rewardResource->getConnection()->select()
|
|
->from($table, ['entity_id', 'website_id'])
|
|
->where('LOWER(email) = ?', strtolower(trim($email)))
|
|
);
|
|
}
|
|
}
|