JavaWeb課設(shè)完整實(shí)戰(zhàn):從需求分析到部署排錯)
簡介這是廣東工業(yè)大學(xué)軟件工程專業(yè)課程設(shè)計(jì)的實(shí)戰(zhàn)成果實(shí)現(xiàn)了一個功能完整的基于Web的二手商品交易系統(tǒng)。系統(tǒng)采用B/S架構(gòu)前端使用HTML/CSS/JavaScript后端為PHP數(shù)據(jù)庫為MySQL涵蓋用戶注冊登錄、商品發(fā)布、瀏覽搜索、在線留言、交易狀態(tài)跟蹤等核心模塊。壓縮包共97個文件以46個PHP源碼、1個SQL建庫腳本、1個DOC設(shè)計(jì)報告、1個TXT使用說明書為主另含大量JPEG/PNG/GIF圖片素材整體大小3.17MB目錄結(jié)構(gòu)清晰便于按需閱讀。配套文檔由3113006452陳聰撰寫包含需求分析、UML用例圖與類圖、數(shù)據(jù)庫E-R圖及表結(jié)構(gòu)說明并附軟件安裝配置與使用說明書可指導(dǎo)Tomcat/Apache與MySQL環(huán)境搭建、項(xiàng)目導(dǎo)入及運(yùn)行。這份資源既適合課程設(shè)計(jì)參考也可作為畢業(yè)設(shè)計(jì)入門范例或Web開發(fā)學(xué)習(xí)案例目前已有42人瀏覽學(xué)習(xí)。 做軟件工程課設(shè)接到“基于Web的二手商品交易系統(tǒng)”這個題目時我第一反應(yīng)是“這不是個老掉牙的商城系統(tǒng)嗎”但真正從零跑完一版帶完整源碼、數(shù)據(jù)庫和設(shè)計(jì)文檔的項(xiàng)目后我發(fā)現(xiàn)這個題目的水比我預(yù)想的深得多。它不像電商秒殺系統(tǒng)那樣拼高并發(fā)也不像人工智能項(xiàng)目那樣拼算法創(chuàng)新它比拼的是你對軟件工程全流程的理解對業(yè)務(wù)建模的把握以及對一套完整Web項(xiàng)目從設(shè)計(jì)到部署的掌控力。這篇文章我就以廣東工業(yè)大學(xué)軟件工程課設(shè)這個項(xiàng)目為藍(lán)本把選題思路、技術(shù)棧選型、數(shù)據(jù)庫設(shè)計(jì)、核心功能實(shí)現(xiàn)、文檔組織、部署排錯整個過程拆開講清楚希望給正在做類似課設(shè)或者想獨(dú)立練手Web開發(fā)的朋友一份可以直接參考的實(shí)操指南。1. 項(xiàng)目定位與需求分析二手交易課設(shè)到底在練什么1.1 為什么這個選題適合做軟件工程課設(shè)軟件工程課設(shè)和普通編程作業(yè)最大的區(qū)別在于它考察的不只是“代碼能不能跑”而是“整個軟件生命周期你能不能駕馭住”。二手商品交易系統(tǒng)這個題目之所以經(jīng)典是因?yàn)樗臉I(yè)務(wù)復(fù)雜度剛剛好用戶、商品、訂單、交易這些核心概念非常直觀不需要深度行業(yè)知識也能快速理解但同時又足夠支撐起完整的需求分析、概要設(shè)計(jì)、詳細(xì)設(shè)計(jì)、測試和部署流程。我當(dāng)時在需求分析階段就先畫了三張圖用戶用例圖、系統(tǒng)功能結(jié)構(gòu)圖、業(yè)務(wù)流程圖。用例圖定角色功能結(jié)構(gòu)圖定模塊業(yè)務(wù)流程圖定數(shù)據(jù)走向。這三張圖畫完整個項(xiàng)目的骨架基本就出來了后面寫代碼只是往骨架里填肉。很多同學(xué)上來就寫代碼寫到一半發(fā)現(xiàn)功能混亂、表結(jié)構(gòu)對不上就是因?yàn)樘^了這一步。1.2 需求邊界劃定三角色模型與權(quán)限范圍二手交易系統(tǒng)和普通電商最大的區(qū)別在于“買家”和“賣家”是同一個人群里的兩個身份。我賣出一件閑置商品時我是賣家買一件別人的舊書時我是買家這意味著系統(tǒng)不能用兩個完全獨(dú)立的模塊去處理用戶體系必須在賬號體系上統(tǒng)一、在業(yè)務(wù)角色上分離。我把系統(tǒng)用戶劃分為三類角色角色核心功能關(guān)鍵權(quán)限游客瀏覽商品、搜索僅可查看注冊用戶買家/賣家一體發(fā)布商品、修改/下架商品、下單購買、管理訂單、個人資料管理操作本人數(shù)據(jù)管理員用戶管理、商品審核/下架、分類管理、公告管理全表操作但需審計(jì)這里有一個容易被忽視的需求點(diǎn)加一個“管理員”角色。課設(shè)評審老師特別看重系統(tǒng)的完整性和安全性完全沒有后臺管理的網(wǎng)站在答辯時會被追問“如果有惡意用戶發(fā)布違規(guī)商品怎么辦”。所以哪怕管理員功能做得簡單一點(diǎn)也要有。1.3 核心業(yè)務(wù)閉環(huán)與用例拆解二手交易系統(tǒng)的核心閉環(huán)是注冊登錄 → 發(fā)布閑置 → 瀏覽搜索 → 下單購買 → 賣家處理訂單 → 確認(rèn)收貨 → 互相評價。評級功能如果時間緊迫可以不做但前面五個環(huán)節(jié)是閉環(huán)主干缺一個系統(tǒng)就不完整。在用例拆解階段我建議把每個用例的屬性寫清楚用例名稱、參與者、前置條件、主流程、異常流、后置條件。比如“買家下單購買”這個用例前置條件是“商品處于在售狀態(tài)且買家不擁有該商品”主流程是先提交訂單再生成訂單記錄異常流是“商品已下架”和“庫存不足”后置條件是“商品狀態(tài)變?yōu)橐驯慌南隆?。這些文字寫起來有點(diǎn)枯燥但后面寫接口時根本不用再想業(yè)務(wù)邏輯直接按用例翻譯成代碼就行。2. 技術(shù)選型與系統(tǒng)架構(gòu)被低估的“簡單”技術(shù)棧2.1 課設(shè)場景下的技術(shù)棧取舍很多同學(xué)在做Web課設(shè)時會陷入技術(shù)選型的糾結(jié)用ServletJSP還是Spring Boot用不用前后端分離要不要加Redis我的建議是先問自己這個項(xiàng)目的主要目標(biāo)是什么。軟件工程課設(shè)的核心目標(biāo)是展示“軟件工程過程”而不是展示你用了多少花哨的框架。我用的是經(jīng)典JavaWeb技術(shù)棧JSP Servlet MyBatis MySQL Tomcat。這套組合放在今天不算新潮但它有個天然優(yōu)勢——結(jié)構(gòu)足夠“樸素”老師一眼就能看清你的分層邏輯。Controller處理請求、Service處理業(yè)務(wù)、Mapper操作數(shù)據(jù)庫每一層職責(zé)單一打分的時候老師看到的就是“這個學(xué)生確實(shí)理解了分層架構(gòu)”。如果用了Spring Boot全家桶Comtroller里直接寫業(yè)務(wù)、連Service層都省了反而容易被質(zhì)疑“為了框架而框架沒有軟件工程思想”。2.2 分層架構(gòu)與工程目錄規(guī)劃我最終采用的是經(jīng)典MVC三層架構(gòu)加DAO層工程目錄結(jié)構(gòu)如下secondhand-trading-system/ ├── src/ │ ├── main/ │ │ ├── java/com/school/secondhand/ │ │ │ ├── controller/ # 控制層接收請求、返回視圖 │ │ │ ├── service/ # 業(yè)務(wù)層處理具體業(yè)務(wù)邏輯 │ │ │ ├── dao/ # 數(shù)據(jù)訪問層封裝數(shù)據(jù)庫操作 │ │ │ ├── entity/ # 實(shí)體類對應(yīng)數(shù)據(jù)庫表 │ │ │ ├── filter/ # 過濾器登錄校驗(yàn)、編碼處理 │ │ │ ├── util/ # 工具類分頁、文件上傳、字符串處理 │ │ │ └── interceptor/ # 攔截器統(tǒng)一權(quán)限管理 │ │ └── resources/ │ │ ├── mybatis/ # MyBatis映射文件 │ │ ├── db.properties # 數(shù)據(jù)庫配置 │ │ └── log4j.properties # 日志配置 │ ├── webapp/ │ │ ├── jsp/ # 視圖頁面 │ │ ├── static/ # css/js/images │ │ └── WEB-INF/web.xml # Web配置文件這個分層有個關(guān)鍵好處哪里出問題一眼就能定位。頁面渲染出錯查JSP接口參數(shù)問題查Controller業(yè)務(wù)邏輯問題查Service數(shù)據(jù)查詢問題查Mapper。我在開發(fā)過程中至少有三分之一的時間是靠著這種清晰的邊界快速定位bug的。2.3 為什么建議保留純JavaWeb實(shí)現(xiàn)方式雖然我后來在工作中大量使用Spring Boot但如果讓我重新做一次這個課設(shè)我依然會選擇直接用Servlet。原因很簡單直接寫Servlet能讓你真正理解HTTP請求是怎么被處理的。請求如何到達(dá)服務(wù)器、參數(shù)如何解析、Session如何管理、響應(yīng)如何寫回瀏覽器——這些內(nèi)容在Spring Boot里全被封裝掉了但軟件工程課設(shè)恰恰需要你展示對這些基礎(chǔ)知識的掌握程度。一句話總結(jié)技術(shù)選型原則課設(shè)不是生產(chǎn)環(huán)境你要選的是“最能展示自己理解深度”的棧而不是“看起來最前沿”的棧。3. 數(shù)據(jù)庫設(shè)計(jì)二手交易系統(tǒng)的核心支柱3.1 從ER分析到核心業(yè)務(wù)表數(shù)據(jù)庫設(shè)計(jì)是二手交易系統(tǒng)里最值得花時間的一部分。我最初設(shè)計(jì)的表數(shù)量是5張用戶表、商品分類表、商品表、訂單表、訂單明細(xì)表后來在寫代碼過程中發(fā)現(xiàn)還需要地址表于是加成了6張。每次功能開發(fā)中需要新表時我都會停下來反思最初的分析哪里遺漏了這也是一種很好的復(fù)盤訓(xùn)練。各表之間的核心關(guān)系是用戶與商品是一對多商品與分類是多對一訂單與用戶是多對一訂單與訂單明細(xì)是一對多。ER圖建議用工具畫我用的是draw.io畫完導(dǎo)出圖片直接貼到設(shè)計(jì)文檔里答辯時老師會重點(diǎn)看這個。3.2 核心表結(jié)構(gòu)與字段設(shè)計(jì)說明這里給出兩張核心表的建表SQL一張是用戶表一張是商品表CREATE TABLE t_user ( id INT NOT NULL AUTO_INCREMENT COMMENT 用戶ID, username VARCHAR(50) NOT NULL COMMENT 用戶名, password VARCHAR(64) NOT NULL COMMENT 密碼加鹽哈希, nickname VARCHAR(50) DEFAULT NULL COMMENT 昵稱, phone VARCHAR(20) DEFAULT NULL COMMENT 聯(lián)系電話, avatar VARCHAR(255) DEFAULT NULL COMMENT 頭像地址, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 注冊時間, status TINYINT DEFAULT 1 COMMENT 狀態(tài)1正常 0禁用, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用戶表;CREATE TABLE t_goods ( id INT NOT NULL AUTO_INCREMENT COMMENT 商品ID, seller_id INT NOT NULL COMMENT 賣家ID關(guān)聯(lián)t_user.id, category_id INT NOT NULL COMMENT 分類ID, title VARCHAR(100) NOT NULL COMMENT 商品標(biāo)題, description TEXT COMMENT 商品描述, price DECIMAL(10,2) NOT NULL COMMENT 價格, original_price DECIMAL(10,2) DEFAULT NULL COMMENT 原價/參考價, cover_image VARCHAR(255) DEFAULT NULL COMMENT 封面圖, images TEXT COMMENT 多圖地址逗號分隔, status TINYINT DEFAULT 1 COMMENT 狀態(tài)1在售 2已拍下 3已下架 4已賣出, view_count INT DEFAULT 0 COMMENT 瀏覽量, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 發(fā)布時間, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新時間, PRIMARY KEY (id), KEY idx_seller_id (seller_id), KEY idx_category_status (category_id, status), CONSTRAINT fk_goods_seller FOREIGN KEY (seller_id) REFERENCES t_user (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品表;有幾個字段設(shè)計(jì)經(jīng)驗(yàn)值得單獨(dú)說。第一所有表都用utf8mb4字符集而不是utf8。utf8在MySQL中最大只支持3字節(jié)字符而用戶昵稱里如果用戶輸入了emoji比如“交易”用utf8會直接報錯或存成亂碼。這個坑我一開始就踩了后來統(tǒng)一改成了utf8mb4。第二價格字段用DECIMAL(10,2)而不是FLOAT或DOUBLE。浮點(diǎn)數(shù)在計(jì)算機(jī)中本身就不是精確保存的0.10.2不等于0.3這種問題在金額計(jì)算中是致命的。DECIMAL是定點(diǎn)數(shù)專門用于金額場景這是金融系統(tǒng)和交易系統(tǒng)的基本常識。第三商品表加了一個status字段來管理生命周期。部分同學(xué)會用“刪除商品”這個動作來管理但真正做交易系統(tǒng)時你應(yīng)該用狀態(tài)字段因?yàn)樯唐芬坏┊a(chǎn)生訂單記錄就不能被物理刪除否則訂單明細(xì)表會關(guān)聯(lián)不到商品數(shù)據(jù)。狀態(tài)字段讓商品可以在在售、已拍下、已賣出之間流轉(zhuǎn)也方便管理員做下架處理。第四所有表都帶create_time和update_time字段。前者用于按時間排序后者方便排查數(shù)據(jù)更新的問題。這兩個字段幾乎是業(yè)務(wù)系統(tǒng)標(biāo)配不要省。3.3 事務(wù)與并發(fā)下單扣庫存的正確姿勢下單操作涉及三步檢查商品狀態(tài)、更新商品狀態(tài)為已拍下、生成訂單記錄。這三步必須是一個事務(wù)任何一個步驟失敗前面的操作都要回滾。我用MyBatis的TransactionManager配合Spring的聲明式事務(wù)實(shí)現(xiàn)Transactional(rollbackFor Exception.class) public Order createOrder(Long buyerId, Long goodsId) { Goods goods goodsDao.selectById(goodsId); // 校驗(yàn)商品存在且在售 if (goods null || goods.getStatus() ! GoodsStatus.ON_SALE) { throw new BusinessException(商品不存在或已被拍下); } // 校驗(yàn)不能買自己的商品 if (goods.getSellerId().equals(buyerId)) { throw new BusinessException(不能購買自己發(fā)布的商品); } // 更新商品狀態(tài) goodsDao.updateStatus(goodsId, GoodsStatus.ORDERED); // 創(chuàng)建訂單并插入 Order order new Order(); order.setGoodsId(goodsId); order.setBuyerId(buyerId); order.setSellerId(goods.getSellerId()); order.setAmount(goods.getPrice()); order.setStatus(OrderStatus.PENDING_PAYMENT); orderDao.insert(order); return order; }這里有一個并發(fā)場景兩個買家同時看到同一件商品同時下單。如果只做了“查狀態(tài)再更新”兩步兩個線程都可能查到“在售”狀態(tài)然后都執(zhí)行更新最后生成兩筆訂單。解決辦法有兩種一是用數(shù)據(jù)庫的樂觀鎖在更新語句中加入版本號條件UPDATE t_goods SET status2, versionversion1 WHERE id? AND status1二是用悲觀鎖SELECT ... FOR UPDATE鎖住商品行。我在課設(shè)中用的是樂觀鎖方案因?yàn)榇a改動最小而且足夠說明我理解了并發(fā)問題。答辯時老師問到并發(fā)場景我把這個方案解釋清楚是一個明顯的加分項(xiàng)。4. 核心功能實(shí)現(xiàn)從登錄到交易閉環(huán)的代碼細(xì)節(jié)4.1 用戶模塊注冊登錄的校驗(yàn)與安全處理用戶模塊是整個系統(tǒng)的入口安全處理必須一開始就做對否則后面所有功能都暴露在風(fēng)險中。密碼存儲不能用明文也不能用簡單的MD5。我在課設(shè)中使用的是加鹽MD5方案注冊時隨機(jī)生成一個鹽值把“密碼鹽值”拼接后做MD5數(shù)據(jù)庫存鹽值和哈希值。登錄時取出鹽值重新計(jì)算哈希再比對。雖然比不過BCrypt但至少能讓老師看到你對密碼安全有概念。public String encryptPassword(String rawPassword, String salt) { String salted rawPassword salt; for (int i 0; i 10; i) { salted DigestUtils.md5Hex(salted); } return salted; }注冊時還需要做的事情包括用戶名唯一性校驗(yàn)、兩次密碼一致性校驗(yàn)、手機(jī)號格式正則校驗(yàn)、用戶名非法字符過濾防SQL注入和XSS。這些校驗(yàn)既要在前端做一遍提升用戶體驗(yàn)也必須在后端做一遍保證數(shù)據(jù)安全。前端校驗(yàn)可以被繞過后端校驗(yàn)才是真正的防線。登錄成功后我用Session保存用戶基本信息同時設(shè)置Session超時時間為30分鐘。每次請求通過Filter統(tǒng)一檢查是否有登錄用戶沒有就跳轉(zhuǎn)到登錄頁。這里要注意靜態(tài)資源css、js、圖片要排除在登錄校驗(yàn)之外否則連登錄頁的樣式都加載不出來。4.2 商品模塊發(fā)布、圖片上傳與分頁檢索商品發(fā)布是用戶使用頻率最高的功能之一實(shí)現(xiàn)時有兩個關(guān)鍵點(diǎn)圖片上傳和列表分頁。圖片上傳我采用的方案是本地磁盤目錄存儲 數(shù)據(jù)庫存相對路徑。前端表單提交時用multipart/form-data格式后端用Servlet的Part接口獲取文件流。圖片路徑我做了重命名用“時間戳隨機(jī)數(shù)原始擴(kuò)展名”拼成新文件名避免重名覆蓋。同時限制了文件大小最大2MB和文件類型jpg、png、gif防止用戶上傳超大文件把服務(wù)器寫爆。String fileName System.currentTimeMillis() _ UUID.randomUUID().toString().substring(0, 8) extName; String uploadPath request.getServletContext().getRealPath(/) upload; File dir new File(uploadPath); if (!dir.exists()) { dir.mkdirs(); } part.write(uploadPath File.separator fileName);商品列表頁的分頁是我手動實(shí)現(xiàn)的PageHelper效果用的是MySQL的LIMIT語法加計(jì)算總頁數(shù)public PageResultGoods pageQuery(String keyword, Integer categoryId, int pageNum, int pageSize) { int offset (pageNum - 1) * pageSize; ListGoods list goodsDao.pageQuery(keyword, categoryId, offset, pageSize); int total goodsDao.countQuery(keyword, categoryId); int totalPages (int) Math.ceil((double) total / pageSize); return new PageResult(list, pageNum, pageSize, total, totalPages); }分頁的細(xì)節(jié)點(diǎn)是首頁和尾頁的頁碼要限制范圍pageNum傳1時不能算出負(fù)數(shù)的offset傳1000時不能一直查出空數(shù)據(jù)。JSP頁面的分頁組件我封裝成了一個JSP標(biāo)簽傳到頁面的參數(shù)就只有當(dāng)前頁和總頁數(shù)其他邏輯都封裝在后臺。4.3 訂單模塊狀態(tài)機(jī)驅(qū)動與操作權(quán)限控制訂單模塊是整個系統(tǒng)里最容易寫亂的地方因?yàn)橛唵卧诓煌A段有不同的狀態(tài)不同角色在不同狀態(tài)下的操作權(quán)限也不同。我用一個狀態(tài)機(jī)來管理訂單狀態(tài)含義可以執(zhí)行的操作目標(biāo)狀態(tài)0 待付款訂單已創(chuàng)建等待買家付款買家付款/取消1 待發(fā)貨 / 4 已取消1 待發(fā)貨買家已付款等待賣家發(fā)貨賣家發(fā)貨2 待收貨2 待收貨賣家已發(fā)貨等待買家確認(rèn)買家確認(rèn)收貨3 已完成3 已完成交易完成無可評價-4 已取消訂單關(guān)閉無-我在代碼里用了一個OrderStatus枚舉類來管理這些狀態(tài)和流轉(zhuǎn)避免在Service層里寫一堆魔法數(shù)字。這里舉個核心方法示例public void confirmReceive(Long orderId, Long currentUserId) { Order order orderDao.selectById(orderId); if (order null) { throw new BusinessException(訂單不存在); } if (!order.getBuyerId().equals(currentUserId)) { throw new BusinessException(無權(quán)操作該訂單); } if (order.getStatus() ! OrderStatus.SHIPPED) { throw new BusinessException(訂單狀態(tài)不允許確認(rèn)收貨); } orderDao.updateStatus(orderId, OrderStatus.COMPLETED); }這段代碼看起來簡單但包含三個關(guān)鍵點(diǎn)第一是業(yè)務(wù)校驗(yàn)訂單是否存在第二是權(quán)限校驗(yàn)只有買家本人能操作第三是狀態(tài)校驗(yàn)必須是已發(fā)貨狀態(tài)才能確認(rèn)收貨。三個校驗(yàn)缺一不可這是我踩過坑后才總結(jié)出來的。4.4 攔截器與安全過濾課設(shè)也要有安全意識安全管理不只是一句“登錄后才能訪問”這么簡單。我加了一個全局過濾器來處理三件事編碼統(tǒng)一、登錄校驗(yàn)、防XSS過濾。filter filter-nameGlobalFilter/filter-name filter-classcom.school.secondhand.filter.GlobalFilter/filter-class /filter filter-mapping filter-nameGlobalFilter/filter-name url-pattern/*/url-pattern /filter-mapping在GlobalFilter里完成三件工作第一設(shè)置請求和響應(yīng)的編碼為UTF-8解決中文亂碼。這里有個細(xì)節(jié)request.setCharacterEncoding(UTF-8)只對POST請求有效GET請求的亂碼需要在Tomcat配置里改URIEncodingUTF-8。第二判斷請求路徑是否在白名單里。白名單包括登錄頁、注冊頁、靜態(tài)資源、商品列表和詳情頁等不需要登錄就能訪問的路徑。其他路徑一律檢查Session中是否有登錄用戶。第三對用戶輸入做簡單的HTML標(biāo)簽過濾防止基本的XSS攻擊。雖然不如專業(yè)的ESAPI庫全面但在課設(shè)場景下已經(jīng)能解決90%的問題。5. 設(shè)計(jì)文檔撰寫讓答辯老師眼前一亮的關(guān)鍵5.1 文檔結(jié)構(gòu)應(yīng)該怎樣組織軟件工程課設(shè)的文檔質(zhì)量有時候比代碼質(zhì)量還重要因?yàn)槔蠋煵豢赡茉谀愦疝q前把所有代碼都看一遍他主要通過文檔來了解你的項(xiàng)目。我按標(biāo)準(zhǔn)軟件工程文檔結(jié)構(gòu)來寫包括五個部分需求分析、概要設(shè)計(jì)、詳細(xì)設(shè)計(jì)、系統(tǒng)測試、項(xiàng)目總結(jié)。需求分析部分要畫用例圖、寫用例說明、放功能需求列表、放數(shù)據(jù)字典截圖。概要設(shè)計(jì)部分要畫系統(tǒng)架構(gòu)圖、功能模塊圖、數(shù)據(jù)庫ER圖。詳細(xì)設(shè)計(jì)部分要寫核心模塊的設(shè)計(jì)說明附上關(guān)鍵類圖和時序圖。測試部分要貼測試用例表和測試結(jié)果截圖。項(xiàng)目總結(jié)部分寫遇到的問題和解決方案。5.2 讓文檔“有分量”的細(xì)節(jié)第一每個模塊的核心功能都要截圖圖比文字更有說服力。我在每個功能模塊下都放了“功能描述操作步驟運(yùn)行截圖”三段式描述老師只看截圖就能大致判斷系統(tǒng)完成度。第二測試部分不要只寫“測試通過”。我按功能點(diǎn)列出了測試用例表每條用例包含用例編號、測試內(nèi)容、操作步驟、預(yù)期結(jié)果、實(shí)際結(jié)果、是否通過。偶爾也留一兩條“導(dǎo)出Excel亂碼然后修復(fù)”的失敗用例這反而能體現(xiàn)測試的真實(shí)性。第三文檔里的所有圖不要手畫用工具生成。用例圖用StartUML或ProcessOnER圖用MySQL Workbench反向生成架構(gòu)圖用draw.io。手工畫的圖不規(guī)范一眼就能看出來。5.3 源碼組織與README給評審留個好印象源碼目錄要有清晰的注釋結(jié)構(gòu)。我習(xí)慣把代碼中的類分為四類實(shí)體類、工具類、業(yè)務(wù)類、控制類每類的類注釋寫明類的職責(zé)。關(guān)鍵業(yè)務(wù)方法上加方法注釋寫明業(yè)務(wù)邏輯和輸入輸出。項(xiàng)目根目錄放一個README.md內(nèi)容包含項(xiàng)目簡介、功能清單、技術(shù)棧、環(huán)境要求、部署步驟、默認(rèn)賬號。這樣老師拿到源碼后按README的步驟5分鐘就能把項(xiàng)目跑起來這個體驗(yàn)對評審來說非常加分。我在README的部署步驟里寫的是# 1. 創(chuàng)建數(shù)據(jù)庫 secondhand并導(dǎo)入 sql/secondhand.sql # 2. 修改 src/main/resources/db.properties 中的數(shù)據(jù)庫連接信息 # 3. 使用 IDEA 打開項(xiàng)目配置 Tomcat 9Deployment 選擇 war exploded # 4. 啟動項(xiàng)目訪問 http://localhost:8080/secondhand/ # 5. 管理員賬號 admin/123456測試用戶 user1/1234566. 本地運(yùn)行與部署排錯實(shí)錄6.1 從零復(fù)現(xiàn)環(huán)境準(zhǔn)備與啟動流程如果你拿到源碼后想讓項(xiàng)目跑起來按以下步驟操作安裝JDK 8、Tomcat 9、MySQL 5.7或8.0開發(fā)工具用IDEA社區(qū)版即可。創(chuàng)建一個名為secondhand的數(shù)據(jù)庫導(dǎo)入項(xiàng)目中SQL文件。注意MySQL 8.0的驅(qū)動配置和5.7不一樣驅(qū)動類名要改成com.mysql.cj.jdbc.DriverURL要加serverTimezoneAsia/ShanghaicharacterEncodingutf8mb4。修改db.properties中的數(shù)據(jù)庫賬號密碼。IDEA中配置TomcatDeployment選擇war exploded模式Application context設(shè)為/secondhand/。啟動項(xiàng)目瀏覽器訪問對應(yīng)地址。6.2 高頻問題排查速查表問題現(xiàn)象原因分析解決方案頁面全部亂碼請求/響應(yīng)編碼不一致確認(rèn)Filter中設(shè)置了UTF-8JSP頁面頂部加% page languagejava contentTypetext/html; charsetUTF-8%Tomcat啟動失敗端口被占用8080端口被其他程序占用netstat -ano數(shù)據(jù)庫連接報錯驅(qū)動類名或URL配置錯誤檢查MySQL版本對應(yīng)的驅(qū)動類名8.0用com.mysql.cj.jdbc.Driver商品圖片上傳后頁面不顯示上傳路徑和訪問路徑不一致確認(rèn)上傳目錄是webapp/upload圖片URL拼接的路徑和物理路徑對應(yīng)登錄后訪問頁面又跳回登錄頁Session寫入失敗或Cookie被禁用檢查Filter的路徑匹配規(guī)則確保一次請求只經(jīng)過一次登錄校驗(yàn)購買自己的商品沒有限制缺少業(yè)務(wù)校驗(yàn)在下單Service中增加buyerId ! sellerId判斷6.3 幾個只有實(shí)操才會踩到的坑第一個坑是Tomcat的GET請求亂碼。我花了大半天排查最后發(fā)現(xiàn)request.setCharacterEncoding只解決POST表單的亂碼GET請求帶中文參數(shù)時Tomcat默認(rèn)按ISO-8859-1解碼必須在server.xml里配置URIEncodingUTF-8。第二個坑是MyBatis的#{}和${}混用。這是我一開始圖方便在動態(tài)查詢里用了${}拼接排序字段后來被SQL注入演練工具掃出了漏洞才狠下心把所有用戶輸入都用#{}傳參。第三個坑是JSP頁面引入CSS和JS時用了相對路徑導(dǎo)致在二級URL頁面下樣式全部丟失。解決方法是統(tǒng)一用${pageContext.request.contextPath}拼接靜態(tài)資源路徑這個坑幾乎每個做JavaWeb課設(shè)的同學(xué)都會踩一次。7. 關(guān)于這次課設(shè)我想再啰嗦幾句做完整套系統(tǒng)后我最大的體會是軟件工程課設(shè)的評分標(biāo)準(zhǔn)從來不是“功能越多越好”而是“工程過程是否規(guī)范”。一個功能完整但沒寫文檔、沒做測試、代碼耦合得一塌糊涂的項(xiàng)目和一個功能適中但需求分析清晰、分層合理、測試完善、文檔規(guī)范的項(xiàng)目后者在答辯時拿到的分?jǐn)?shù)通常更高。如果你現(xiàn)在正在做類似的課設(shè)我的建議是先花一周時間把需求、表結(jié)構(gòu)、接口定義全部定下來再花兩周時間寫代碼留一周做測試和寫文檔、準(zhǔn)備答辯。很多同學(xué)習(xí)慣邊寫代碼邊改需求結(jié)果越改越亂最后文檔全是編的。這個項(xiàng)目做完之后我建議你還可以繼續(xù)擴(kuò)展把支付模擬改成微信支付沙箱、加一個Redis緩存熱點(diǎn)商品瀏覽量、把Session登錄改成JWT令牌、部署到云服務(wù)器上去。每一步擴(kuò)展對應(yīng)的都是實(shí)際工作中的一個真實(shí)場景做完這些你才算是把這門課學(xué)透了。本文還有配套的精品資源點(diǎn)擊獲取