首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 單機版書店管理系統:從零構建分層架構的完整實戰總結

     編輯:Java 綜合教學

Java 單機版書店管理系統:從零構建分層架構的完整實戰總結

作為 Java 學習者從「寫代碼」邁向「做項目」的關鍵一步,單機版書店管理系統涵蓋了面向對象設計、分層架構、設計模式、Swing GUI、文件持久化等核心技術點。本文將系統地梳理該項目的架構設計、功能模塊、核心代碼實現與開發心得,為初中級開發者提供完整的參考藍圖。

一、項目背景與練習目標

本項目旨在通過一個完整的桌面應用開發過程,達成以下 12 項核心訓練目標:

  1. Java 應用程序基本分析與建模能力
  2. 面向對象編程思想的深度內化
  3. 常用設計模式(工廠、單例、DAO、VO 等)綜合應用
  4. 分層架構(表現層、邏輯層、數據層)與接口導向設計
  5. 規範的 Java 包結構構建與命名規範
  6. JSE 核心知識體系的綜合運用
  7. 集合框架在業務場景中的合理選型
  8. Swing 常用組件(JTable、JTree、JMenu、JDialog 等)實戰
  9. 表現層交互機制與事件驅動模型理解
  10. IO 流與文件操作的基本技能
  11. 良好的編碼習慣(命名、註釋、異常處理、資源關閉)
  12. 調試技巧與 Bug 定位修復能力

二、功能需求與業務建模

核心業務模塊

模塊核心功能關鍵業務規則
用戶管理註冊、信息修改、刪除、查詢固定權限控制,admin 擁有所有權限
圖書管理基本信息增刪改查圖書為進貨/銷售的前置條件
進貨管理進貨單錄入(主從表)、庫存自動增加進貨圖書必須存在;庫存同步更新
銷售管理銷售單錄入(主從表)、庫存自動扣減銷售數量不能超過庫存;庫存同步更新
庫存查詢明細查看、多條件篩選支持按庫存編號、圖書、數量區間查詢

權限設計簡化版

  • 用戶按模塊劃分:用戶管理員、圖書管理員、進貨管理員、銷售管理員、庫存管理員
  • 超級管理員:擁有所有模塊操作權限
  • 登錄後動態啟用對應菜單項,未授權模塊置灰不可用

三、系統架構設計

分層架構

cn.hncu.bookStore
├── ui/              # 表現層 - Swing 界面面板
├── business/        # 邏輯層
│   ├── ebi/         # 業務接口
│   ├── ebo/         # 業務實現
│   └── factory/     # 工廠類
├── dao/             # 數據層
│   ├── dao/         # 數據接口
│   ├── impl/        # 數據實現
│   └── factory/     # 工廠類
├── vo/              # 值對象
├── util/            # 工具類
└── common/          # 公共常量/枚舉

核心設計模式應用

  • 工廠模式:各層均提供 Factory 類,實現接口與實現解耦
  • DAO 模式:數據訪問邏輯封裝,支持文件/數據庫無縫切換
  • VO 模式:Model/QueryModel 分離,封裝數據傳輸
  • 面向接口編程:上層依賴接口,工廠注入實現
  • 單例/工具類:FileIoUtil、DbUtil、UuidDao 等

四、核心模塊代碼深度解析

1. 程序入口:BookStore 主框架

public class BookStore extends JFrame {
    public BookStore() {
        super("書店管理系統---CHX---聯系QQ——619699629");
        initComponents();
        this.setContentPane(new LoginPanel(this));  // 初始顯示登錄面板
        this.setResizable(false);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        // 居中顯示
        Dimension scr = Toolkit.getDefaultToolkit().getScreenSize();
        this.setLocation(scr.width / 5, scr.height / 5);
    }
    
    // 菜單項切換面板的核心邏輯
    protected void jMenuItemUserActionPerformed(ActionEvent evt) {
        this.setContentPane(new ListPanel(this));
        this.validate();
    }
    // 其他模塊切換邏輯類似...
}

關鍵點:主框架持有所有菜單項引用,登錄成功後根據用戶類型動態 setEnabled(true/false) 控制菜單可用性;面板切換使用 setContentPane() + validate() 實現無閃爍切換。

2. 登錄模塊:完整的表現-邏輯-數據鏈路

邏輯層接口

public interface LoginEbi {
    String login(String name, String pwd);  // 成功返回 null,失敗返回錯誤信息
}

邏輯層實現

public class LoginEbo implements LoginEbi {
    @Override
    public String login(String name, String pwd) {
        UserEbi ebi = UserEbiFactory.getUserEbi();
        UserModel user = ebi.getUserByName(name);
        if (user == null) return "對不起,該用戶不存在!";
        if (!user.getPwd().equals(pwd)) return "您輸入的密碼不正確!";
        return null;
    }
}

