news 2026/10/18 14:10:39

医院信息管理系统HIS的Java Swing实战:报告模块开发与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
医院信息管理系统HIS的Java Swing实战:报告模块开发与避坑指南

简介:医院信息管理系统Java Swing课程设计资源,面向高校数据库课设及桌面应用开发学习者。系统完整实现药品类型与药品信息管理、药品入库出库、科室医生病人管理、处方登记及挂号诊断收费等模块,数据库层包含十多个表、三个视图、七个触发器、两个存储过程,并建立参照完整性约束,适合作为课程设计与期末项目的参考模板。包体共279个文件,主要包括61个java源码、171个class编译文件、SQL脚本、docx课程设计报告、jar依赖库及png界面截图等,压缩包整体10.62MB,便于对应查看代码、文档与界面效果。目前已有1621人学习下载。借助该项目可快速理解Swing界面与数据库交互方式,可从SQL脚本学习视图、触发器、存储过程的应用,也可对照课程设计报告撰写自身项目的设计说明,整体结构清晰,实用性强。

1. 医院信息管理系统为什么还在用 Java Swing:定位、场景与「报告」的差异点

医院信息管理系统还在用 Java Swing?我第一次接手类似项目时也这么问过。但真正看过几家区县级医院的窗口机就会发现,Swing 这类老桌面技术不但没消失,反而稳稳当当地跑在挂号、收费、药房这些每天开机即用的岗位上。原因不复杂:医院内网环境要求的是稳定和可控,不是界面多新潮。标题里「包含报告」四个字才是真正的差异点——挂号、收费谁都能写,报告这块涉及数据来源、预览排版、打印存档三条链路,做不好整个系统就没法验收。这篇内容适合拿它做课程设计的同学,也适合接手老项目的维护者。下面直接给能跑的骨架、参数和踩坑记录。

2. 搭一个 Swing 版 HIS 的最小骨架:登录跳转、数据库连接与后台任务

一个能演示的 Swing HIS 最小骨架包含三块:登录窗口到主窗口的跳转、数据库访问层、耗时任务的线程处理。很多初学 Swing 的开发者习惯把所有代码塞进按钮事件里,结果登录后主窗口卡半天,点哪里都没反应。这里给出我一般会用的结构,按这个分层写,后面加报告模块、患者列表都不会乱。

2.1 登录窗体到主窗体:用一个静态会话上下文传递用户信息

登录后主窗体的工具栏要显示当前医生姓名,报告列表要按科室过滤,打印操作要记录操作人。如果只用构造器传参,遇到对话框、跨窗体回调时会很别扭。常见做法是维护一个静态会话上下文,登录成功后写入,其它窗口随时读取。

// SessionContext.java —— 登录态的全局上下文 public class SessionContext { private static String currentUserId; private static String currentUserName; private static String deptCode; public static void setLoginUser(String userId, String userName, String deptCode) { SessionContext.currentUserId = userId; SessionContext.currentUserName = userName; SessionContext.deptCode = deptCode; } public static String getCurrentUserId() { return currentUserId; } public static String getCurrentUserName() { return currentUserName; } public static String getDeptCode() { return deptCode; } }

登录按钮的点击逻辑如下。这里特意用new String(passwordField.getPassword())而不是getText(),因为JPasswordField.getText()在较新 JDK 上已经标记废弃,密码框返回的是字符数组,取完值后最好立即清掉数组内容。

// LoginFrame.java —— 登录动作 private void doLogin() { String userId = userIdField.getText().trim(); String password = new String(passwordField.getPassword()); if (userId.isEmpty() || password.isEmpty()) { JOptionPane.showMessageDialog(this, "账号和密码都不能为空"); return; } boolean ok = UserDao.checkLogin(userId, password); if (ok) { User u = UserDao.findById(userId); SessionContext.setLoginUser(u.getUserId(), u.getUserName(), u.getDeptCode()); dispose(); new MainFrame().setVisible(true); } else { JOptionPane.showMessageDialog(this, "账号或密码错误"); } }

