- 新增 ec_account_ledger_remarks 映射表与 account-ledger-remarks:import 导入命令(平台/账单项目/总账备注) - 新增 AccountLedgerRemarkMap:按店铺名前缀识别平台,无前缀时用页签默认平台;缺失匹配保持空白 - 全部账户页签(含淘系支付宝自有列定义)在「店铺/店铺名称」右侧新增「总账备注」列,页面/列设置/导出共用同一份列定义 - 控制器在分页与导出流读取时补齐 payload.ledger_remark,不改动上游账单与共享镜像数据 - 用例:新增 AccountLedgerRemarkMapTest;修正唯品会 JX/MP 列清单断言
167 lines
5.1 KiB
PHP
167 lines
5.1 KiB
PHP
<?php
|
||
|
||
namespace Modules\EcommerceFinance\Services;
|
||
|
||
use Illuminate\Support\Facades\Cache;
|
||
use Illuminate\Support\Facades\Schema;
|
||
use Throwable;
|
||
|
||
/**
|
||
* 「总账备注」取值:按(平台 + 账单项目)匹配 ec_account_ledger_remarks。
|
||
*
|
||
* 店铺收款账户账单各页签的账单项目名称不一致,平台口径以店铺名前缀为准
|
||
* (如「抖音-xxx」「天猫-xxx」),页签默认平台只作为兜底,避免淘系页签里
|
||
* 天猫/淘宝/阿里巴巴三类项目混用同一份映射。
|
||
*/
|
||
class AccountLedgerRemarkMap
|
||
{
|
||
public const CACHE_KEY = 'ec_account_ledger_remarks_map_v1';
|
||
|
||
/** 页签默认平台(店铺名没有平台前缀时使用)。 */
|
||
private const TAB_PLATFORM = [
|
||
'taobao-alipay' => '淘宝',
|
||
'taobao-wechat' => '淘宝',
|
||
'pinduoduo' => '拼多多',
|
||
'jd-transactions' => '京东',
|
||
'jingxi' => '京喜',
|
||
'jd-wallet' => '京东',
|
||
'douyin' => '抖音',
|
||
'dewu' => '得物',
|
||
'vipshop' => '唯品会MP',
|
||
'vipshop-jx' => '唯品会jx',
|
||
];
|
||
|
||
/** 店铺名前缀可识别的平台(顺序敏感:先长后短)。 */
|
||
private const STORE_PREFIX_PLATFORMS = [
|
||
'唯品会JX' => '唯品会jx',
|
||
'唯品会MP' => '唯品会MP',
|
||
'阿里巴巴' => '阿里巴巴',
|
||
'拼多多' => '拼多多',
|
||
'天猫' => '天猫',
|
||
'淘宝' => '淘宝',
|
||
'抖音' => '抖音',
|
||
'得物' => '得物',
|
||
'京喜' => '京喜',
|
||
'京东' => '京东',
|
||
];
|
||
|
||
/**
|
||
* @return array<string,array<string,string>> [platform => [bill_item => ledger_remark]]
|
||
*/
|
||
public function map(): array
|
||
{
|
||
return Cache::rememberForever(self::CACHE_KEY, function (): array {
|
||
try {
|
||
if (! Schema::hasTable('ec_account_ledger_remarks')) {
|
||
return [];
|
||
}
|
||
|
||
$map = [];
|
||
foreach (\Illuminate\Support\Facades\DB::table('ec_account_ledger_remarks')
|
||
->select(['platform', 'bill_item', 'ledger_remark'])
|
||
->get() as $row) {
|
||
$platform = trim((string) $row->platform);
|
||
$item = trim((string) $row->bill_item);
|
||
if ($platform === '' || $item === '') {
|
||
continue;
|
||
}
|
||
$map[$platform][$item] = (string) $row->ledger_remark;
|
||
}
|
||
|
||
return $map;
|
||
} catch (Throwable) {
|
||
return [];
|
||
}
|
||
});
|
||
}
|
||
|
||
public function forget(): void
|
||
{
|
||
Cache::forget(self::CACHE_KEY);
|
||
}
|
||
|
||
/** 页签默认平台。 */
|
||
public function defaultPlatform(string $tab): ?string
|
||
{
|
||
return self::TAB_PLATFORM[$tab] ?? null;
|
||
}
|
||
|
||
/** 解析某一行账单所属平台:优先店铺名前缀,其次页签默认。 */
|
||
public function platform(?string $storeName, ?string $tabPlatform): ?string
|
||
{
|
||
$name = trim((string) $storeName);
|
||
if ($name !== '') {
|
||
foreach (self::STORE_PREFIX_PLATFORMS as $prefix => $platform) {
|
||
if (str_starts_with($name, $prefix)) {
|
||
return $platform;
|
||
}
|
||
}
|
||
}
|
||
|
||
return $tabPlatform;
|
||
}
|
||
|
||
/** 查总账备注;没有匹配返回 null。 */
|
||
public function remark(?string $storeName, ?string $tabPlatform, ?string $billItem): ?string
|
||
{
|
||
$item = trim((string) $billItem);
|
||
if ($item === '') {
|
||
return null;
|
||
}
|
||
|
||
$map = $this->map();
|
||
if ($map === []) {
|
||
return null;
|
||
}
|
||
|
||
foreach ([$this->platform($storeName, $tabPlatform), $tabPlatform] as $platform) {
|
||
if ($platform === null || $platform === '') {
|
||
continue;
|
||
}
|
||
if (isset($map[$platform][$item])) {
|
||
return $map[$platform][$item];
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 给一行账单补上 payload.ledger_remark(内部字段名 ledger_remark)。
|
||
*
|
||
* @param array<string,mixed> $row
|
||
* @return array<string,mixed>
|
||
*/
|
||
public function decorate(array $row, string $tab): array
|
||
{
|
||
$tabPlatform = $this->defaultPlatform($tab);
|
||
$remark = $this->remark(
|
||
$row['store_name'] ?? null,
|
||
$tabPlatform,
|
||
$row['bill_item'] ?? null,
|
||
);
|
||
if ($remark === null) {
|
||
return $row;
|
||
}
|
||
|
||
$payload = is_array($row['payload'] ?? null) ? $row['payload'] : [];
|
||
$payload['ledger_remark'] = $remark;
|
||
$row['payload'] = $payload;
|
||
// 淘系支付宝页签按顶层字段取值,这里同时补一份,页面与导出共用。
|
||
$row['ledger_remark'] = $remark;
|
||
|
||
return $row;
|
||
}
|
||
|
||
/**
|
||
* @param iterable<int,array<string,mixed>> $rows
|
||
* @return \Generator<int,array<string,mixed>>
|
||
*/
|
||
public function decorateAll(iterable $rows, string $tab): \Generator
|
||
{
|
||
foreach ($rows as $row) {
|
||
yield is_array($row) ? $this->decorate($row, $tab) : $row;
|
||
}
|
||
}
|
||
}
|