> */ private const HEADERS = [ 'taobao' => [ '类型', 'ERP店铺名称', '店铺名称', '店铺状态', '店铺主体', '原店铺主体', '状态', '登录账号', '登录密码', '支付密码', '提现路径', '提现账户名称', '支付宝账号', ], 'jd' => [ '类型', 'ERP店铺名称', '店铺名称', '店铺id', '店铺状态', '店铺主体', '原店铺主体', '登录账号', '登录密码', '支付验证方式', '金融账号', '金融密码', '支付验证方式', '状态', '提现路径', '提现账户名称', '银行卡账号(后四位)', ], 'douyin' => [ 'ERP店铺名称', '店铺名称', '店铺状态', '店铺主体', '原店铺主体', '状态', '登录账号', '登录密码', '支付密码', '提现路径', '提现账户名称', '银行卡账号(后四位)', ], 'pinduoduo' => [ 'ERP店铺名称', '店铺名称', '店铺状态', '店铺主体', '原店铺主体', '状态', '登录账号', '登录密码', '支付密码', '提现路径', '提现账户名称', '银行卡账号(后四位)', ], ]; public function import(UploadedFile $file, string $tab, ?int $companyId = null): array { if (! isset(self::HEADERS[$tab])) { throw ValidationException::withMessages(['import' => '未知的店铺收款账户页签。']); } $spreadsheet = IOFactory::load($file->getRealPath()); $worksheet = $this->worksheet($spreadsheet, $tab); if ($worksheet === null) { throw ValidationException::withMessages([ 'import' => '未找到“' . $this->sheetTitle($tab) . '”工作表,请使用当前页签的模板。', ]); } $expected = self::HEADERS[$tab]; $actual = array_map(static fn ($value): string => trim((string) $value), $worksheet->toArray(null, true, false, false)[0] ?? []); if ($actual !== $expected) { throw ValidationException::withMessages([ 'import' => '“' . $this->sheetTitle($tab) . '”表头不匹配,请先下载当前页签模板。', ]); } $rows = $worksheet->toArray(null, true, false, false); $records = []; $skipped = 0; foreach (array_slice($rows, 1) as $index => $row) { $row = array_pad($row, count($expected), null); $erpName = trim((string) ($row[array_search('ERP店铺名称', $expected, true)] ?? '')); if ($erpName === '') { $skipped++; continue; } $records[] = $this->mapRow($tab, $expected, $row); } $created = 0; $updated = 0; $companyId ??= (int) (company_id() ?: 1); DB::transaction(function () use ($records, $companyId, &$created, &$updated): void { foreach ($records as $data) { $platform = (string) $data['store_platform']; $query = StoreReceivingAccount::withoutGlobalScopes() ->withTrashed() ->where('company_id', $companyId) ->where('store_platform', $platform); if (($data['wallet_number'] ?? '') !== '') { $query->where('wallet_number', $data['wallet_number']); } else { $query->where('store_name', $data['store_name']); } $record = $query->first(); if ($record === null) { $record = new StoreReceivingAccount(); $record->fill(['company_id' => $companyId] + $data); $record->save(); $created++; continue; } if ($record->trashed()) { $record->restore(); } $record->fill($data); $record->save(); $updated++; } }); return [ 'created' => $created, 'updated' => $updated, 'skipped' => $skipped, 'rows' => count($records), ]; } /** @return array> */ public static function headers(): array { return self::HEADERS; } public static function sheetTitle(string $tab): string { return match ($tab) { 'taobao' => '淘宝', 'jd' => '京东', 'douyin' => '抖音小店', 'pinduoduo' => '拼多多', default => $tab, }; } private function worksheet($spreadsheet, string $tab): ?object { $title = $this->sheetTitle($tab); foreach ($spreadsheet->getWorksheetIterator() as $worksheet) { if (trim((string) $worksheet->getTitle()) === $title) { return $worksheet; } } return null; } /** @param list $headers */ private function mapRow(string $tab, array $headers, array $row): array { $value = static function (string $header) use ($headers, $row): string { $index = array_search($header, $headers, true); return $index === false ? '' : trim((string) ($row[$index] ?? '')); }; $platform = $tab === 'taobao' || $tab === 'jd' ? $value('类型') : ($tab === 'douyin' ? '抖音' : '拼多多'); $allowedPlatforms = TableRegistry::tabValues('fund-management', 'store-receiving-accounts', $tab); if (! in_array($platform, $allowedPlatforms, true)) { throw ValidationException::withMessages([ 'import' => '第 ' . (count($row) > 0 ? '数据' : '') . '行的平台类型不属于当前页签,请检查“类型”列。', ]); } $storeId = $value('店铺id'); $data = [ 'store_platform' => $platform, 'store_name' => $value('ERP店铺名称'), 'wallet_number' => $storeId, 'store_entity_name' => $value('店铺主体'), 'login_account' => $value('登录账号'), 'login_password' => $value('登录密码'), 'receiving_method' => $value('提现路径') ?: '银行卡', 'receiving_account_name' => $value('提现账户名称'), 'alipay_or_bank_account' => $value('支付宝账号') ?: $value('银行卡账号(后四位)'), 'opening_bank' => null, 'erp_store_name' => $value('ERP店铺名称'), 'shop_name' => $value('店铺名称'), 'store_status' => $value('店铺状态'), 'original_store_entity' => $value('原店铺主体'), 'account_status' => $value('状态'), 'payment_password' => $value('支付密码'), 'payment_verification_method' => $value('支付验证方式'), 'financial_account' => $value('金融账号'), 'financial_password' => $value('金融密码'), 'payment_verification_method_secondary' => $this->valueAt($headers, $row, '支付验证方式', 1), 'bank_card_last_four' => $value('银行卡账号(后四位)'), 'store_id' => $storeId, 'source_platform' => $platform, ]; return array_filter($data, static fn ($item): bool => $item !== null); } /** @param list $headers */ private function valueAt(array $headers, array $row, string $header, int $occurrence): string { $found = 0; foreach ($headers as $index => $value) { if ($value !== $header) { continue; } if ($found++ === $occurrence) { return trim((string) ($row[$index] ?? '')); } } return ''; } }