feat: 增强代理结算和账单管理功能

- 在多个控制器中引入 SettlementPartyEnrichment 服务,以优化代理结算和账单的处理逻辑。
- 更新 AgentSettlementBillIndexController 和 AgentSettlementBillShowController,支持根据账单 ID 和关键字进行查询。
- 在 AgentSettlementPeriodCloseController 中添加对站点管理权限的验证,确保只有具备相应权限的管理员能够关闭账期。
- 在 AgentSettlementPeriodIndexController 中更新账期数据的返回格式,提升数据的完整性和可用性。
- 引入对相对占成比例的支持,增强代理资料的管理能力,确保数据一致性。
This commit is contained in:
2026-06-05 18:00:56 +08:00
parent a44679665d
commit 2d32f006c5
63 changed files with 4893 additions and 288 deletions

View File

@@ -20,6 +20,57 @@ final class AgentOverdueGuard
->exists();
}
public static function agentHasSevereOverdueBills(int $agentNodeId, int $days = 7): bool
{
if ($agentNodeId <= 0) {
return false;
}
$cutoff = now()->subDays($days);
return DB::table('settlement_bills')
->where('owner_type', 'agent')
->where('owner_id', $agentNodeId)
->where('status', 'overdue')
->where('unpaid_amount', '>', 0)
->where('updated_at', '<', $cutoff)
->exists();
}
public static function agentLineHasSevereOverdueBills(int $agentNodeId, int $days = 7): bool
{
if ($agentNodeId <= 0) {
return false;
}
$agent = DB::table('agent_nodes')->where('id', $agentNodeId)->first();
if ($agent === null) {
return false;
}
// 获取该代理的所有祖先节点ID包括自己
$path = (string) $agent->path;
if ($path === '') {
// 根节点,只检查自己
$ancestorIds = [$agentNodeId];
} else {
// 解析 path 获取所有祖先ID
$parts = explode('/', trim($path, '/'));
$ancestorIds = array_map('intval', $parts);
$ancestorIds[] = $agentNodeId;
}
$cutoff = now()->subDays($days);
return DB::table('settlement_bills')
->where('owner_type', 'agent')
->where('status', 'overdue')
->where('unpaid_amount', '>', 0)
->where('updated_at', '<', $cutoff)
->whereIn('owner_id', $ancestorIds)
->exists();
}
public static function assertAgentMayGrantCredit(int $agentNodeId): void
{
if (self::agentHasOverdueBills($agentNodeId)) {
@@ -28,4 +79,46 @@ final class AgentOverdueGuard
]);
}
}
public static function assertAgentLineMayPlaceBet(int $agentNodeId, ?int $severeOverdueDays = null): void
{
$days = $severeOverdueDays ?? config('agent_line_defaults.overdue.severe_days_threshold', 7);
if (self::agentLineHasSevereOverdueBills($agentNodeId, $days)) {
throw \Illuminate\Validation\ValidationException::withMessages([
'credit' => ['agent_line_severe_overdue'],
]);
}
}
public static function parentHasOverdueBills(int $agentNodeId): bool
{
if ($agentNodeId <= 0) {
return false;
}
$agent = DB::table('agent_nodes')->where('id', $agentNodeId)->first();
if ($agent === null || $agent->parent_id === null) {
return false;
}
return DB::table('settlement_bills')
->where('owner_type', 'agent')
->where('owner_id', $agent->parent_id)
->where('status', 'overdue')
->where('unpaid_amount', '>', 0)
->exists();
}
public static function assertMayOperateWhenParentOverdue(int $agentNodeId): void
{
if (! config('agent_line_defaults.overdue.cascade_freeze_on_parent_overdue', false)) {
return;
}
if (self::parentHasOverdueBills($agentNodeId)) {
throw \Illuminate\Validation\ValidationException::withMessages([
'parent_id' => ['parent_overdue'],
]);
}
}
}