表現層登錄面板

public class LoginPanel extends JPanel {
    private BookStore mainFrame;
    
    private void btnLoginActionPerformed(ActionEvent evt) {
        String name = tfdName.getText();
        String pwd = new String(tfpPwd.getPassword());
        
        // 硬編碼 admin 測試賬號
        if ("admin".equals(name) && "123456".equals(pwd)) {
            enableAllMenus();
            mainFrame.setContentPane(new LoginWelcomePanel());
            mainFrame.validate();
            return;
        }
        
        // 正常業務流程
        String result = LoginEbiFactory.getLoginEbi().login(name, pwd);
        if (result != null) {
            JOptionPane.showMessageDialog(mainFrame, result);
            return;
        }
        
        // 根據用戶類型啟用對應菜單
        UserModel user = UserEbiFactory.getUserEbi().getUserByName(name);
        enableMenusByUserType(user.getType());
        mainFrame.setContentPane(new LoginWelcomePanel());
        mainFrame.validate();
    }
}

3. 庫存模塊:文件持久化 DAO 實現

值對象設計

public class StockModel implements Serializable {
    private String uuid;      // 庫存編號
    private String bookUuid;  // 圖書編號
    private int sumNum;       // 庫存數量
    private String bookName;  // 顯示用書名(冗餘字段)
    // getter/setter、equals/hashCode、toString...
}

public class StockQueryModel extends StockModel {
    private int sumNum2;  // 最大庫存數量(區間查詢用)
}

文件 DAO 實現

public class StockDaoImpl implements StockDao {
    private final String FILE_NAME = "Stock.txt";
    
    @Override
    public boolean create(StockModel stock) {
        List<StockModel> lists = FileIoUtil.readFormFile(FILE_NAME);
        for (StockModel model : lists) {
            if (model.getUuid().equals(stock.getUuid())) return false;
        }
        lists.add(stock);
        FileIoUtil.write2file(lists, FILE_NAME);
        return true;
    }
    
    @Override
    public List<StockModel> getByCondition(StockQueryModel sqm) {
        List<StockModel> lists = FileIoUtil.readFormFile(FILE_NAME);
        List<StockModel> results = new ArrayList<>();
        for (StockModel model : lists) {
            if (!StringComparison.stringEquals(model.getUuid(), sqm.getUuid())) continue;
            if (!StringComparison.stringEquals(model.getBookUuid(), sqm.getBookUuid())) continue;
            if (sqm.getSumNum() > 0 && sqm.getSumNum() > model.getSumNum()) continue;
            if (sqm.getSumNum2() > 0 && sqm.getSumNum2() < model.getSumNum()) continue;
            results.add(model);
        }
        return results;
    }
    // update、getSingle、getAl 類似...
}

邏輯層薄封裝

public class StockEbo implements StockEbi {
    private StockDao dao = StockDaoFactory.getStockDao();
    
    @Override public List<StockModel> getAll() { return dao.getAl(); }
    @Override public List<StockModel> getByCondition(StockQueryModel sqm) { return dao.getByCondition(sqm); }
}

4. 進貨/銷售模塊:主從表與庫存聯動

進貨邏輯核心

public class InMainEbo implements InMainEbi {
    @Override
    public boolean create(InMainModel inMain, List<InDetailModel> inDetails) {
        // 1. 補全主單信息
        String inUuid = uuidDao.getNextUuid(UuidModelConstance.IN_MAIN);
        inMain.setUuid(inUuid);
        inMain.setInDate(System.currentTimeMillis());
        inMainDao.create(inMain);
        
        // 2. 處理明細 & 3. 入庫更新庫存
        for (InDetailModel detail : inDetails) {
            detail.setUuid(uuidDao.getNextUuid(UuidModelConstance.IN_DETAIL));
            detail.setInId(inUuid);
            double sumMoney = detail.getSumNum() * bookEbi.getSingle(detail.getBookId()).getInPrice();
            detail.setSumMoney(sumMoney);
            inDetailDao.create(detail);
            
            // 關鍵:調用庫存入庫邏輯
            putInStock(detail.getBookId(), detail.getSumNum());
        }
        return true;
    }
    
