1. 项目概述
这个基于Java Swing和MySQL的药品管理系统是一个典型的桌面端应用程序,主要用于药品的库存管理、进销存记录和用户权限控制。系统采用MVC架构模式,前端使用Swing实现GUI界面,后端通过JDBC连接MySQL数据库进行数据持久化。
作为一个完整的药品管理解决方案,系统涵盖了药品从采购入库到销售出库的全流程管理,同时提供了用户权限管理、数据查询统计等辅助功能。特别适合作为医药零售门店、小型诊所或医药公司的库存管理系统使用。
2. 系统架构设计
2.1 技术选型分析
选择Java Swing作为GUI框架主要基于以下考虑:
- 跨平台性:Swing作为Java标准库的一部分,可以在任何支持Java的平台上运行
- 开发效率:相比JavaFX等其他GUI框架,Swing学习曲线更平缓
- 组件丰富:提供表格、按钮、输入框等完整的UI组件库
MySQL作为数据库的选择理由:
- 开源免费:适合教学和小型商业项目
- 性能稳定:能够满足药品管理系统的数据存储需求
- 社区支持:丰富的文档和问题解决方案
2.2 系统模块划分
系统主要分为以下几个功能模块:
- 用户管理模块:处理用户注册、登录和权限控制
- 药品库存模块:管理药品基本信息、库存数量和价格
- 进货管理模块:记录药品采购入库信息
- 销售管理模块:处理药品销售出库操作
- 报表统计模块:提供各类业务数据的查询统计功能
3. 数据库设计
3.1 核心表结构
sql复制-- 药品信息表
CREATE TABLE medicine (
meid VARCHAR(20) PRIMARY KEY, -- 药品编号
mename VARCHAR(50) NOT NULL, -- 药品名称
meprice DECIMAL(10,2) NOT NULL,-- 药品价格
meamounts INT NOT NULL, -- 库存数量
meproducer VARCHAR(100), -- 生产商
indate DATE -- 入库日期
);
-- 员工信息表
CREATE TABLE EmployeeInfo (
eid VARCHAR(20) PRIMARY KEY, -- 员工ID
password VARCHAR(50) NOT NULL, -- 密码
ename VARCHAR(50) NOT NULL, -- 员工姓名
sex INT DEFAULT 0, -- 性别(0女,1男)
age INT, -- 年龄
joydate DATE, -- 入职日期
isAdmin INT DEFAULT 0 -- 是否管理员(0普通,1管理员)
);
-- 进货记录表
CREATE TABLE marketinview (
meid VARCHAR(20), -- 药品编号
mename VARCHAR(50), -- 药品名称
inprice DECIMAL(10,2), -- 进货价格
inamounts INT, -- 进货数量
indate DATE, -- 进货日期
producer VARCHAR(100) -- 生产商
);
-- 销售记录表
CREATE TABLE marketoutview (
meid VARCHAR(20), -- 药品编号
mename VARCHAR(50), -- 药品名称
outprice DECIMAL(10,2), -- 销售价格
outamounts INT, -- 销售数量
outdate DATE, -- 销售日期
incomes DECIMAL(10,2) -- 销售金额
);
3.2 数据库连接实现
系统使用单独的SqlHelper类封装数据库操作:
java复制public class SqlHelper {
private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
private static final String URL = "jdbc:mysql://localhost:3306/medicine_db?useSSL=false";
private static final String USER = "root";
private static final String PWD = "password";
static {
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public ResultSet query(String sql, String[] params) throws SQLException {
Connection conn = DriverManager.getConnection(URL, USER, PWD);
PreparedStatement ps = conn.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
ps.setString(i + 1, params[i]);
}
return ps.executeQuery();
}
public boolean update(String sql, String[] params) throws SQLException {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = DriverManager.getConnection(URL, USER, PWD);
ps = conn.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
ps.setString(i + 1, params[i]);
}
return ps.executeUpdate() > 0;
} finally {
if (ps != null) ps.close();
if (conn != null) conn.close();
}
}
}
4. 核心功能实现
4.1 用户登录与权限控制
系统采用基于角色的访问控制(RBAC)模型,用户分为普通员工和管理员两种角色:
java复制public class LoginView extends JFrame {
private JTextField userIdField = new JTextField(15);
private JPasswordField passwordField = new JPasswordField(15);
public LoginView() {
// 初始化UI组件
setTitle("药品管理系统登录");
setLayout(new GridLayout(3, 2, 10, 10));
add(new JLabel("用户名:"));
add(userIdField);
add(new JLabel("密码:"));
add(passwordField);
JButton loginBtn = new JButton("登录");
loginBtn.addActionListener(e -> {
String userId = userIdField.getText().trim();
String password = new String(passwordField.getPassword());
try {
String sql = "SELECT * FROM EmployeeInfo WHERE eid=? AND password=?";
ResultSet rs = new SqlHelper().query(sql, new String[]{userId, password});
if (rs.next()) {
boolean isAdmin = rs.getInt("isAdmin") == 1;
new MainFrame(isAdmin).setVisible(true);
this.dispose();
} else {
JOptionPane.showMessageDialog(this, "用户名或密码错误", "登录失败", JOptionPane.ERROR_MESSAGE);
}
} catch (SQLException ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(this, "数据库连接失败", "系统错误", JOptionPane.ERROR_MESSAGE);
}
});
add(loginBtn);
setSize(300, 150);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
}
4.2 药品库存管理
药品库存管理界面采用JTable展示药品信息,支持增删改查操作:
java复制public class MedicinePanel extends JPanel {
private DefaultTableModel tableModel = new DefaultTableModel();
private JTable medicineTable = new JTable(tableModel);
public MedicinePanel() {
// 初始化表格列
String[] columns = {"药品ID", "药品名称", "价格", "库存量", "生产商", "入库日期"};
tableModel.setColumnIdentifiers(columns);
// 添加功能按钮
JButton addBtn = new JButton("新增药品");
addBtn.addActionListener(e -> new AddMedicineDialog(this));
JButton updateBtn = new JButton("修改药品");
updateBtn.addActionListener(e -> {
int row = medicineTable.getSelectedRow();
if (row == -1) {
JOptionPane.showMessageDialog(this, "请先选择一行", "提示", JOptionPane.WARNING_MESSAGE);
return;
}
String meid = (String) tableModel.getValueAt(row, 0);
new UpdateMedicineDialog(meid, this);
});
// 布局设置
setLayout(new BorderLayout());
add(new JScrollPane(medicineTable), BorderLayout.CENTER);
JPanel btnPanel = new JPanel();
btnPanel.add(addBtn);
btnPanel.add(updateBtn);
add(btnPanel, BorderLayout.SOUTH);
// 加载数据
refreshTable();
}
public void refreshTable() {
tableModel.setRowCount(0);
try {
String sql = "SELECT * FROM medicine";
ResultSet rs = new SqlHelper().query(sql, new String[]{});
while (rs.next()) {
Object[] row = {
rs.getString("meid"),
rs.getString("mename"),
rs.getBigDecimal("meprice"),
rs.getInt("meamounts"),
rs.getString("meproducer"),
rs.getDate("indate")
};
tableModel.addRow(row);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
4.3 进货管理实现
进货管理模块记录药品采购信息,并自动更新库存:
java复制public class PurchaseDialog extends JDialog {
private JTextField meidField = new JTextField();
private JTextField menameField = new JTextField();
private JTextField priceField = new JTextField();
private JTextField amountField = new JTextField();
private JTextField producerField = new JTextField();
public PurchaseDialog(JFrame parent) {
super(parent, "药品进货", true);
// 表单布局
setLayout(new GridLayout(6, 2, 10, 10));
add(new JLabel("药品编号:"));
add(meidField);
add(new JLabel("药品名称:"));
add(menameField);
add(new JLabel("进货单价:"));
add(priceField);
add(new JLabel("进货数量:"));
add(amountField);
add(new JLabel("生产厂商:"));
add(producerField);
JButton confirmBtn = new JButton("确认");
confirmBtn.addActionListener(e -> {
try {
// 开启事务
Connection conn = DriverManager.getConnection(SqlHelper.URL, SqlHelper.USER, SqlHelper.PWD);
conn.setAutoCommit(false);
// 1. 插入进货记录
String insertSql = "INSERT INTO marketinview VALUES(?,?,?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(insertSql);
ps.setString(1, meidField.getText());
ps.setString(2, menameField.getText());
ps.setBigDecimal(3, new BigDecimal(priceField.getText()));
ps.setInt(4, Integer.parseInt(amountField.getText()));
ps.setDate(5, new Date(System.currentTimeMillis()));
ps.setString(6, producerField.getText());
ps.executeUpdate();
// 2. 更新库存
String updateSql = "UPDATE medicine SET meamounts=meamounts+? WHERE meid=?";
ps = conn.prepareStatement(updateSql);
ps.setInt(1, Integer.parseInt(amountField.getText()));
ps.setString(2, meidField.getText());
int affected = ps.executeUpdate();
if (affected == 0) {
// 如果药品不存在,则新增
String insertMedSql = "INSERT INTO medicine VALUES(?,?,?,?,?,?)";
ps = conn.prepareStatement(insertMedSql);
ps.setString(1, meidField.getText());
ps.setString(2, menameField.getText());
ps.setBigDecimal(3, new BigDecimal(priceField.getText()));
ps.setInt(4, Integer.parseInt(amountField.getText()));
ps.setString(5, producerField.getText());
ps.setDate(6, new Date(System.currentTimeMillis()));
ps.executeUpdate();
}
conn.commit();
JOptionPane.showMessageDialog(this, "进货成功", "提示", JOptionPane.INFORMATION_MESSAGE);
dispose();
} catch (Exception ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(this, "进货失败: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE);
}
});
add(confirmBtn);
setSize(400, 300);
setLocationRelativeTo(parent);
}
}
5. 系统部署与运行
5.1 环境准备
- JDK安装:确保安装JDK 8或更高版本
- MySQL安装:安装MySQL 5.7或更高版本
- 数据库初始化:
sql复制CREATE DATABASE medicine_db; USE medicine_db; -- 执行前面提供的建表语句
5.2 项目配置
-
添加MySQL JDBC驱动依赖:
- Maven项目在pom.xml中添加:
xml复制<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.23</version> </dependency> - 非Maven项目下载mysql-connector-java.jar并添加到classpath
- Maven项目在pom.xml中添加:
-
修改SqlHelper类中的数据库连接信息:
java复制private static final String URL = "jdbc:mysql://localhost:3306/medicine_db?useSSL=false"; private static final String USER = "your_username"; private static final String PWD = "your_password";
5.3 常见问题解决
-
数据库连接失败:
- 检查MySQL服务是否启动
- 确认用户名密码是否正确
- 检查防火墙是否阻止了3306端口
-
界面显示异常:
- 确保使用Swing的EDT线程更新UI:
java复制SwingUtilities.invokeLater(() -> { // UI更新代码 });
- 确保使用Swing的EDT线程更新UI:
-
中文乱码问题:
- 在数据库连接URL中添加字符集参数:
java复制jdbc:mysql://localhost:3306/medicine_db?useUnicode=true&characterEncoding=UTF-8
- 在数据库连接URL中添加字符集参数:
6. 项目扩展建议
-
数据备份与恢复:
- 添加定期自动备份数据库功能
- 实现数据导入导出功能
-
报表生成:
- 集成JasperReport生成销售报表
- 添加数据统计图表展示
-
网络版扩展:
- 将系统改造成C/S架构
- 使用Socket或RMI实现客户端与服务端通信
-
条码扫描支持:
- 集成条码扫描设备接口
- 实现药品扫码入库和销售
-
数据安全增强:
- 对用户密码进行加密存储
- 添加操作日志记录功能
在实际开发中,我建议采用以下最佳实践:
- 使用连接池管理数据库连接,如HikariCP
- 将SQL语句提取到配置文件中,便于维护
- 添加输入验证,防止SQL注入
- 使用日志框架(如Log4j)替代System.out.println
- 考虑使用SwingWorker处理耗时操作,避免界面卡顿