Files
梁铭洋 97fea48393 Initial commit: 金牛集团贷款档案管理系统
导入现有代码与文档,排除 data/uploads 运行时数据。
2026-08-29 11:52:22 +08:00

235 lines
9.2 KiB
PHP

<?php
declare(strict_types=1);
function qqBuildPayableInterestData(array $rowsAll, array $financeAll, array $planAll): array
{
$todayQ = date('Y-m-d');
$monthStart = date('Y-m-01');
$monthEnd = date('Y-m-t');
$archiveMap = [];
foreach ($rowsAll as $row) {
$archiveMap[(int)($row['id'] ?? 0)] = $row;
}
$bankActive = array_values(array_filter($rowsAll, function ($a) use ($financeAll) {
$kind = normalizeArchiveKind((string)($a['archive_kind'] ?? ''));
if ($kind !== ARCHIVE_KIND_BANK_LOAN) {
return false;
}
return archiveStatusIsOngoingAggregate((string)($a['end_date'] ?? ''), getArchiveFinanceConfig($financeAll, (int)$a['id']));
}));
$instStats = [];
foreach ($bankActive as $a) {
$aid = (int)$a['id'];
$inst = (string)($a['loan_institution'] ?? '');
if ($inst === '') {
$inst = '未填写机构';
}
if (!isset($instStats[$inst])) {
$instStats[$inst] = ['loan_count' => 0, 'current_interest' => 0.0, 'settlement_interest' => 0.0, 'settlement_dates' => []];
}
$cfg = getArchiveFinanceConfig($financeAll, $aid);
$plans = (array)((getArchiveRepaymentPlan($planAll, $aid))['plans'] ?? []);
$mode = (string)($cfg['interest_mode'] ?? 'monthly');
$nextSettle = $mode === 'bullet'
? ((string)($a['end_date'] ?? '') >= $todayQ ? (string)$a['end_date'] : null)
: nextSettlementDate($todayQ, $mode, (int)($cfg['settlement_day'] ?? 21));
$instStats[$inst]['loan_count']++;
$instStats[$inst]['current_interest'] += calcCurrentInterest($a, $cfg, $plans);
if (!empty($nextSettle)) {
$instStats[$inst]['settlement_interest'] += calcSettlementPeriodInterest($a, $cfg, $plans, $todayQ);
$instStats[$inst]['settlement_dates'][(string)$nextSettle] = true;
}
}
ksort($instStats);
$instRows = [];
foreach ($instStats as $inst => $s) {
$instRows[] = [
'loan_institution' => $inst,
'loan_count' => (int)$s['loan_count'],
'current_interest' => round((float)$s['current_interest'], 2),
'settlement_interest' => round((float)$s['settlement_interest'], 2),
'settlement_date' => implode(' / ', array_keys((array)$s['settlement_dates'])),
];
}
$planRows = [];
$planTotal = 0.0;
foreach ($planAll as $aid => $cfg) {
if (empty($cfg['enabled']) || empty($cfg['plans']) || !is_array($cfg['plans'])) {
continue;
}
$aidInt = (int)$aid;
$arc = $archiveMap[$aidInt] ?? null;
if (!$arc) {
continue;
}
foreach ((array)$cfg['plans'] as $p) {
$status = (string)($p['status'] ?? 'pending');
$due = (string)($p['due_date'] ?? '');
if ($due === '' || $due < $monthStart || $due > $monthEnd || $status === 'paid') {
continue;
}
$inst = (string)($arc['loan_institution'] ?? '');
if ($inst === '') {
$inst = '未填写机构';
}
$amt = (float)($p['amount'] ?? 0);
$planRows[] = [
'loan_institution' => $inst,
'archive_name' => (string)($arc['name'] ?? ''),
'borrower' => (string)($arc['borrower'] ?? ''),
'due_date' => $due,
'amount' => $amt,
'status' => $status,
];
$planTotal += $amt;
}
}
usort($planRows, function ($a, $b) {
$cmp = strcmp((string)$a['due_date'], (string)$b['due_date']);
if ($cmp !== 0) {
return $cmp;
}
return strcmp((string)$a['loan_institution'], (string)$b['loan_institution']);
});
return ['instRows' => $instRows, 'planRows' => $planRows, 'planTotal' => round($planTotal, 2)];
}
/**
* @param string $scope all_bank|all_guar|bank_inst|guar_inst
* @param string|null $inst 与档案「贷款机构/债权人」完全一致;后两项 scope 必填
* @return array{count:int,sum_credit:float,sum_remaining:float}
*/
function qqBuildTotalsQuery(array $rowsAll, array $financeAll, string $scope, ?string $inst): array
{
if (($scope === 'bank_inst' || $scope === 'guar_inst') && ($inst === null || trim($inst) === '')) {
return ['count' => 0, 'sum_credit' => 0.0, 'sum_remaining' => 0.0];
}
$instNorm = $inst !== null ? trim($inst) : '';
$filtered = array_filter($rowsAll, function ($a) use ($financeAll, $scope, $instNorm): bool {
if (!archiveStatusIsOngoingAggregate((string)($a['end_date'] ?? ''), getArchiveFinanceConfig($financeAll, (int)$a['id']))) {
return false;
}
$kind = normalizeArchiveKind((string)($a['archive_kind'] ?? ''));
if ($scope === 'all_bank') {
return $kind === ARCHIVE_KIND_BANK_LOAN;
}
if ($scope === 'all_guar') {
return $kind === ARCHIVE_KIND_EXTERNAL_GUARANTEE;
}
$rowInst = (string)($a['loan_institution'] ?? '');
if ($scope === 'bank_inst') {
return $kind === ARCHIVE_KIND_BANK_LOAN && $rowInst === $instNorm;
}
if ($scope === 'guar_inst') {
return $kind === ARCHIVE_KIND_EXTERNAL_GUARANTEE && $rowInst === $instNorm;
}
return false;
});
$sumCredit = 0.0;
$sumRemaining = 0.0;
$count = 0;
foreach ($filtered as $a) {
$count++;
if (isset($a['credit_limit']) && $a['credit_limit'] !== null && $a['credit_limit'] !== '') {
$sumCredit += (float)$a['credit_limit'];
}
if (isset($a['remaining_limit']) && $a['remaining_limit'] !== null && $a['remaining_limit'] !== '') {
$sumRemaining += (float)$a['remaining_limit'];
}
}
return [
'count' => $count,
'sum_credit' => round($sumCredit, 2),
'sum_remaining' => round($sumRemaining, 2),
];
}
function qqSearchGuarantorArchives(array $rows, string $kw): array
{
return array_values(array_filter($rows, function ($r) use ($kw) {
if ($kw === '') {
return false;
}
return mb_stripos((string)($r['guarantor'] ?? ''), $kw) !== false;
}));
}
/**
* 历史还款:分期计划已还本金明细 + 已完成档案授信合计(按类型)
*
* @return array{plan_rows: list<array>, plan_paid_total: float, completed_bank: array{count:int,sum_credit:float}, completed_guar: array{count:int,sum_credit:float}}
*/
function qqBuildRepaymentHistory(array $rowsAll, array $planAll, array $financeAll): array
{
$map = [];
foreach ($rowsAll as $r) {
$map[(int)($r['id'] ?? 0)] = $r;
}
$planRows = [];
$planPaidTotal = 0.0;
foreach ($planAll as $aidStr => $cfg) {
$aid = (int)$aidStr;
if (empty($cfg['enabled']) || empty($cfg['plans']) || !is_array($cfg['plans'])) {
continue;
}
$arc = $map[$aid] ?? null;
foreach ($cfg['plans'] as $p) {
if ((string)($p['status'] ?? '') !== 'paid') {
continue;
}
$amt = (float)($p['amount'] ?? 0);
$planPaidTotal += $amt;
$planRows[] = [
'archive_id' => $aid,
'archive_name' => $arc ? (string)($arc['name'] ?? '') : '(档案不存在)',
'loan_institution' => $arc ? (string)($arc['loan_institution'] ?? '') : '',
'borrower' => $arc ? (string)($arc['borrower'] ?? '') : '',
'due_date' => (string)($p['due_date'] ?? ''),
'paid_at' => (string)($p['paid_at'] ?? ''),
'amount' => $amt,
];
}
}
usort($planRows, static function ($a, $b): int {
$c = strcmp((string)($b['paid_at'] ?? ''), (string)($a['paid_at'] ?? ''));
if ($c !== 0) {
return $c;
}
return ((int)($b['archive_id'] ?? 0)) <=> ((int)($a['archive_id'] ?? 0));
});
$completedBank = ['count' => 0, 'sum_credit' => 0.0];
$completedGuar = ['count' => 0, 'sum_credit' => 0.0];
foreach ($rowsAll as $a) {
$st = archiveStatusWithConfig((string)($a['end_date'] ?? ''), getArchiveFinanceConfig($financeAll, (int)$a['id']));
if ($st !== '已完成') {
continue;
}
$kind = normalizeArchiveKind((string)($a['archive_kind'] ?? ''));
$credit = $a['credit_limit'] !== null && $a['credit_limit'] !== '' ? (float)$a['credit_limit'] : 0.0;
if ($kind === ARCHIVE_KIND_BANK_LOAN) {
$completedBank['count']++;
$completedBank['sum_credit'] += $credit;
} elseif ($kind === ARCHIVE_KIND_EXTERNAL_GUARANTEE) {
$completedGuar['count']++;
$completedGuar['sum_credit'] += $credit;
}
}
return [
'plan_rows' => $planRows,
'plan_paid_total' => round($planPaidTotal, 2),
'completed_bank' => [
'count' => (int)$completedBank['count'],
'sum_credit' => round((float)$completedBank['sum_credit'], 2),
],
'completed_guar' => [
'count' => (int)$completedGuar['count'],
'sum_credit' => round((float)$completedGuar['sum_credit'], 2),
],
];
}