    private void putInStock(String bookId, int sumNum) {
        StockDao dao = StockDaoFactory.getStockDao();
        StockQueryModel sqm = new StockQueryModel();
        sqm.setBookUuid(bookId);
        List<StockModel> lists = dao.getByCondition(sqm);
        
        if (lists.isEmpty()) {
            // 新建庫存記錄
            StockModel stock = new StockModel();
            stock.setUuid(UuidDaoFactory.getUuidDao().getNextUuid(UuidModelConstance.STOCK));
            stock.setBookName(BookEbiFactory.getBookEbi().getSingle(bookId).getName());
            stock.setBookUuid(bookId);
            stock.setSumNum(sumNum);
            dao.create(stock);
        } else {
            // 累加庫存
            StockModel stock = lists.get(0);
            stock.setSumNum(stock.getSumNum() + sumNum);
            dao.update(stock);
        }
    }
}

銷售邏輯核心(含庫存預檢)

public class OutMainEbo implements OutMainEbi {
    @Override
    public boolean create(OutMainModel outMain, List<OutDetailModel> outDetails) {
        // 1. 庫存預檢:防止超賣
        StockDao stockDao = StockDaoFactory.getStockDao();
        for (OutDetailModel detail : outDetails) {
            StockQueryModel sqm = new StockQueryModel();
            sqm.setBookUuid(detail.getBookId());
            List<StockModel> lists = stockDao.getByCondition(sqm);
            if (lists.isEmpty() || lists.get(0).getSumNum() < detail.getSumNum()) {
                JOptionPane.showMessageDialog(null, "庫存不足,銷售失敗!");
                return false;
            }
        }
        
        // 2. 保存銷售主從單
        String outUuid = uuidDao.getNextUuid(UuidModelConstance.OUT_MAIN);
        outMain.setUuid(outUuid);
        outMain.setOutDate(System.currentTimeMillis());
        outMainDao.create(outMain);
        
        for (OutDetailModel detail : outDetails) {
            detail.setUuid(uuidDao.getNextUuid(UuidModelConstance.OUT_DETAIL));
            detail.setOutId(outUuid);
            double sumMoney = detail.getSumNum() * bookEbi.getSingle(detail.getBookId()).getOutPrice();
            detail.setSumMoney(sumMoney);
            outDetailDao.create(detail);
            
            // 3. 扣減庫存
            reduceStock(detail.getBookId(), detail.getSumNum());
        }
        return true;
    }
}

五、界面展示與交互流程

登錄界面

初始狀態:僅「注銷」菜單可用,其餘模塊菜單置灰

登錄界面

幫助界面

提示:若用戶文件被刪除,可使用 admin/123456 登錄恢復

幫助界面

管理員登錄後

所有模塊菜單點亮,擁有完整操作權限

管理員界面

庫存管理員登錄後

僅「庫存」菜單可用,體現最小權限原則

庫存管理員界面

銷售管理員登錄後

僅「銷售」菜單可用

銷售管理員界面

六、開發心得與避坑指南

「這可以說是自己的第一個項目吧。寫到現在也有10多天了,自己比較享受寫項目的這個過程... 現在對分包啊,取名字啊,值對象封裝啊,工廠方法,面向接口編程啊,表現層,邏輯層,數據層啊,這些以前用都沒用過的,那時候完全不懂得怎麼回事,不過在自己寫了之後,感覺嘛、也就那麼回事。這種項目開發,還是得自己多練練手,寫得多了,自己就熟了。以後就是手到擒來。」

關鍵經驗總結

  • 包結構規劃先行:按模塊分包,再按層分包,命名規範統一
  • 接口先行,實現後置:定義 Ebi/Dao 接口,再寫 Ebo/Impl,工廠統一創建
  • VO 分離:Model 存業務數據,QueryModel 存查詢條件,避免參數爆炸
  • 文件持久化陷阱:全量讀寫性能差,適合教學/小數據;生產必須用數據庫
  • UUID 生成策略:統一 UuidDao 管理,避免主鍵衝突
  • Swing 線程安全:耗時操作放 SwingWorker,避免界面凍結
  • 異常處理規範:DAO 層拋異常,Ebo 層捕獲轉換,UI 層友好提示

七、項目源碼獲取

完整源碼已上傳百度網盤:
鏈接: https://pan.baidu.com/s/1c2ob5r6 密碼: u6jv

總結

這個單機版書店管理系統雖然功能不復雜,但完整踐行了「分層、分模塊、分包、面向接口、工廠解耦、VO 封裝、文件持久化、Swing 界面」的工程化開發流程。對於從教學代碼走向項目代碼的開發者,它是一個極佳的練手項目——代碼量適中、架構清晰、覆蓋面廣、易於擴展。建議學習者在跑通源碼基礎上,嘗試:

  1. 將文件持久化替換為 SQLite/MySQL
  2. 引入連接池、事務管理
  3. 增加日誌框架、異常統一處理
  4. 嘗試 JavaFX 重構界面
  5. 添加單元測試覆蓋核心業務邏輯

通過不斷重構優化,真正內化「工程化思維」,這才是項目練習的最大價值。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved