CaiWuXiTong/modules/EcommerceFinance/Services/ShengsuanOrderProfitDetailMapper.php
renshen d79a1eedfe feat: 成本运费异常接入完整订单商品明细 (#1004)
合并 PR #1004:补全订单商品明细并按条码识别缺成本。
2026-11-09 11:02:21 +08:00

146 lines
5.3 KiB
PHP

<?php
namespace Modules\EcommerceFinance\Services;
class ShengsuanOrderProfitDetailMapper
{
public const TABLE = 'ops_shengsuan_order_profit_detail_snapshots';
private const SHOP_FIELD = 'c_900901';
private const ONLINE_ORDER_FIELD = 'c_900902';
private const INTERNAL_ORDER_FIELD = 'c_900903';
private const BARCODE_FIELD = 'c_900904';
private const PRODUCT_COST_FIELD = 'c_900201';
public function table(): string
{
return self::TABLE;
}
/**
* @param array<string,mixed> $decoded
* @return list<array<string,mixed>>
*/
public function snapshots(array $decoded): array
{
$data = is_array($decoded['data'] ?? null) ? $decoded['data'] : $decoded;
$orders = $this->section($data['ords'] ?? []);
$afterSales = $this->section($data['afters'] ?? []);
$groups = [];
foreach ([['orders', $orders], ['after_sales', $afterSales]] as [$section, $table]) {
foreach ($table['dataList'] as $record) {
$key = $this->orderKey($record);
if ($key === null) {
continue;
}
$groups[$key] ??= [
'order_key' => $key,
'shop_name' => trim((string) ($record[self::SHOP_FIELD] ?? '')),
'online_order_no' => $this->nullableText($record[self::ONLINE_ORDER_FIELD] ?? null),
'internal_order_no' => $this->nullableText($record[self::INTERNAL_ORDER_FIELD] ?? null),
'orders_headers' => $orders['fldHeads'],
'orders' => [],
'afters_headers' => $afterSales['fldHeads'],
'afters' => [],
];
$groups[$key][$section === 'orders' ? 'orders' : 'afters'][] = $record;
}
}
$now = now();
$snapshots = [];
foreach ($groups as $group) {
$missingCostBarcodes = [];
$missingCostItemCount = 0;
foreach ($group['orders'] as $item) {
if (! $this->isZeroOrMissing($item[self::PRODUCT_COST_FIELD] ?? null)) {
continue;
}
$missingCostItemCount++;
$barcode = $this->nullableText($item[self::BARCODE_FIELD] ?? null);
if ($barcode !== null && ! in_array($barcode, $missingCostBarcodes, true)) {
$missingCostBarcodes[] = $barcode;
}
}
$snapshots[] = [
'order_key' => $group['order_key'],
'shop_name' => $group['shop_name'],
'online_order_no' => $group['online_order_no'],
'internal_order_no' => $group['internal_order_no'],
'item_count' => count($group['orders']),
'after_item_count' => count($group['afters']),
'missing_cost_item_count' => $missingCostItemCount,
'order_headers' => $this->json($group['orders_headers']),
'order_items' => $this->json($group['orders']),
'after_headers' => $this->json($group['afters_headers']),
'after_items' => $this->json($group['afters']),
'missing_cost_barcodes' => $this->json($missingCostBarcodes),
'synced_at' => $now,
'created_at' => $now,
'updated_at' => $now,
];
}
return $snapshots;
}
/** @return array{fldHeads:list<array<string,mixed>>,dataList:list<array<string,mixed>>} */
private function section(mixed $section): array
{
if (! is_array($section)) {
return ['fldHeads' => [], 'dataList' => []];
}
$headers = is_array($section['fldHeads'] ?? null) ? $section['fldHeads'] : [];
return [
'fldHeads' => array_values(array_map(
static fn (array $header): array => [
'fld' => (string) ($header['fld'] ?? ''),
'title' => (string) ($header['title'] ?? ''),
'align' => $header['align'] ?? null,
'sum' => (bool) ($header['sum'] ?? false),
],
array_filter($headers, 'is_array'),
)),
'dataList' => is_array($section['dataList'] ?? null)
? array_values(array_filter($section['dataList'], 'is_array'))
: [],
];
}
/** @param array<string,mixed> $record */
private function orderKey(array $record): ?string
{
$shop = trim((string) ($record[self::SHOP_FIELD] ?? ''));
$online = trim((string) ($record[self::ONLINE_ORDER_FIELD] ?? ''));
$internal = trim((string) ($record[self::INTERNAL_ORDER_FIELD] ?? ''));
$orderNo = $online !== '' ? $online : $internal;
return $shop === '' || $orderNo === '' ? null : $shop . '|' . $orderNo;
}
private function nullableText(mixed $value): ?string
{
$text = trim((string) $value);
return $text === '' ? null : $text;
}
private function isZeroOrMissing(mixed $value): bool
{
return $value === null || trim((string) $value) === '' || (float) $value === 0.0;
}
private function json(mixed $value): string
{
return json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '[]';
}
}