案例研究:金融報表自動化¶
摘要:某台灣上市金融機構的財務部門每季耗費 3 個工作日手工製作 PDF 季報,無法附加法律效力的數位簽章,且報告品質因人工操作而參差不齊。導入 NextPDF Pro 後,季報生成時間縮短至 18 分鐘,所有輸出均附 PAdES B-LTA 簽章,並通過主管機關電子申報系統的格式驗證。
挑戰¶
現況痛點¶
一家擁有 200 名員工的台灣上市保險公司(化名「台灣安心保險」),其財務部門每季面臨以下挑戰:
- 手工製作耗時:財務分析師使用 Microsoft Office 與 Adobe Acrobat 手工製作季報,整合來自 5 個不同系統的數據,每季耗費 3 個工作日
- 缺乏法律效力簽章:向主管機關(金管會)提交的 PDF 僅有掃描簽名圖片,不符合電子申報規範中對數位簽章的要求
- 版本混亂:多人協作時,無法追蹤哪一版本是最終定稿,曾發生提交舊版本的錯誤
- 格式不一致:不同人製作的章節排版、字體選擇、圖表樣式不統一,不符合上市公司資訊揭露品質要求
- 無自動化圖表:每季需手動更新 12 個 Excel 圖表並截圖貼入 PDF
技術約束¶
- 既有系統以 PHP 8.2 + Laravel 9 構建(需升級至 PHP 8.5)
- 財務數據存於 Oracle 資料庫,需即時查詢
- 簽章金鑰必須使用公司現有的 AWS KMS(已通過 ISO 27001 稽核)
- 最終 PDF 必須符合金管會電子申報格式(PDF/A-3b)
解決方案¶
graph TD
Oracle[(Oracle DB\n財務數據)] --> Query["Laravel\nQuery Service"]
Query --> DataDTO["FinancialReportDTO\n強型別資料物件"]
DataDTO --> Engine["StreamingLayoutEngine\n串流排版"]
Engine --> Charts["ChartEngine\n12 個向量圖表"]
Engine --> TOC["自動目錄\n(兩遍渲染)"]
Engine --> PDF["原始 PDF"]
PDF --> Optimizer["PdfOptimizer\nMedium 等級"]
Optimizer --> PdfA["PDF/A-3b 轉換器"]
PdfA --> Signer["PadesSignatureAppender\nB-LTA via AWS KMS"]
Signer --> Final["最終簽署 PDF"]
Final --> Archive["文件歸檔系統"]
Final --> Regulator["金管會\n電子申報"] 核心實作¶
use NextPDF\Pro\Document\ProDocument;
use NextPDF\Pro\FlowLayout\StreamingLayoutEngine;
use NextPDF\Pro\FlowLayout\LayoutOptions;
use NextPDF\Pro\FlowLayout\Content\{Heading, Paragraph, FlowTable, ChartContent, PageBreak};
use NextPDF\Pro\Charts\{BarChart, LineChart, PieChart, ChartDataset, ChartOptions};
use NextPDF\Pro\Optimizer\{PdfOptimizer, OptimizationLevel};
use NextPDF\Pro\Compliance\PdfA\PdfAConverter;
use NextPDF\Pro\Compliance\PdfA\PdfALevel;
use NextPDF\Pro\Signatures\PAdES\{PadesSignatureAppender, PadesSignatureLevel, PadesSignatureOptions};
use NextPDF\Pro\Signatures\Hsm\Driver\AwsKmsSigningDriver;
use NextPDF\Pro\Signatures\Timestamp\TimestampAuthorityClient;
final class FinancialReportGenerator
{
public function __construct(
private readonly FinancialDataService $dataService,
private readonly AwsKmsSigningDriver $kmsDriver,
private readonly TimestampAuthorityClient $tsa,
) {}
public function generate(int $year, int $quarter): string
{
// 1. 取得強型別財務數據
$data = $this->dataService->getQuarterlyReport($year, $quarter);
// 2. 建立串流排版引擎
$engine = new StreamingLayoutEngine(
LayoutOptions::create(
pageWidth: 595.28,
pageHeight: 841.89,
marginTop: 80.0,
marginBottom: 80.0,
marginLeft: 72.0,
marginRight: 72.0,
)
);
// 設定文件語言與詮釋資料(PDF/A 必要)
$engine->setLanguage('zh-TW');
$engine->setTitle(sprintf('%d 年第 %d 季季報', $year, $quarter));
$engine->setAuthor('台灣安心保險股份有限公司');
// 3. 加入自動目錄佔位符
$toc = $engine->addTableOfContents(title: '目錄', maxLevel: 2);
// 4. 執行摘要
$engine->add(Heading::create('執行摘要', level: 1));
$engine->add(Paragraph::create($data->getExecutiveSummary()));
// 5. 財務摘要表
$engine->add(Heading::create('財務摘要', level: 2));
$engine->add(
FlowTable::create(headers: ['項目', '本季', '上季', '同比增減'])
->addRow(['總保費收入', $data->formatCurrency($data->premiumRevenue), '...', $data->formatGrowth($data->premiumGrowth)])
->addRow(['理賠支出', $data->formatCurrency($data->claimsExpense), '...', $data->formatGrowth($data->claimsGrowth)])
->addRow(['淨利潤', $data->formatCurrency($data->netProfit), '...', $data->formatGrowth($data->profitGrowth)])
->withRepeatHeaderOnNewPage(true)
);
// 6. 向量圖表
$engine->add(Heading::create('財務趨勢分析', level: 2));
$revenueChart = BarChart::create(
ChartOptions::create(title: sprintf('Q1–Q4 %d 年保費收入', $year), width: 450.0, height: 220.0)
)
->setLabels($data->getQuarterLabels())
->addDataset(
ChartDataset::create('保費收入(百萬元)')
->data($data->getQuarterlyRevenue())
->color('#1E3A8A')
);
$engine->add(ChartContent::create($revenueChart)->caption('圖 1:各季保費收入'));
$engine->add(PageBreak::create());
// 7. 投資組合分析(圓餅圖)
$portfolioChart = PieChart::create(
ChartOptions::create(title: '投資組合分布', width: 280.0, height: 280.0)
)
->setMode('donut');
foreach ($data->getPortfolioAllocation() as $category => $percentage) {
$portfolioChart->addSlice(/* ... */);
}
$engine->add(ChartContent::create($portfolioChart)->caption('圖 2:投資組合分布'));
// 8. 渲染 PDF
$rawPdf = $engine->render();
// 9. 最佳化(簽章前執行)
$optimizer = new PdfOptimizer(level: OptimizationLevel::Medium);
$optimizedPdf = $optimizer->optimize($rawPdf);
// 10. PDF/A-3b 轉換(金管會申報要求)
$converter = new PdfAConverter(level: PdfALevel::A3b);
$pdfABytes = $converter->convert($optimizedPdf);
// 11. PAdES B-LTA 簽章(使用 AWS KMS)
$hsmContext = HsmSigningContext::fromDriver(
driver: $this->kmsDriver,
publicCertificatePem: file_get_contents('/certs/financial-signing.pem'),
);
$options = PadesSignatureOptions::fromHsmContext(
hsmContext: $hsmContext,
level: PadesSignatureLevel::BLta,
timestampAuthority: $this->tsa,
reason: sprintf('CFO authorized — %dQ%d Financial Report', $year, $quarter),
location: 'Taipei, Taiwan',
);
$appender = new PadesSignatureAppender($options);
return $appender->sign($pdfABytes);
}
}
成果¶
| 指標 | 導入前 | 導入後 | 改善幅度 |
|---|---|---|---|
| 季報生成時間 | 3 個工作日(約 24 人時) | 18 分鐘(全自動) | 98.7% |
| 人工作業時數 | 24 人時/季 | 0 人時(監控 5 分鐘) | 99.7% |
| 數位簽章合規 | 無(掃描簽名) | PAdES B-LTA | 達標 |
| PDF/A 合規 | 否 | PDF/A-3b | 達標 |
| 格式一致性錯誤 | 平均 8 處/季報 | 0 | 100% |
| 主管機關退件次數 | 2.3 次/年 | 0 次 | 100% |
| PDF 檔案大小 | ~18 MB(含截圖) | ~4.2 MB(向量圖表) | 縮減 77% |
量化效益¶
- 人力節省:每季節省 24 人時,年化節省約 96 人時,按財務分析師時薪計算,年省約 NT$192,000
- 合規風險消除:消除因不符合金管會數位簽章規定可能引發的罰款風險
- 資料準確性:直接從 Oracle 資料庫取數,消除手動抄錄錯誤
技術亮點¶
強型別資料物件¶
final readonly class FinancialReportDTO
{
public function __construct(
public readonly int $year,
public readonly int $quarter,
/** @var positive-int */
public readonly int $premiumRevenue,
/** @var float */
public readonly float $premiumGrowth,
/** @var list<float> */
public readonly array $quarterlyRevenue,
/** @var array<string, float> */
public readonly array $portfolioAllocation,
public readonly string $executiveSummary,
) {}
public function formatCurrency(int $amount): string
{
return 'NT$' . number_format($amount / 1_000_000, 2) . 'M';
}
}
自動化排程(Laravel Scheduler)¶
// App\Console\Kernel.php
$schedule->call(fn() => app(FinancialReportGenerator::class)
->generate(now()->year, now()->quarter))
->quarterly()
->at('02:00')
->emailOutputOnFailure('cfo@insurance.example.com');
相關資源¶
Commercial License
This feature requires a commercial license. Contact our team for pricing and deployment support.
Contact Sales