Files
esun3d/app/code/Esun/Reward/Model/Import/Reward.php
T
2026-09-15 10:35:56 +08:00

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)))
);
}
}