CaiWuXiTong/modules/EcommerceFinance/Services/AccountLedgerRemarkMap.php
qiuxiang 985281c100 店铺收款账户账单新增「总账备注」列,按平台+账单项目匹配映射表
- 新增 ec_account_ledger_remarks 表与 account-ledger-remarks:import 导入命令(平台/账单项目/总账备注)
- 新增 AccountLedgerRemarkMap:按店铺名前缀识别平台(天猫/淘宝/阿里巴巴/抖音/得物/京东/京喜/拼多多/唯品会),无前缀用页签默认平台
- 全部账户页签在「店铺/店铺名称」右侧新增「总账备注」列(含淘系支付宝自有列定义),页面、列设置、导出共用同一份列定义
- 控制器在读取分页与导出流时补齐 payload.ledger_remark,不改动上游账单与镜像数据
- 用例:新增 AccountLedgerRemarkMapTest;修正唯品会 JX/MP 的列清单断言
2026-10-06 15:41:10 +08:00

167 lines
5.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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