- Added new section in AGENTS.md detailing learned workspace facts for better understanding of settlement processes. - Updated AgentNodeDestroyController to remove unnecessary checks for admin users. - Enhanced AgentSettlement controllers to assert permissions for finance adjustments and bill operations. - Improved query scopes in AgentSettlement services to ensure proper data access based on admin roles. - Refactored methods in SettlementPartyEnrichment for better bill row enrichment and data handling. - Introduced new methods in AdminAgentSettlementScope for managing agent node visibility and finance adjustments.
103 lines
3.1 KiB
PHP
103 lines
3.1 KiB
PHP
<?php
|
|
|
|
namespace App\Services\AgentSettlement;
|
|
|
|
use App\Models\AgentNode;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
/** 代理账单:汇总下级代理在本期保留的占成。 */
|
|
final class SettlementBillDownlineShareBuilder
|
|
{
|
|
public function __construct(
|
|
private readonly SettlementPartyEnrichment $partyEnrichment,
|
|
) {}
|
|
|
|
/**
|
|
* @return array{
|
|
* total: int,
|
|
* items: list<array{owner_id: int, owner_label: string, share_profit: int}>
|
|
* }
|
|
*/
|
|
public function forBill(object $bill): array
|
|
{
|
|
if ((string) $bill->bill_type !== 'agent' || (string) $bill->owner_type !== 'agent') {
|
|
return ['total' => 0, 'items' => []];
|
|
}
|
|
|
|
$ownerId = (int) $bill->owner_id;
|
|
$periodId = (int) $bill->settlement_period_id;
|
|
if ($ownerId <= 0 || $periodId <= 0) {
|
|
return ['total' => 0, 'items' => []];
|
|
}
|
|
|
|
$owner = AgentNode::query()->find($ownerId);
|
|
if ($owner === null) {
|
|
return ['total' => 0, 'items' => []];
|
|
}
|
|
|
|
$descendantIds = AgentNode::query()
|
|
->where('admin_site_id', (int) $owner->admin_site_id)
|
|
->where('id', '!=', $ownerId)
|
|
->where('path', 'like', $owner->path.'%')
|
|
->pluck('id')
|
|
->map(static fn ($id): int => (int) $id)
|
|
->all();
|
|
|
|
if ($descendantIds === []) {
|
|
return ['total' => 0, 'items' => []];
|
|
}
|
|
|
|
$rows = DB::table('settlement_bills')
|
|
->where('settlement_period_id', $periodId)
|
|
->where('bill_type', 'agent')
|
|
->where('owner_type', 'agent')
|
|
->whereIn('owner_id', $descendantIds)
|
|
->orderBy('owner_id')
|
|
->get(['owner_id', 'meta_json']);
|
|
|
|
if ($rows->isEmpty()) {
|
|
return ['total' => 0, 'items' => []];
|
|
}
|
|
|
|
$agentIds = $rows->pluck('owner_id')->map(static fn ($id): int => (int) $id)->all();
|
|
$agents = $this->partyEnrichment->loadAgents($agentIds);
|
|
|
|
$items = [];
|
|
$total = 0;
|
|
foreach ($rows as $row) {
|
|
$shareProfit = $this->shareProfitFromMeta($row->meta_json ?? null);
|
|
if ($shareProfit === 0) {
|
|
continue;
|
|
}
|
|
|
|
$agentId = (int) $row->owner_id;
|
|
$items[] = [
|
|
'owner_id' => $agentId,
|
|
'owner_label' => $this->partyEnrichment->formatAgent($agents->get($agentId), $agentId),
|
|
'share_profit' => $shareProfit,
|
|
];
|
|
$total += $shareProfit;
|
|
}
|
|
|
|
usort($items, static fn (array $a, array $b): int => $b['share_profit'] <=> $a['share_profit']
|
|
?: $a['owner_label'] <=> $b['owner_label']);
|
|
|
|
return [
|
|
'total' => $total,
|
|
'items' => $items,
|
|
];
|
|
}
|
|
|
|
private function shareProfitFromMeta(mixed $metaJson): int
|
|
{
|
|
if ($metaJson === null || $metaJson === '') {
|
|
return 0;
|
|
}
|
|
|
|
$decoded = is_string($metaJson) ? json_decode($metaJson, true) : $metaJson;
|
|
|
|
return is_array($decoded) ? (int) ($decoded['share_profit'] ?? 0) : 0;
|
|
}
|
|
}
|