CaiWuXiTong/modules/EcommerceFinance/Repositories/GoodsProfitAfsRepository.php

185 lines
6.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\Repositories;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
/**
* 订单售后费用明细:聚水潭「商品利润 → 导出明细数据 → 订单售后费用数据」原表。
*
* 原表 rpt.jst_goods_profit_kpi_items_afs 的列名是 undefined1..undefinedN,
* 中文表头放在 rpt.jst_goods_profit_kpi_items_afs_schemas(column → name)。
* 两张表都镜像到共享业务库后,页面按 schemas 映射读取字段。
*/
class GoodsProfitAfsRepository
{
public const MAIN_DATASET = 'operations_replica.rpt.jst_goods_profit_kpi_items_afs';
public const SCHEMA_DATASET = 'operations_replica.rpt.jst_goods_profit_kpi_items_afs_schemas';
private const MAIN_TABLE = 'ops_jst_goods_profit_kpi_items_afs';
private const SCHEMA_TABLE = 'ops_jst_goods_profit_kpi_items_afs_schemas';
/** @var array<string,string>|null */
private ?array $fieldColumns = null;
public function ready(): bool
{
try {
return Schema::connection($this->connectionName())->hasTable(self::MAIN_TABLE)
&& Schema::connection($this->connectionName())->hasTable(self::SCHEMA_TABLE)
&& $this->connection()->table(self::MAIN_TABLE)->exists();
} catch (\Throwable) {
return false;
}
}
/**
* 页面字段键 => 上游列名(undefinedN)。
*
* @return array<string,string>
*/
public function fieldColumns(): array
{
if ($this->fieldColumns !== null) {
return $this->fieldColumns;
}
$headers = (array) config('order_after_sales_cost.headers', []);
$map = [];
foreach ($this->connection()->table(self::SCHEMA_TABLE)->orderBy('id')->get(['column', 'name']) as $schema) {
$label = self::normalizeLabel((string) $schema->name);
$field = $headers[$label] ?? null;
// 同一科目出现多次(如 800105佣金 / 800105平台佣金)时保留第一次映射。
if ($field !== null && ! isset($map[$field])) {
$map[$field] = (string) $schema->column;
}
}
return $this->fieldColumns = $map;
}
/** @param array<string,mixed> $filters */
public function covers(array $filters): bool
{
return $this->ready() && $this->filtered($filters, $this->fieldColumns())->limit(1)->exists();
}
/**
* @param array<string,mixed> $filters
* @return array{rows:list<array<string,mixed>>,has_more:bool}
*/
public function paginate(array $filters, int $page, int $limit): array
{
$page = max($page, 1);
$limit = min(max($limit, 1), 100);
$columns = $this->fieldColumns();
$select = array_values(array_unique(array_merge(['id'], array_values($columns))));
$orderColumn = $columns['order_time'] ?? null;
$query = $this->filtered($filters, $columns)->select($select);
if ($orderColumn !== null) {
$query->orderByDesc($orderColumn);
}
$records = $query->orderByDesc('id')->forPage($page, $limit + 1)->get();
$numberKeys = array_flip((array) config('order_after_sales_cost.number_keys', []));
$rows = [];
foreach ($records as $record) {
$row = [];
foreach ($columns as $field => $column) {
$value = $record->{$column} ?? null;
if (isset($numberKeys[$field])) {
$value = $value === null ? null : (float) $value;
}
$row[$field] = is_string($value) ? trim($value) : $value;
}
$rows[] = $row;
}
return [
'rows' => array_slice($rows, 0, $limit),
'has_more' => count($rows) > $limit,
];
}
/**
* @param array<string,mixed> $filters
* @param array<string,string> $columns
*/
private function filtered(array $filters, array $columns): \Illuminate\Database\Query\Builder
{
$query = $this->connection()->table(self::MAIN_TABLE);
$from = trim((string) ($filters['from'] ?? ''));
$to = trim((string) ($filters['to'] ?? ''));
$orderNo = trim((string) ($filters['order_no'] ?? ''));
$sku = trim((string) ($filters['sku_id'] ?? ''));
$shops = array_values(array_filter((array) ($filters['shop_names'] ?? []), static fn ($v) => trim((string) $v) !== ''));
if ($from !== '' && isset($columns['order_time'])) {
$query->where($columns['order_time'], '>=', $from . ' 00:00:00');
}
if ($to !== '' && isset($columns['order_time'])) {
$query->where($columns['order_time'], '<=', $to . ' 23:59:59');
}
if ($shops !== [] && isset($columns['shop_name'])) {
$query->whereIn($columns['shop_name'], $shops);
}
if ($orderNo !== '') {
$query->where(function ($inner) use ($columns, $orderNo): void {
$matched = false;
foreach (['online_order_no', 'internal_order_no'] as $field) {
if (! isset($columns[$field])) {
continue;
}
$inner->orWhere($columns[$field], 'like', $orderNo . '%');
$matched = true;
}
if (! $matched) {
$inner->whereRaw('1 = 0');
}
});
}
if ($sku !== '' && isset($columns['product_code'])) {
$query->where($columns['product_code'], 'like', '%' . addcslashes($sku, '%_\\') . '%');
}
return $query;
}
private function connection(): \Illuminate\Database\ConnectionInterface
{
return DB::connection($this->connectionName());
}
private function connectionName(): string
{
return (string) config('upstream_mirrors.connection', 'business_mirror');
}
private static function normalizeLabel(string $label): string
{
$label = str_replace(["\u{3000}", "\u{00A0}"], ' ', $label);
$label = preg_replace('/\s+/u', ' ', $label) ?? $label;
return trim($label);
}
}