这段代码有两个细节值得记住。第一,登录成功之后先写SessionContext,再dispose()当前登录窗,避免新窗口打开时读不到用户信息。第二,UserDao返回的User对象里不要放密码字段,即使放在内存里也是多余风险。课程设计阶段用 SHA-256 加盐存密码就够了,生产环境建议直接上现成的登录框架,不要在 Swing 里手搓密码学。

2.2 JDBC 连接参数:驱动、URL 与编码三件套

数据库访问层是整个 HIS 的基座。这里以 MySQL 为例,但驱动类名和 URL 参数在不同数据库上差别很大,SQL Server 是com.microsoft.sqlserver.jdbc.SQLServerDriver,Oracle 又是另外一套。我习惯把连接参数集中在一个工具类里,避免每个 DAO 各写一遍。

// DBUtil.java public class DBUtil { private static final String DRIVER = "com.mysql.cj.jdbc.Driver"; private static final String URL = "jdbc:mysql://127.0.0.1:3306/his_db" + "?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"; private static final String USER = "his_app"; private static final String PASSWORD = "his_app_pwd"; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError("缺少JDBC驱动,请检查lib目录"); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }

参数说明,这三个参数踩坑率极高:

  • useSSL=false:医院内网环境用不上证书,开了反而多一次 SSL 握手,局域网里感觉不明显,但没必要。
  • serverTimezone=Asia/Shanghai:MySQL 8 的驱动强制要求时区参数,不写启动直接报错。
  • characterEncoding=utf8:中文乱码的头号源头,后面避坑章还会展开。

连接账号建议单独建一个只有该库权限的账号,不要用 root。我见过某课程设计直接把 root 密码写死在代码里提交,这种习惯放到医院内网系统里就是事故隐患。DriverManager.getConnection每次调用拿新连接,这种写法对演示系统完全够用;如果要在门诊高峰期扛并发,再换成连接池,原理和参数后面单独说。

2.3 用 SwingWorker 把慢查询挪出界面线程

Swing 的界面更新只能在事件分发线程(EDT)执行,而 JDBC 查询是阻塞操作。如果把查询直接写在按钮回调里,窗口会在查询期间完全卡住,操作系统甚至会弹「未响应」。SwingWorker 是标准解法,它在后台线程执行耗时逻辑,再切回 EDT 更新界面。

// 主窗口加载待处理报告列表 SwingWorker<List<Report>, Void> worker = new SwingWorker<>() { @Override protected List<Report> doInBackground() throws Exception { return ReportDao.findPending(SessionContext.getDeptCode()); } @Override protected void done() { try { List<Report> reports = get(); reportTableModel.setRows(reports); } catch (Exception e) { JOptionPane.showMessageDialog(MainFrame.this, "加载待处理报告失败:" + e.getMessage()); } } }; worker.execute();

使用时有两条红线:doInBackground()里面绝对不能碰任何 Swing 组件,比如不能在这里调用table.setModel();done()方法在 EDT 上执行,可以安全更新界面。如果用户在主窗口没加载完就点了关闭,done()里再去更新一个已经销毁的组件会抛异常,稳妥做法是进入done()先判断MainFrame.this.isDisplayable()。这条判断我每次写都会加,后面不知道帮我挡了多少次翻车。

3. 把「报告」做成能看能打能存档:数据模型、生成预览与打印选型

标题里的「包含报告」是验收的硬指标。挂号、收费谁都能做,报告模块要把「数据来源、预览排版、打印存档」三条链路打通,这四节按这个顺序展开。先设计表结构,再讲界面渲染,最后落到打印和导出。

3.1 检验报告与检查报告:一张主表加一张明细表

HIS 里的报告通常分两类:检验报告是数值型,比如血常规、生化,一行一个检验项,有结果值、参考范围、单位;检查报告是描述型,比如超声、CT,一大段文字结论加几张图。两类数据差异不小,但落库可以用一套结构:主表存报告头信息,明细表存检验项。检查报告不需要明细,明细表不写数据就行。

