313 lines
11 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 App\Http\Controllers\Business;
use App\Models\Banking\MarketingAccountBill;
use App\Models\Banking\MarketingTaobaoAlipayBill;
use App\Models\StoreReceivables\StoreZhongcaiReconciliation;
use App\Utilities\SidebarPermissions;
use Illuminate\Routing\Controller;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Modules\EcommerceFinance\Repositories\PinduoduoSpreadBillRepository;
use Modules\EcommerceFinance\Services\UpstreamQueryCacheFactory;
class Pages extends Controller
{
/**
* 支持的营销账户平台列表
*/
protected const MARKETING_PLATFORMS = [
'taobao-alipay', 'taobao-wechat', 'pinduoduo',
'jd-wallet', 'douyin', 'dewu', 'vipshop',
];
public function home()
{
$section = collect(SidebarPermissions::navigationFor(user()))->first();
abort_if(empty($section), 403, '当前账号没有已授权的侧边栏功能。');
$page = collect($section['pages'] ?? [])->first();
$parameters = ['section' => $section['slug']];
if ($page) {
$parameters['page'] = $page['slug'];
if (! empty($page['tabs'])) {
$parameters['tab'] = $page['tabs'][0]['slug'];
}
}
return redirect()->route('business.pages.show', $parameters);
}
public function show(string $section, ?string $page = null, ?string $tab = null)
{
$sectionConfig = collect(SidebarPermissions::navigationFor(user()))->firstWhere('slug', $section);
abort_if(empty($sectionConfig), 403, '当前账号没有该页面权限。');
$pages = collect($sectionConfig['pages'] ?? []);
if ($pages->isEmpty()) {
abort_if($page !== null || $tab !== null, 404);
return view('business.pages.show', [
'section' => $sectionConfig,
'page' => null,
'tabs' => collect(),
'activeTab' => null,
]);
}
if ($page === null) {
return $this->redirectToPage($sectionConfig, $pages->first());
}
$pageConfig = $pages
->flatMap(fn (array $configuredPage) => [
$configuredPage,
...($configuredPage['children'] ?? []),
])
->firstWhere('slug', $page);
abort_if(empty($pageConfig), 404);
// 如果该 page 是一个 children-only 的父级分组,访问它时跳到第一个子页面
if (! empty($pageConfig['children']) && empty($pageConfig['tabs'])) {
return $this->redirectToPage($sectionConfig, $pageConfig['children'][0]);
}
$tabs = collect($pageConfig['tabs'] ?? []);
if ($tabs->isEmpty()) {
abort_if($tab !== null, 404);
$content = $this->resolveContent($page, null);
return view('business.pages.show', [
'section' => $sectionConfig,
'page' => $pageConfig,
'tabs' => $tabs,
'activeTab' => null,
'content' => $content,
]);
}
if ($tab === null) {
return $this->redirectToPage($sectionConfig, $pageConfig);
}
$activeTab = $tabs->firstWhere('slug', $tab);
abort_if(empty($activeTab), 404);
$content = $this->resolveContent($page, $tab);
return view('business.pages.show', [
'section' => $sectionConfig,
'page' => $pageConfig,
'tabs' => $tabs,
'activeTab' => $activeTab,
'content' => $content,
]);
}
/**
* 根据页面和标签加载对应的业务内容数据
*/
protected function resolveContent(string $page, ?string $tab): ?array
{
return match ($page) {
'marketing-account-bills' => $this->contentForMarketingAccountBills($tab),
'zhongcai-reconciliation' => $this->contentForZhongcaiReconciliation(),
default => null,
};
}
/**
* 种菜应收对账 - 加载对账数据(21字段)
*/
protected function contentForZhongcaiReconciliation(): ?array
{
return [
'view' => 'store_receivables.zhongcai_reconciliations.partials.embedded-table',
'records' => StoreZhongcaiReconciliation::orderBy('bill_date', 'desc')
->orderBy('id', 'desc')
->paginate(setting('default.list_limit', 25)),
];
}
/**
* 营销账户账单 - 加载指定平台的数据
* 淘系支付宝、拼多多使用独立的流水账单表,其他平台使用通用汇总账单表
*/
protected function contentForMarketingAccountBills(string $tab): ?array
{
if (!in_array($tab, self::MARKETING_PLATFORMS, true)) {
return null;
}
// 淘系支付宝 - 使用独立的流水账单表(8字段)
if ($tab === 'taobao-alipay') {
$request = request();
$storeName = mb_substr(trim((string) $request->query('store_name')), 0, 191);
$fromDate = $this->dateFilter($request->query('from'));
$toDate = $this->dateFilter($request->query('to'));
$query = MarketingTaobaoAlipayBill::query();
if ($storeName !== '') {
$query->where('store_name', $storeName);
}
if ($fromDate !== '') {
$query->where('transaction_date', '>=', $fromDate);
}
if ($toDate !== '') {
$query->where('transaction_date', '<=', $toDate);
}
// 筛选项下拉用的店铺列表(去重)
$storeOptions = MarketingTaobaoAlipayBill::select('store_name')
->whereNotNull('store_name')
->distinct()
->orderBy('store_name')
->pluck('store_name')
->toArray();
return [
'view' => 'banking.marketing_taobao_alipay_bills.partials.embedded-table',
'bills' => $query->orderBy('transaction_date', 'desc')
->orderBy('id', 'desc')
->paginate(setting('default.list_limit', 25))
->appends([
'store_name' => $storeName,
'from' => $fromDate,
'to' => $toDate,
]),
'filters' => [
'store_name' => $storeName,
'from' => $fromDate,
'to' => $toDate,
'store_options' => $storeOptions,
],
];
}
// 拼多多 - 从上游 JianSun.pdd_spread_bills 读取并按表1字段展示(7列)
// 表1列:店铺名称 | 金额 | 余额 | 摘要 | 收支类型 | 资金类型 | 费用时间
if ($tab === 'pinduoduo') {
$request = request();
$storeName = mb_substr(trim((string) $request->query('store_name')), 0, 191);
$fromDate = $this->dateFilter($request->query('from'));
$toDate = $this->dateFilter($request->query('to'));
$flowType = trim((string) $request->query('flow_type'));
$flowType = in_array($flowType, ['income', 'expense'], true) ? $flowType : '';
$page = max(1, (int) $request->query('page', 1));
$pageSize = min(max((int) setting('default.list_limit', 25), 1), 100);
$repository = app(PinduoduoSpreadBillRepository::class);
$cache = app(UpstreamQueryCacheFactory::class)->forSource('jiansun');
$filters = [
'store_name' => $storeName !== '' ? $storeName : null,
'from' => $fromDate !== '' ? $fromDate : null,
'to' => $toDate !== '' ? $toDate : null,
'flow_type' => $flowType !== '' ? $flowType : null,
];
// 页面只读取共享查询快照;冷查询由统一限流和熔断链路访问上游。
$result = $cache->remember(
'pinduoduo-bills.list',
['filters' => $filters, 'page' => $page, 'limit' => $pageSize],
fn () => $repository->paginate(
$filters['store_name'],
$filters['from'],
$filters['to'],
$filters['flow_type'],
$page,
$pageSize
)
);
$summary = $cache->remember(
'pinduoduo-bills.summary',
['filters' => $filters],
fn () => $repository->summary(
$filters['store_name'],
$filters['from'],
$filters['to'],
$filters['flow_type']
)
);
$storeOptions = $cache->remember(
'pinduoduo-bills.options',
[],
fn () => $repository->storeOptions()
);
// 包装成 Laravel 分页器,便于 Blade 用 $bills->links()
$bills = new LengthAwarePaginator(
Collection::make($result['rows']),
$result['total'],
$pageSize,
$page,
[
'path' => $request->url(),
'query' => $request->query(),
]
);
return [
'view' => 'banking.marketing_pinduoduo_bills.partials.embedded-table',
'bills' => $bills,
'filters' => [
'store_name' => $storeName,
'from' => $fromDate,
'to' => $toDate,
'flow_type' => $flowType,
'store_options' => $storeOptions,
],
'summary' => $summary,
];
}
// 其他平台 - 使用通用汇总账单表(15字段)
return [
'view' => 'banking.marketing_account_bills.partials.embedded-table',
'platform' => $tab,
'accounts' => MarketingAccountBill::platform($tab)
->orderBy('bill_date', 'desc')
->orderBy('id', 'desc')
->paginate(setting('default.list_limit', 25)),
];
}
protected function redirectToPage(array $section, array $page)
{
$parameters = [
'section' => $section['slug'],
'page' => $page['slug'],
];
if (! empty($page['tabs'])) {
$parameters['tab'] = $page['tabs'][0]['slug'];
}
return redirect()->route('business.pages.show', $parameters);
}
private function dateFilter(mixed $value): string
{
$date = trim((string) $value);
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $date) !== 1) {
return '';
}
[$year, $month, $day] = array_map('intval', explode('-', $date));
return checkdate($month, $day, $year) ? $date : '';
}
}