CaiWuXiTong/modules/EcommerceFinance/Services/AccountLedgerRemarkMap.php
qiuxiang e147c90a2c 店铺收款账户账单新增「总账备注」列(按平台+账单项目映射表取值)
- 新增 ec_account_ledger_remarks 映射表与 account-ledger-remarks:import 导入命令(平台/账单项目/总账备注)
- 新增 AccountLedgerRemarkMap:按店铺名前缀识别平台,无前缀时用页签默认平台;缺失匹配保持空白
- 全部账户页签(含淘系支付宝自有列定义)在「店铺/店铺名称」右侧新增「总账备注」列,页面/列设置/导出共用同一份列定义
- 控制器在分页与导出流读取时补齐 payload.ledger_remark,不改动上游账单与共享镜像数据
- 用例:新增 AccountLedgerRemarkMapTest;修正唯品会 JX/MP 列清单断言
2026-10-06 15:42:57 +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;
}
}
}