CaiWuXiTong/modules/EcommerceFinance/View/OrderDataPageData.php
renshen 09d593dd2d 订单数据接入聚水潭上游只读数据 (#27)
## 任务背景
订单数据页面已完成附件表头,本任务由 renshen 接入聚水潭上游真实只读数据。

## 任务目标
- 订单售后费用明细:按附件字段展示订单、商品、SKU 成本及售后关联数据。
- 商品推广费用明细:按商品/款式汇总推广费用。
- 不硬编码附件演示值;缺失字段保持空值或零。

## 预计修改路径
- modules/EcommerceFinance/Repositories/OrderDataRepository.php
- modules/EcommerceFinance/View/Composers/OrderDataPageComposer.php
- modules/EcommerceFinance/Providers/Main.php
- resources/views/business/pages/order-data/*.blade.php
- tests/Unit/EcommerceFinance/OrderDataRepositoryTest.php
- tests/Feature/Business/OrderDataPagesUpstreamTest.php

## 页面和路由
复用 order-data/after-sales-expenses、order-data/promotion-expenses 和 business.pages.show;不新增或修改 routes。

## Controller / Repository
新增 OrderDataRepository 和专用 View Composer;不修改 app/Http/Controllers/Business/Pages.php。

## 数据源、表和字段
source_key=operations_replica,数据库 JSReportReplica,schema=rpt。固定参数化只读查询使用 jst_Orders、jst_OrderItem、jst_sku、jst_skumap、jst_returnAndRefundInfo、jst_returnAndRefundItem、jst_warehouseInfo、ads_itemid_sales_cost_ratio_v2_di。字段限于订单/商品标识、时间、店铺、SKU 元数据、金额、售后数量金额及 push_fee。

## scope
- order-data.after-sales.list
- order-data.promotion.list

## 权限、菜单和公共组件
复用现有页面权限、菜单、UpstreamReadOnlyQueryFactory 和 UpstreamQueryCacheFactory;不修改权限和导航。

## 明确不修改
不修改 routes、Pages Controller、数据库迁移、上游连接配置、权限、菜单、京东钱包功能、共享数据库结构或任何上游数据。

## 范围补充:后台快照刷新
- modules/EcommerceFinance/Services/OperationsReplicaSnapshotRefresher.php
- tests/Unit/EcommerceFinance/OperationsReplicaSnapshotRefresherTest.php

刷新器仅为 order-data.after-sales.list 和 order-data.promotion.list 分派同一 OrderDataRepository 固定查询;其他 scope 继续复用现有 UpstreamBusinessInformation。

Reviewed-on: http://git.1080p.hk/WuYongJie/CaiWuXiTong/pulls/27
2026-08-11 14:50:56 +08:00

136 lines
4.6 KiB
PHP

<?php
namespace Modules\EcommerceFinance\View;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Collection;
use Modules\EcommerceFinance\Repositories\OrderDataRepository;
use Modules\EcommerceFinance\Services\UpstreamQueryCacheFactory;
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
use Throwable;
class OrderDataPageData
{
private const SOURCE = 'operations_replica';
public function __construct(
private readonly OrderDataRepository $orders,
private readonly UpstreamQueryCacheFactory $caches,
) {
}
public function afterSales(): array
{
$filters = [
'from' => $this->date(request('from')) ?: now()->subDays(6)->toDateString(),
'to' => $this->date(request('to')) ?: now()->toDateString(),
'order_no' => $this->text(request('order_no')),
'shop_name' => $this->text(request('shop_name')),
'sku_id' => $this->text(request('sku_id')),
];
[$page, $limit] = $this->pageSettings();
$cache = $this->caches->forSource(self::SOURCE);
try {
$result = $cache->remember(
'order-data.after-sales.list',
compact('filters', 'page', 'limit'),
fn () => $this->orders->afterSales($filters, $page, $limit),
);
return [
'orderDataRows' => $this->paginator((array) ($result['rows'] ?? []), $page, $limit),
'orderDataFilters' => $filters,
'orderDataStatus' => $cache->responseHeaders(),
'orderDataError' => null,
];
} catch (ServiceUnavailableHttpException $exception) {
return $this->failed($filters, $page, $limit, $exception->getMessage());
} catch (Throwable $exception) {
report($exception);
return $this->failed($filters, $page, $limit, '订单售后费用数据暂时无法读取,请稍后重试。');
}
}
public function promotionExpenses(): array
{
$filters = [
'date' => $this->date(request('date')),
'shop_name' => $this->text(request('shop_name')),
'sku_id' => $this->text(request('sku_id')),
];
[$page, $limit] = $this->pageSettings();
$cache = $this->caches->forSource(self::SOURCE);
try {
$result = $cache->remember(
'order-data.promotion.list',
compact('filters', 'page', 'limit'),
fn () => $this->orders->promotionExpenses($filters, $page, $limit),
);
return [
'orderDataRows' => $this->paginator((array) ($result['rows'] ?? []), $page, $limit),
'orderDataFilters' => array_replace($filters, ['date' => $result['date'] ?? $filters['date']]),
'orderDataStatus' => $cache->responseHeaders(),
'orderDataError' => null,
];
} catch (ServiceUnavailableHttpException $exception) {
return $this->failed($filters, $page, $limit, $exception->getMessage());
} catch (Throwable $exception) {
report($exception);
return $this->failed($filters, $page, $limit, '商品推广费用数据暂时无法读取,请稍后重试。');
}
}
private function failed(array $filters, int $page, int $limit, string $message): array
{
return [
'orderDataRows' => $this->paginator([], $page, $limit),
'orderDataFilters' => $filters,
'orderDataStatus' => [],
'orderDataError' => $message,
];
}
private function paginator(array $rows, int $page, int $limit): Paginator
{
return new Paginator(
Collection::make($rows)->map(static fn (array $row): object => (object) $row),
$limit,
$page,
[
'path' => request()->url(),
'query' => request()->except('page'),
],
);
}
/** @return array{0:int,1:int} */
private function pageSettings(): array
{
return [
max((int) request('page', 1), 1),
min(max((int) setting('default.list_limit', 25), 1), 100),
];
}
private function text(mixed $value): string
{
return mb_substr(trim((string) $value), 0, 100);
}
private function date(mixed $value): string
{
$value = trim((string) $value);
if (preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $value, $matches) !== 1) {
return '';
}
return checkdate((int) $matches[2], (int) $matches[3], (int) $matches[1]) ? $value : '';
}
}