-- 报告主表 CREATE TABLE report ( report_id VARCHAR(32) PRIMARY KEY, visit_id VARCHAR(32) NOT NULL COMMENT '关联就诊记录', patient_id VARCHAR(32) NOT NULL, report_type TINYINT NOT NULL COMMENT '1=检验 2=检查', report_name VARCHAR(128) NOT NULL COMMENT '报告名称,如血常规、腹部彩超', doctor_code VARCHAR(32) COMMENT '开单医生', report_content TEXT COMMENT '检查描述或结论', report_status TINYINT DEFAULT 0 COMMENT '0未出 1已出 2已打印', print_count INT DEFAULT 0, create_time DATETIME, update_time DATETIME ); -- 检验明细表 CREATE TABLE report_item ( id INT AUTO_INCREMENT PRIMARY KEY, report_id VARCHAR(32) NOT NULL, item_code VARCHAR(32), item_name VARCHAR(64), item_value VARCHAR(32), ref_range VARCHAR(64), unit VARCHAR(16) );

字段设计的几个关键点:

  • report_content用 TEXT 而不是 VARCHAR(255)。检查报告经常是一整段描述性文字,VARCHAR 长度不够,截断后打印出来很难看。
  • report_type用 TINYINT 而不是字符串,后续扩展时可以加3=病理,前端渲染时再映射成中文。
  • print_count用来记录打印次数,医院对报告补打有审计要求,这个字段配合后面的操作日志能回答「谁在什么时候打了几次」。
  • report_status驱动报告列表的筛选,待打印和已打印分开展示,窗口科室就不会拿着旧报告去问医生。

课程设计里没有外部系统推送报告数据,常见做法是做一个报告录入界面,医生选择患者和开单申请后手工填结果。真实医院环境里,检验报告大多由 LIS 系统自动推送,但表结构完全兼容,只是数据写入方从人工录入变成接口调用。

3.2 用 JTextPane 渲染报告模板:HTML 排版比手动 setText 更稳

报告预览不能直接拿 JTable 去打印,表格打印的排版很难控制分页。我一般把报告渲染成一个只读的 JTextPane,内容用 HTML 片段拼出来。Swing 自带的 HTML 渲染支持 table、font、align 这些基础标签,足够把一份报告排得像模像样。

// ReportView.java —— 报告预览窗格 JTextPane pane = new JTextPane(); pane.setContentType("text/html; charset=UTF-8"); pane.setEditable(false); pane.setText(buildReportHtml(report, items)); private String buildReportHtml(Report r, List<ReportItem> items) { StringBuilder sb = new StringBuilder(); sb.append("<html><body style='font-family:宋体;font-size:12pt'>"); sb.append("<h2 style='text-align:center'>").append(r.getReportName()).append("</h2>"); sb.append("<p>患者ID:").append(r.getPatientId()).append("</p>"); sb.append("<table border='1' cellpadding='4' style='border-collapse:collapse'>"); sb.append("<tr><th>项目</th><th>结果</th><th>参考范围</th><th>单位</th></tr>"); for (ReportItem item : items) { sb.append("<tr><td>").append(item.getItemName()).append("</td>"); sb.append("<td>").append(item.getItemValue()).append("</td>"); sb.append("<td>").append(item.getRefRange()).append("</td>"); sb.append("<td>").append(item.getUnit()).append("</td></tr>"); } sb.append("</table><br/>"); sb.append("<p>诊断意见:").append(r.getReportContent()).append("</p>"); sb.append("</body></html>"); return sb.toString(); }

这里的关键是pane.setContentType("text/html; charset=UTF-8")一定要带charset=UTF-8,否则 HTML 里的中文在某些系统字体下直接变成方框。用 HTML 模板而不是一行行setText拼排版,好处是换行、居中、表格边框统一由 HTML 处理,后期调字号改一个地方就行。JTextPane 对 HTML 的支持有限,不要用 CSS3 或者复杂嵌套,保持这份代码的标签级别最稳。

