Java 生成「客户月度对账单」PDF 示例(Template)
Template1 页 A4 · 27 KB
明细自动分页、底部汇总行与页码,适合按月向客户出具对账单。

输出规格
- PDF 页数
- 1
- 图表类型
- Template
- 模板行数
- 159
- 源码行数
- 102
Java 源码 · JQuickBiz72Statement.java
以下代码即生成上方 PDF 的完整测试类,可直接复制运行;Maven 依赖见快速开始章节。
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright (c) [2025-2099] Martin ([email protected])
*/
package com.github.paohaijiao.biz.scenario;
import com.github.paohaijiao.config.JPdfConfig;
import com.github.paohaijiao.executor.JQuickPdfFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
public class JQuickBiz72Statement {
private static final String PDF_DIR = "D:/test/pdf/";
private static final String OUT_FILE = PDF_DIR + "72_statement.pdf";
private static final String TEMPLATE = "report/biz/72_statement.txt";
public static void main(String[] args) throws IOException {
new File(PDF_DIR).mkdirs();
// ---------- 1. 数据(普通 Map,无实体类)----------
String[][] raw = {
{"2026-09-01", "8 月平台服务费", "12800.00"},
{"2026-09-03", "接口调用超量计费(8 月)", "3260.00"},
{"2026-09-05", "银行转账还款", "-20000.00"},
{"2026-09-08", "报表模板定制(合同 BJ-2026-0032)", "26000.00"},
{"2026-09-12", "SVG 图表组件扩容", "4500.00"},
{"2026-09-15", "技术服务费(9 月上半月)", "6400.00"},
{"2026-09-18", "票据贴现利息", "-860.00"},
{"2026-09-20", "银行转账还款", "-30000.00"},
{"2026-09-22", "数据迁移服务", "32000.00"},
{"2026-09-25", "账户管理费", "-120.00"},
{"2026-09-26", "现场部署服务费", "19200.00"},
{"2026-09-28", "培训服务(4 场)", "19200.00"},
{"2026-09-30", "结算尾差调整", "-38.50"}
};
BigDecimal increase = BigDecimal.ZERO;
BigDecimal decrease = BigDecimal.ZERO;
Map<String, Object> data = new LinkedHashMap<String, Object>();
for (int i = 0; i < raw.length; i++) {
BigDecimal amount = new BigDecimal(raw[i][2]);
boolean isIncrease = amount.signum() >= 0;
if (isIncrease) {
increase = increase.add(amount);
} else {
decrease = decrease.add(amount.abs());
}
String suffix = String.valueOf(i + 1);
data.put("t" + suffix, raw[i][0]);
data.put("s" + suffix, raw[i][1]);
data.put("a" + suffix, amount.abs().setScale(2, RoundingMode.HALF_UP).toPlainString());
data.put("d" + suffix, isIncrease ? "增加" : "减少");
}
BigDecimal opening = new BigDecimal("186420.00");
BigDecimal closing = opening.add(increase).subtract(decrease);
data.put("customer", "××城市商业银行股份有限公司");
data.put("month", "2026 年 09 月");
data.put("monthShort", "09");
data.put("billNo", "DZ-2026-09-0871");
data.put("openingBalance", opening.setScale(2, RoundingMode.HALF_UP).toPlainString());
data.put("totalIncrease", increase.setScale(2, RoundingMode.HALF_UP).toPlainString());
data.put("totalDecrease", decrease.setScale(2, RoundingMode.HALF_UP).toPlainString());
data.put("closingBalance", closing.setScale(2, RoundingMode.HALF_UP).toPlainString());
// ---------- 2. 渲染 ----------
JPdfConfig config = new JPdfConfig();
config.getLayoutConfig().setFlexLayout(true);
config.getDoc().setMargins(Arrays.asList(45, 45, 55, 45));
config.getFooterConfig().setEnabled(true);
config.getFooterConfig().setFontSize(8);
config.getFooterConfig().setPageNumberFormat("第 %d 页");
JQuickPdfFactory factory = new JQuickPdfFactory(config);
factory.bindAll(data);
byte[] pdf = factory.executeResource(TEMPLATE);
FileOutputStream out = new FileOutputStream(OUT_FILE);
out.write(pdf);
out.close();
System.out.println("已生成:" + OUT_FILE);
}
}最小调用(3 行)
不想读完整测试类?核心就是这三行:绑定图表 SVG,执行模板,拿到 PDF 字节流。可直接复制到你的项目里。
// config 可省略;svg 来自 JChartRendererFactory.renderChart(...),见下方完整源码
byte[] pdf = new JQuickPdfFactory(config).executeResource("report/biz/72_statement.txt");模板文件 · 72_statement.txt
类 HTML + 内联 CSS 的模板语法,绑定数据后渲染为 PDF。下方为完整模板,可直接复制,也可下载原始文件。
<pdf>
<body>
<h1 style="fontSize:19pt; bold:true; color:#1F4E79; textAlignment:center; characterSpacing:4pt">'客户月度对账单'</h1>
<p style="fontSize:9pt; color:#5A6B7C; textAlignment:center; marginBottom:8px">'STATEMENT OF ACCOUNT · '${month}</p>
<table style="width:100%; fontSize:9pt">
<tr>
<td style="width:78px; padding:5px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'客户名称'</td>
<td style="width:180px; padding:5px; border:1px solid #C3CDD8">${customer}</td>
<td style="width:78px; padding:5px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'对账月份'</td>
<td style="padding:5px; border:1px solid #C3CDD8">${month}</td>
</tr>
<tr>
<td style="padding:5px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'账单编号'</td>
<td style="padding:5px; border:1px solid #C3CDD8">${billNo}</td>
<td style="padding:5px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'币种'</td>
<td style="padding:5px; border:1px solid #C3CDD8">'人民币(CNY)'</td>
</tr>
</table>
<table style="width:100%; fontSize:9pt; marginTop:6px">
<tr>
<td style="padding:6px; backgroundColor:#F7F9FC; bold:true; color:#1F4E79; border:1px solid #C3CDD8; width:160px">'期初余额('${monthShort}' 月初)'</td>
<td style="padding:6px; backgroundColor:#F7F9FC; textAlign:right; bold:true; border:1px solid #C3CDD8">'¥'${openingBalance}</td>
</tr>
</table>
<table style="width:100%; fontSize:8.5pt; marginTop:6px">
<tr>
<th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79; width:34px">'序号'</th>
<th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79; width:110px">'交易时间'</th>
<th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79; width:200px">'项目'</th>
<th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79; width:80px">'金额'</th>
<th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79; width:70px">'方向'</th>
</tr>
<tr>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'1'</td>
<td style="padding:5px; border:1px solid #C3CDD8">${t1}</td>
<td style="padding:5px; border:1px solid #C3CDD8">${s1}</td>
<td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a1}</td>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d1}</td>
</tr>
<tr>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'2'</td>
<td style="padding:5px; border:1px solid #C3CDD8">${t2}</td>
<td style="padding:5px; border:1px solid #C3CDD8">${s2}</td>
<td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a2}</td>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d2}</td>
</tr>
<tr>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'3'</td>
<td style="padding:5px; border:1px solid #C3CDD8">${t3}</td>
<td style="padding:5px; border:1px solid #C3CDD8">${s3}</td>
<td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a3}</td>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d3}</td>
</tr>
<tr>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'4'</td>
<td style="padding:5px; border:1px solid #C3CDD8">${t4}</td>
<td style="padding:5px; border:1px solid #C3CDD8">${s4}</td>
<td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a4}</td>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d4}</td>
</tr>
<tr>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'5'</td>
<td style="padding:5px; border:1px solid #C3CDD8">${t5}</td>
<td style="padding:5px; border:1px solid #C3CDD8">${s5}</td>
<td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a5}</td>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d5}</td>
</tr>
<tr>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'6'</td>
<td style="padding:5px; border:1px solid #C3CDD8">${t6}</td>
<td style="padding:5px; border:1px solid #C3CDD8">${s6}</td>
<td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a6}</td>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d6}</td>
</tr>
<tr>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'7'</td>
<td style="padding:5px; border:1px solid #C3CDD8">${t7}</td>
<td style="padding:5px; border:1px solid #C3CDD8">${s7}</td>
<td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a7}</td>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d7}</td>
</tr>
<tr>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'8'</td>
<td style="padding:5px; border:1px solid #C3CDD8">${t8}</td>
<td style="padding:5px; border:1px solid #C3CDD8">${s8}</td>
<td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a8}</td>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d8}</td>
</tr>
<tr>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'9'</td>
<td style="padding:5px; border:1px solid #C3CDD8">${t9}</td>
<td style="padding:5px; border:1px solid #C3CDD8">${s9}</td>
<td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a9}</td>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d9}</td>
</tr>
<tr>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'10'</td>
<td style="padding:5px; border:1px solid #C3CDD8">${t10}</td>
<td style="padding:5px; border:1px solid #C3CDD8">${s10}</td>
<td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a10}</td>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d10}</td>
</tr>
<tr>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'11'</td>
<td style="padding:5px; border:1px solid #C3CDD8">${t11}</td>
<td style="padding:5px; border:1px solid #C3CDD8">${s11}</td>
<td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a11}</td>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d11}</td>
</tr>
<tr>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'12'</td>
<td style="padding:5px; border:1px solid #C3CDD8">${t12}</td>
<td style="padding:5px; border:1px solid #C3CDD8">${s12}</td>
<td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a12}</td>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d12}</td>
</tr>
<tr>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'13'</td>
<td style="padding:5px; border:1px solid #C3CDD8">${t13}</td>
<td style="padding:5px; border:1px solid #C3CDD8">${s13}</td>
<td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a13}</td>
<td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d13}</td>
</tr>
</table>
<table style="width:100%; fontSize:9pt; marginTop:4px">
<tr>
<td style="padding:4px; border:1px solid #C3CDD8; textAlign:center; backgroundColor:#F7F9FC; color:#5A6B7C; width:25%">'本期增加'</td>
<td style="padding:4px; border:1px solid #C3CDD8; textAlign:center; backgroundColor:#F7F9FC; color:#5A6B7C; width:25%">'本期减少'</td>
<td style="padding:4px; border:1px solid #C3CDD8; textAlign:center; backgroundColor:#F7F9FC; color:#5A6B7C; width:25%">'期末余额'</td>
<td style="padding:4px; border:1px solid #C3CDD8; textAlign:center; backgroundColor:#F7F9FC; color:#5A6B7C; width:25%">'应结金额'</td>
</tr>
<tr>
<td style="padding:5px; border:1px solid #C3CDD8; textAlign:center; bold:true; color:#B71C1C; fontSize:11pt">'¥'${totalIncrease}</td>
<td style="padding:5px; border:1px solid #C3CDD8; textAlign:center; bold:true; color:#1B5E20; fontSize:11pt">'¥'${totalDecrease}</td>
<td style="padding:5px; border:1px solid #C3CDD8; textAlign:center; bold:true; color:#1F4E79; fontSize:11pt">'¥'${closingBalance}</td>
<td style="padding:5px; border:1px solid #C3CDD8; textAlign:center; bold:true; color:#1F4E79; fontSize:11pt">'¥'${closingBalance}</td>
</tr>
</table>
<div style="borderLeft:4px solid #2F5597; background:#F7F9FC; padding:7px 9px; marginTop:8px">
<p style="fontSize:8.5pt; color:#333333">'说明:本对账单统计 '${month}' 期间全部交易。「本期增加」为借方发生额合计,「本期减少」为贷方发生额合计,期末余额 = 期初余额 + 本期增加 - 本期减少。'</p>
<p style="fontSize:8.5pt; color:#333333">'请贵司于收到本对账单之日起 7 个工作日内核对并签章回传,逾期未回复视为无异议。'</p>
</div>
<div style="display:flex; justifyContent:space-between; marginTop:26px">
<div style="width:230px; borderTop:1px solid #5A6B7C; paddingTop:4px">
<p style="fontSize:9pt; color:#5A6B7C; textAlignment:center">'客户方(盖章): '</p>
</div>
<div style="width:230px; borderTop:1px solid #5A6B7C; paddingTop:4px">
<p style="fontSize:9pt; color:#5A6B7C; textAlignment:center">'财务专用章: '</p>
</div>
</div>
</body>
</pdf>同分组示例
本页 PDF、预览图、模板与源码均由 JQuick-Pdf 真实运行产出,可自由复制到你的项目中使用。