3.3 打印的两条路:Printable 接口与打印对话框的参数控制

Swing 报告打印有两条路,一条是直接调JTextPane.print(),简单但无法精确控制页边距和缩放;另一条是实现Printable接口,用Graphics2D自己画。医院报告对版式有要求,我一般用 Printable 方案:

// ReportPrintable.java public class ReportPrintable implements Printable { private JTextPane pane; public ReportPrintable(JTextPane pane) { this.pane = pane; } @Override public int print(Graphics g, PageFormat pf, int pageIndex) { Graphics2D g2 = (Graphics2D) g; g2.translate(pf.getImageableX(), pf.getImageableY()); double scale = pf.getImageableWidth() / pane.getWidth(); g2.scale(scale, scale); pane.printAll(g2); return Printable.PAGE_EXISTS; } }

调用侧配合PrinterJob:

PrinterJob job = PrinterJob.getPrinterJob(); job.setPrintable(new ReportPrintable(pane)); if (job.printDialog()) { job.print(); ReportDao.markPrinted(reportId); }

参数说明:translate和scale是把预览窗格的内容等比缩放到打印纸的可打印区域。pane.getWidth()是预览时窗格的像素宽度,实际打印时以打印区域宽度为准做缩放。调用printDialog()让用户选打印机和份数,不要绕过对话框直接job.print(),医院窗口的打印机经常有多台,直接打印会打到默认设备上出乱子。打印成功后立刻更新report_status和print_count,这一步既是业务要求,也是审计留痕的依据。

3.4 导出与存档:CSV 给明细,PDF 给完整报告

报告只有打印不够,还要能导出存档。检验明细导出 CSV 最稳,纯 JDK 代码就能写,不引入额外依赖。需要注意 Excel 打开 UTF-8 的 CSV 会乱码,解决办法是文件开头写一个 BOM 字符:

// 检验明细导出 CSV try (OutputStreamWriter fw = new OutputStreamWriter( new FileOutputStream("report_" + reportId + ".csv"), StandardCharsets.UTF_8)) { fw.write('\ufeff'); fw.write("项目,结果,参考范围,单位\n"); for (ReportItem item : items) { fw.write(item.getItemName() + "," + item.getItemValue() + "," + item.getRefRange() + "," + item.getUnit() + "\n"); } }

CSV 方案够用但不适合整份报告,因为检查报告的段落文字里可能包含逗号和换行,CSV 转义会让人抓狂。完整报告存档用开源 PDF 库,但要注意引入依赖时确认新旧坐标不冲突,我见过有人同时引了两套包,运行时类加载直接报错。如果项目不打算引入 PDF 库,退一步的做法是把 HTML 预览内容原样存成.html文件,浏览器随时能打开和打印,只是格式约束弱一些。课程设计做到 CSV 加 HTML 存档已经完全够演示;生产环境再上 PDF 方案。

4. 用 JTable 撑起患者列表和挂号队列:渲染、分页与状态着色的三个要点

Swing 版 HIS 的界面核心就是 JTable。患者列表、挂号队列、待打印报告列表,全部要落在表格上。这一章讲三个必踩的点:TableModel 的选择、渲染器的性能边界、分页查询的写法。

4.1 自定义 TableModel 而不是硬塞 DefaultTableModel

课程设计里最常见的写法是new DefaultTableModel(data, columnNames),快是真快,但后续取单元格值时拿到的全是Object,还要自己转类型。报告列表需要根据行数据做双击查看、右键打印,我通常直接继承AbstractTableModel:

// ReportTableModel.java public class ReportTableModel extends AbstractTableModel { private List<Report> data = new ArrayList<>(); private String[] columns = {"报告ID", "患者ID", "报告名称", "类型", "状态", "报告时间"}; @Override public int getRowCount() { return data.size(); } @Override public int getColumnCount() { return columns.length; } @Override public Object getValueAt(int row, int col) { Report r = data.get(row); switch (col) { case 0: return r.getReportId(); case 1: return r.getPatientId(); case 2: return r.getReportName(); case 3: return r.getReportType() == 1 ? "检验" : "检查"; case 4: return statusText(r.getReportStatus()); case 5: return new SimpleDateFormat("yyyy-MM-dd HH:mm").format(r.getUpdateTime()); default: return ""; } } public void setRows(List<Report> rows) { this.data = rows; fireTableDataChanged(); } }

自定义模型的核心收益是列和后台对象直接映射,getValueAt里做类型转换一次完成。后面做渲染器时,getValueAt返回的已经是易读的字符串,不用在渲染器里再判断一次Report类型。setRows末尾的fireTableDataChanged()一定要调,否则界面不会刷新,这是新手最容易漏的一步。

4.2 状态列着色:渲染器的高频调用与性能边界

报告列表的「状态」列需要颜色区分:未出的标红,已出的标绿,已打印的用默认色。JTable 的单元格渲染器会被高频调用,滚动、重绘、鼠标划过都会触发,所以渲染器内部绝不能做耗时操作。

// 状态列渲染器,绑定在表格第5列 table.getColumnModel().getColumn(4).setCellRenderer( new DefaultTableCellRenderer() { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); if (!isSelected) { if ("未出".equals(value)) { c.setForeground(new Color(0xCC0000)); } else if ("已出".equals(value)) { c.setForeground(new Color(0x006600)); } else { c.setForeground(table.getForeground()); } } return c; } });

这里有两个容易被忽略的点。第一,只在!isSelected时改前景色,选中状态交给系统的选中配色,否则选了行之后红字和选中背景糊在一起。第二,不要在getTableCellRendererComponent里创建新对象,比如不要每次 new 一个Color实例,颜色值应该提成静态常量;更不要在这里查数据库,滚动一下列表就会触发几十次查询,数据库直接被打满。

4.3 SQL 分页比内存分页靠谱:LIMIT 参数与关键字转义

JTable 本身不带分页,要自己实现。内存分页适合几百条数据的小列表,但 HIS 的挂号队列一天就好几千条,全查出来塞进内存不仅慢,还会拖垮界面。我一般用 SQL 分页,数据库只返回当前页需要的数据:

// ReportDao.java —— 分页查询 public List<Report> pageQuery(String deptCode, int page, int pageSize, String keyword) { String sql = "SELECT * FROM report WHERE dept_code=? " + "AND (report_name LIKE ? OR patient_id LIKE ?) " + "LIMIT ?,?"; String like = "%" + keyword.replace("%", "\\%").replace("_", "\\_") + "%"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, deptCode); ps.setString(2, like); ps.setString(3, like); ps.setInt(4, (page - 1) * pageSize); ps.setInt(5, pageSize); return mapRows(ps.executeQuery()); } catch (SQLException e) { throw new RuntimeException("分页查询失败", e); } }

参数说明:(page - 1) * pageSize把页码从 1 换成 SQL 的偏移量,LIMIT ?,?第一个问号是 offset,第二个是行数。关键字里如果包含%或_,会把LIKE的通配符带进去,导致匹配到意料之外的记录,要提前转义成\%和\_;MySQL 默认转义字符是反斜杠,所以这里的replace写法在通用场景下没问题,跨数据库时记得确认对应数据库的转义语法。

分页控件的两个参数——页码和每页条数——建议放在表格下方,用JSpinner或下拉框控制页大小。页大小我一般设 20,报告列表这种场景 20 行一屏刚好,翻页也不频繁。页码变化时重新发起查询,数据加载用第 2 章说的 SwingWorker,避免切换页码时界面卡顿。

5. Swing + HIS 避坑指南:线程阻塞、乱码、内存泄漏与打包部署的 5 个典型问题

这一章全是真实项目里反复出现的坑,按「现象 → 原因 → 解决」记录。每一条我都亲手踩过,有些是课程设计阶段踩的,有些是老项目维护时帮别人填的。

5.1 界面假死:JDBC 查询不能占用 UI 线程

现象:点了「查询全部患者」按钮,窗口转圈十几秒没响应,标题栏出现「未响应」,任务管理器里 Java 进程的 CPU 占用却不高。

原因:按钮事件回调里直接执行了 JDBC 查询。Swing 是单线程界面模型,EDT 被阻塞后,重绘、鼠标事件全部排队,界面看起来就是死了。

解决:所有数据库查询都放进 SwingWorker 的doInBackground(),查询结束后在done()里更新表格模型。如果查询中途要弹进度条,用publish()和process()配合。还有一个隐藏变体:有人把查询放进了TableModel.getValueAt(),这比按钮事件更隐蔽,滚动表格就触发一堆查询,同样会卡死。

5.2 中文乱码:URL、建表、写文件三处编码要一致

现象:界面按钮都是正常中文,但数据库读出来的中文显示成问号;导出 CSV 后 Excel 打开是乱码。

原因:三处编码不一致。连接 URL 没带characterEncoding=utf8;建表时字符集不是utf8mb4;CSV 文件没写 BOM。

解决:连接 URL 补上编码参数;建表语句里加DEFAULT CHARSET=utf8mb4;导出 CSV 时先写一个'\ufeff'字符。还有一个容易被忽略的场景是 JTextPane 的 HTML 渲染,setContentType("text/html; charset=UTF-8")里没写 charset 也会乱码。这三个地方一次检查完,中文问题基本能根治。

5.3 窗口关了进程还在:监听器和 Timer 的内存泄漏

现象:点了窗口右上角的 X,任务管理器里 Java 进程不退,重复登录几次内存占用一直涨。

原因:窗口关闭只释放了引用,但注册的监听器没有被移除,后台 SwingWorker 或 Timer 还在运行,EDT 上仍有对窗口的引用,垃圾回收无法回收窗体对象。

解决:在窗口关闭监听器里做清理,注销鼠标监听、停止 Timer、取消还在跑的 SwingWorker:

frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { if (refreshTimer != null) { refreshTimer.stop(); } table.removeMouseListener(doubleClickAdapter); frame.dispose(); } });

顺带一提,课程设计通常不需要这么讲究,但如果你要把系统交给医院信息科验收,他们拿 VisualVM 一盯内存曲线,这个坑躲不过去。

5.4 打包后双击没反应:jar 内资源与外部配置路径

现象:在 IDE 里运行一切都正常,打成可执行 jar 后双击没反应,或者报找不到图片、配置文件。

原因:IDE 运行时用的是文件系统路径,而 jar 内部是压缩包的逻辑路径。代码里如果用new File("config/db.properties")读外部文件,jar 内部根本没有这个盘符路径。

解决:jar 内的资源用getResourceAsStream("/config/db.properties")读取;对外部配置,放在 jar 同级的config目录,用System.getProperty("user.dir")定位当前目录再拼接路径。数据库连接串这种每个医院都不一样的配置,必须走外部文件,不能写死在代码里重新打包。

5.5 隔夜后查询报错:数据库空闲连接被回收

现象:系统前一天晚上还能正常用,第二天早上第一个操作就报CommunicationsException或Connection is not available。

原因:MySQL 的wait_timeout参数默认把空闲超过 8 小时的连接关掉了,代码里持有的是早已失效的连接对象。

解决:最直接的做法是不持有长连接,每次查询从DBUtil.getConnection()拿新连接,用完马上关闭,课程设计阶段这样写完全够。医疗系统上线后要上连接池,把maxLifetime设成小于 MySQL 的wait_timeout(比如 5 分钟),这样连接池会在服务端回收前主动换新连接。注意,连接池不是配上就完事,这个数值要跟数据库配置对齐,否则照样踩坑。

6. 从能跑到能上线:报告批量导出与内存走查的收尾习惯

功能跑通后,离「能交给科室用」还差两步:批量导出和权限留痕。这两步课堂项目通常不做,但验收必查。

6.1 批量导出用 SwingWorker 带进度条

报告列表支持多选导出,导出几十份报告是耗时操作,要带进度条。用 SwingWorker 的publish和process组合:

// 批量导出任务 SwingWorker<Integer, Integer> exporter = new SwingWorker<>() { @Override protected Integer doInBackground() throws Exception { for (int i = 0; i < reportIds.size(); i++) { exportOne(reportIds.get(i)); publish(i + 1); } return reportIds.size(); } @Override protected void process(List<Integer> chunks) { int latest = chunks.get(chunks.size() - 1); progressBar.setValue(latest); progressLabel.setText("已导出 " + latest + " / " + reportIds.size()); } }; exporter.execute();

publish可以在后台线程多次调用,process在 EDT 上合并执行,不会因为频繁刷新而卡界面。

6.2 交付前用 VisualVM 盯一轮内存曲线

每次交付前我习惯做一轮走查:打开报告预览,打印,关闭窗口,连续重复十次以上,观察堆内存曲线是否持续上升不回弹。持续上升就说明有监听器泄漏或 SwingWorker 没有正确结束。再检查一遍操作日志表,确认打印、导出动作都留了痕。这两件事做完,系统才算「能上线」而不是「能演示」。

说到打印走查,血泪经验是:把检验报告、检查报告各打印一份样张,确认字体是宋体而不是默认字体,页码位置正确,超过一页的长报告第二页表头有没有重复。这些细节决定窗口科室愿不愿意真的用你的系统。

我自己每次交付 Swing 项目,最后一天就只做这两件事:把批量导出和日志补完,再用 VisualVM 盯二十分钟内存曲线。这两件事帮我挡掉了至少三次验收翻车。打印确认也是同样套路,所有报告类型过一遍预览才敢说完工。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 14:09:45

VTK 体绘制入门到排错:传递函数、采样距离与透明度的关系

同一组体数据&#xff0c;换一套透明度曲线就可能从“什么都看不到”变成“整块发白”。这不一定意味着数据出错&#xff0c;也不一定是显卡不够快。体绘制把空间采样、标量映射与沿光线合成连接在一起&#xff0c;任何一个环节的约定变化都可能影响最终画面。本文先建立可解释…

作者头像 李华
网站建设 2026/10/18 14:09:14

多动作并行异步DDPG:选矿浮选智能决策模型工程化实践

简介&#xff1a;本资源是一篇发表于《控制与决策》2022年第8期的学术论文PDF&#xff0c;面向自动化、智能优化与流程工业领域的研究生、科研人员及工程技术人员&#xff0c;聚焦选矿过程运行指标的动态决策难题。针对传统深度确定性策略梯度&#xff08;DDPG&#xff09;算法…

作者头像 李华
网站建设 2026/10/18 14:06:43

COMP9021 Python课程测验资料:练习与高频题型拆解

简介&#xff1a;COMP9021课程Python编程练习与测验资料合集是悉尼新南威尔士大学Principles of Programming课程的配套练习包&#xff0c;覆盖Python基础语法、控制流、函数定义与简单数据结构应用&#xff0c;适合课堂同步巩固、课后自主复习与考前冲刺。压缩包共7个文件&…

作者头像 李华
网站建设 2026/10/18 14:06:41

charinfo-droiyanOnline:Unicode字符元数据解析工具链

简介&#xff1a;本资源是Droiyan Online游戏后端中「角色信息与聊天服务」的核心源码实现&#xff0c;面向C游戏服务器开发者、网络编程学习者及MMORPG架构研究者&#xff0c;聚焦于实时通信、用户状态管理与跨平台文本处理等关键后端能力。压缩包共75个文件&#xff0c;以19个…

作者头像 李华