:從數(shù)據(jù)庫(kù)設(shè)計(jì)到答辯全流程實(shí)戰(zhàn))
簡(jiǎn)介這份答辯PPT圍繞基于JSP的服裝商城交易管理系統(tǒng)展開(kāi)適合計(jì)算機(jī)相關(guān)專(zhuān)業(yè)畢業(yè)生用于畢業(yè)設(shè)計(jì)答辯展示也可供學(xué)習(xí)JSP開(kāi)發(fā)的學(xué)習(xí)者參考。內(nèi)容涵蓋選題意義、課題背景、開(kāi)發(fā)環(huán)境與核心技術(shù)等模塊重點(diǎn)講解JSP、Servlet、數(shù)據(jù)庫(kù)管理以及HTML、CSS、JavaScript等前端交互技術(shù)的配合應(yīng)用并梳理了需求分析、系統(tǒng)設(shè)計(jì)、編碼實(shí)現(xiàn)、測(cè)試優(yōu)化與上線維護(hù)的完整開(kāi)發(fā)流程能幫助讀者理解服裝電商平臺(tái)的功能架構(gòu)與業(yè)務(wù)邏輯。資源共1個(gè)PPTX文件壓縮包大小約739KB內(nèi)容精煉、結(jié)構(gòu)清晰便于直接修改和演示適合需要快速準(zhǔn)備答辯材料或了解該課題設(shè)計(jì)思路的用戶。目前已有138人學(xué)習(xí)下載。1. 為什么“JSP服裝商城交易管理系統(tǒng)”仍是答辯臺(tái)和實(shí)訓(xùn)課上的常青樹(shù)每年畢業(yè)季都能看到一批以“基于JSP的XX管理系統(tǒng)”為標(biāo)題的答辯PPT其中服裝商城交易管理系統(tǒng)是出場(chǎng)率最高的那一類(lèi)。原因很實(shí)際JSP上手曲線平緩ServletJSPMySQL這套組合能在一臺(tái)普通筆記本上完整跑通不用額外裝中間件不用寫(xiě)復(fù)雜的前后端分離工程一個(gè)人從建表到答辯演示完全能控制住工作量。對(duì)做課設(shè)、畢業(yè)設(shè)計(jì)的人來(lái)說(shuō)這不是“過(guò)時(shí)技術(shù)”而是一個(gè)能在有限時(shí)間內(nèi)把業(yè)務(wù)邏輯講清楚、把數(shù)據(jù)庫(kù)設(shè)計(jì)展示明白的最小可行方案。這套系統(tǒng)核心要回答的問(wèn)題有三個(gè)服裝商品怎么錄入和展示用戶怎么把衣服放進(jìn)購(gòu)物車(chē)并完成下單訂單和庫(kù)存怎么保持一致。答得越具體答辯通過(guò)率越高。這篇筆記就按我實(shí)際帶課設(shè)項(xiàng)目的順序來(lái)寫(xiě)從表和模塊拆到避坑細(xì)節(jié)再落到一份能直接照著講的答辯PPT結(jié)構(gòu)。2. 先把業(yè)務(wù)邊界劃清楚服裝商城的模塊拆分與核心表設(shè)計(jì)2.1 從“買(mǎi)衣服”這個(gè)動(dòng)作反推系統(tǒng)邊界不少同學(xué)拿到題目第一反應(yīng)是去網(wǎng)上找一份代碼然后對(duì)著表結(jié)構(gòu)反推功能。這其實(shí)把順序搞反了。正確的做法是先從“買(mǎi)衣服”這個(gè)動(dòng)作出發(fā)把用戶會(huì)做的事列出來(lái)系統(tǒng)邊界自然會(huì)浮出來(lái)。用戶要注冊(cè)登錄要瀏覽服裝列表要按分類(lèi)篩選要看商品詳情圖要把不同尺碼和顏色的衣服加入購(gòu)物車(chē)要填收貨地址并提交訂單要查看訂單狀態(tài)甚至取消訂單。管理員要做的事情是另一條線登錄后臺(tái)、錄入服裝商品、維護(hù)庫(kù)存、處理訂單發(fā)貨、查看統(tǒng)計(jì)信息。把這兩條線畫(huà)成用例圖模塊就清楚了。這決定了系統(tǒng)的標(biāo)準(zhǔn)分層前臺(tái)用戶模塊、商品展示模塊、購(gòu)物車(chē)模塊、訂單模塊后臺(tái)管理員模塊、商品管理模塊、訂單管理模塊。答辯PPT里最忌諱把“商城系統(tǒng)”寫(xiě)成無(wú)限擴(kuò)張的大平臺(tái)比如硬加秒殺、優(yōu)惠券、積分商城。那些功能不是不能提但放在“后期展望”里講一句就夠了放進(jìn)核心設(shè)計(jì)里只會(huì)讓評(píng)委追問(wèn)到你自己都圓不住。2.2 用戶-商品-訂單三張核心表的設(shè)計(jì)與字段取舍服裝商城和圖書(shū)商城最大的不同在規(guī)格上同一款衣服有顏色、尺碼、庫(kù)存三個(gè)維度。很多照著圖書(shū)商城寫(xiě)的代碼在這地方翻車(chē)因?yàn)閳D書(shū)不拆SKU訂單明細(xì)表里只存一個(gè)商品ID和數(shù)量遇到服裝就必須多存兩個(gè)字段。我一般建議核心表控制在五到六張用戶表、服裝商品表含款式與分類(lèi)、商品規(guī)格表顏色尺碼庫(kù)存、購(gòu)物車(chē)表、訂單表、訂單明細(xì)表。以下是一組可以直接用的建表SQLCREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, nickname VARCHAR(50), receiver_name VARCHAR(30), receiver_phone VARCHAR(20), receiver_address VARCHAR(255), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE clothing_product ( id INT PRIMARY KEY AUTO_INCREMENT, category_id INT NOT NULL, name VARCHAR(100) NOT NULL, description TEXT, main_image VARCHAR(255), price DECIMAL(10,2) NOT NULL, status TINYINT DEFAULT 1, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE product_sku ( id INT PRIMARY KEY AUTO_INCREMENT, product_id INT NOT NULL, color VARCHAR(30), size VARCHAR(10), stock INT NOT NULL DEFAULT 0, UNIQUE KEY uk_product_spec (product_id, color, size) );這段SQL有三個(gè)值得在答辯時(shí)主動(dòng)講的點(diǎn)。第一個(gè)是UNIQUE KEY uk_product_spec它從數(shù)據(jù)庫(kù)層面保證同一個(gè)商品不會(huì)出現(xiàn)兩條顏色、尺碼完全相同的SKU記錄比單純?cè)贘ava代碼里判斷更可靠。第二個(gè)是DECIMAL(10,2)而不是FLOAT存價(jià)格避免浮點(diǎn)誤差這也是評(píng)委喜歡聽(tīng)的細(xì)節(jié)。第三是status字段做上下架邏輯而不是刪商品記錄因?yàn)橛唵蚊骷?xì)外鍵往往指著商品表物理刪除會(huì)導(dǎo)致歷史訂單變成殘缺數(shù)據(jù)。訂單表建議單獨(dú)設(shè)計(jì)不要把整個(gè)購(gòu)物車(chē)內(nèi)容直接拼成一個(gè)字符串塞進(jìn)訂單表。CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL UNIQUE, user_id INT NOT NULL, total_amount DECIMAL(10,2) NOT NULL, status TINYINT NOT NULL DEFAULT 0, receiver_name VARCHAR(30), receiver_phone VARCHAR(20), receiver_address VARCHAR(255), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE order_item ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL, product_id INT NOT NULL, product_name VARCHAR(100), color VARCHAR(30), size VARCHAR(10), price DECIMAL(10,2) NOT NULL, quantity INT NOT NULL DEFAULT 1 );order_item里冗余了product_name、color、size、price這幾個(gè)字段。這不是偷懶而是刻意做的反規(guī)范化設(shè)計(jì)商品名稱(chēng)和價(jià)格可能調(diào)整用戶歷史訂單里的快照不應(yīng)該跟著變。答辯時(shí)能說(shuō)清“為什么冗余”和“為什么不能冗余”比背十條范式規(guī)則有用得多。2.3 用DBHelper加JSP跑通最小骨架設(shè)計(jì)完表下一步不是急著寫(xiě)十幾個(gè)頁(yè)面而是先跑通一條最小鏈路首頁(yè)列出商品點(diǎn)詳情加入購(gòu)物車(chē)提交訂單。這條鏈路通了剩下都是重復(fù)勞動(dòng)。連接數(shù)據(jù)庫(kù)的公共類(lèi)我習(xí)慣寫(xiě)成靜態(tài)方法避免每個(gè)Servlet重復(fù)寫(xiě)DriverManager代碼public class DBHelper { static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { String url jdbc:mysql://localhost:3306/fashion_mall ?useUnicodetruecharacterEncodingutf8 useSSLfalseserverTimezoneAsia/Shanghai; return DriverManager.getConnection(url, root, 123456); } public static void close(ResultSet rs, Statement stmt, Connection conn) { if (rs ! null) { try { rs.close(); } catch (SQLException e) {} } if (stmt ! null) { try { stmt.close(); } catch (SQLException e) {} } if (conn ! null) { try { conn.close(); } catch (SQLException e) {} } } }useUnicodetruecharacterEncodingutf8和serverTimezone這兩段參數(shù)是新手最容易漏的。漏掉前者JSP頁(yè)面上顯示的就是問(wèn)號(hào)漏掉后者換裝新版MySQL驅(qū)動(dòng)后連接會(huì)直接報(bào)時(shí)區(qū)錯(cuò)誤。我在帶項(xiàng)目時(shí)要求所有人都把這串URL作為固定模板不要每次現(xiàn)敲。商品列表頁(yè)用JSTL循環(huán)輸出比在JSP里寫(xiě)一堆Java腳本片段干凈很多% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % table c:forEach items${productList} varp tr td${p.name}/td td${p.price}/td tda hrefproduct/detail?id${p.id}查看詳情/a/td /tr /c:forEach /table這里有個(gè)容易踩的坑${p.name}依賴(lài)Product類(lèi)里的getName()方法如果屬性名和表字段不一致頁(yè)面上會(huì)直接空。比字段命名更隱蔽的是EL表達(dá)式取值的順序問(wèn)題items${productList}里的productList必須是request或session域里的屬性名而不能是Servlet里的局部變量名。所以Servlet里一定要寫(xiě)request.setAttribute(productList, list);少了這一行頁(yè)面會(huì)靜默地什么都不輸出。3. 購(gòu)物車(chē)與下單從Session存儲(chǔ)到庫(kù)存扣減的完整實(shí)現(xiàn)3.1 購(gòu)物車(chē)為什么不適合塞進(jìn)Session但課設(shè)里還是要用它購(gòu)物車(chē)放進(jìn)Session是多數(shù)課設(shè)代碼的默認(rèn)做法也是答辯時(shí)最容易被評(píng)委點(diǎn)名的點(diǎn)。Session購(gòu)物車(chē)的好處是零額外表、代碼量小、用戶未登錄也能裝東西壞處是服務(wù)端內(nèi)存占用隨在線用戶數(shù)線性增長(zhǎng)用戶關(guān)閉瀏覽器購(gòu)物車(chē)數(shù)據(jù)就丟而且分布式環(huán)境下Session同步是個(gè)大麻煩。正規(guī)互聯(lián)網(wǎng)系統(tǒng)的做法是購(gòu)物車(chē)數(shù)據(jù)落庫(kù)關(guān)聯(lián)用戶ID保存成購(gòu)物車(chē)表或直接放Redis。但課設(shè)系統(tǒng)沒(méi)有并發(fā)量也沒(méi)有多臺(tái)服務(wù)器我一般建議折中購(gòu)物車(chē)表落庫(kù)同時(shí)用Session做會(huì)話保持。下面是一個(gè)簡(jiǎn)化的購(gòu)物車(chē)Service方法public boolean addToCart(int userId, int skuId, int quantity, HttpSession session) { try (Connection conn DBHelper.getConnection()) { String checkStock SELECT stock FROM product_sku WHERE id?; PreparedStatement ps1 conn.prepareStatement(checkStock); ps1.setInt(1, skuId); ResultSet rs ps1.executeQuery(); if (rs.next() rs.getInt(stock) quantity) { return false; // 庫(kù)存不足提示用戶 } String sql INSERT INTO cart (user_id, sku_id, quantity) VALUES (?,?,?) ON DUPLICATE KEY UPDATE quantityquantity?; PreparedStatement ps conn.prepareStatement(sql); ps.setInt(1, userId); ps.setInt(2, skuId); ps.setInt(3, quantity); ps.setInt(4, quantity); ps.executeUpdate(); // 同步刷新Session中的購(gòu)物車(chē)數(shù)量用于頁(yè)面角標(biāo) CartService service new CartService(); session.setAttribute(cartCount, service.getCartCount(userId)); return true; } catch (SQLException e) { e.printStackTrace(); return false; } }ON DUPLICATE KEY UPDATE依賴(lài)cart表里(user_id, sku_id)的唯一約束這個(gè)寫(xiě)法比先查一次是否存在、再?zèng)Q定update還是insert要少一次數(shù)據(jù)庫(kù)交互。另一個(gè)細(xì)節(jié)是方法最后手動(dòng)刷新session中的cartCount如果不刷新用戶添加商品后頁(yè)面角標(biāo)還是舊數(shù)字得刷新整個(gè)頁(yè)面才變演示時(shí)會(huì)顯得很突兀。Session在這個(gè)方案里只承擔(dān)輕量級(jí)讀操作不再存整個(gè)購(gòu)物車(chē)的商品明細(xì)這樣既保留了頁(yè)面間快速取值的好處又避免了Session內(nèi)存無(wú)限制增長(zhǎng)。答辯被問(wèn)到“為什么不用純Session購(gòu)物車(chē)”時(shí)這個(gè)解釋能站住腳。3.2 庫(kù)存扣減的兩種寫(xiě)法先查再改與原子更新商品下單涉及最核心的數(shù)據(jù)一致性場(chǎng)景庫(kù)存扣減和訂單生成必須放在同一個(gè)事務(wù)里。新手最常見(jiàn)的寫(xiě)法是先查庫(kù)存判斷夠不夠夠就update然后insert訂單。這在單用戶測(cè)試時(shí)沒(méi)問(wèn)題但被評(píng)委追問(wèn)“兩個(gè)用戶同時(shí)買(mǎi)最后一件衣服怎么辦”時(shí)很容易卡殼。先查再改存在競(jìng)態(tài)條件線程A查到庫(kù)存為1線程B也查到庫(kù)存為1A扣減成0并下單B也扣減成0并下單最后超賣(mài)一件。解決辦法是在update語(yǔ)句里直接帶上庫(kù)存條件讓數(shù)據(jù)庫(kù)來(lái)做原子判斷String deductSql UPDATE product_sku SET stock stock - ? WHERE id ? AND stock ?; PreparedStatement ps conn.prepareStatement(deductSql); ps.setInt(1, quantity); ps.setInt(2, skuId); ps.setInt(3, quantity); int affected ps.executeUpdate(); if (affected 0) { conn.rollback(); return 庫(kù)存不足下單失敗; }affected 0只可能是兩種原因SKU不存在或者庫(kù)存不夠。因?yàn)锳ND stock ?條件不滿足時(shí)update不會(huì)匹配到任何行返回影響行數(shù)為0。這個(gè)寫(xiě)法不需要先select不需要加鎖還保證了并發(fā)安全是能在答辯現(xiàn)場(chǎng)直接講的亮點(diǎn)。下單時(shí)的整體事務(wù)控制建議用conn.setAutoCommit(false)包住三件事扣減SKU庫(kù)存、插入訂單主表、插入訂單明細(xì)表。任何一個(gè)環(huán)節(jié)失敗都回滾避免出現(xiàn)“訂單生成了但庫(kù)存沒(méi)扣”或“庫(kù)存扣了但訂單沒(méi)生成”的中間狀態(tài)。演示時(shí)不妨現(xiàn)場(chǎng)開(kāi)兩個(gè)瀏覽器窗口同時(shí)下單驗(yàn)證最終只有一單成功這是最有說(shuō)服力的測(cè)試場(chǎng)景。3.3 訂單狀態(tài)流轉(zhuǎn)從待付款到已收貨的5個(gè)狀態(tài)服裝商城有別于虛擬商品訂單生命周期相對(duì)長(zhǎng)狀態(tài)機(jī)設(shè)計(jì)得好答辯時(shí)能省很多口舌。我常用的是一個(gè)最小的五狀態(tài)模型狀態(tài)值狀態(tài)含義觸發(fā)動(dòng)作頁(yè)面可見(jiàn)范圍0待付款用戶提交訂單用戶可取消1待發(fā)貨用戶付款/模擬支付成功管理員可見(jiàn)2已發(fā)貨管理員點(diǎn)擊發(fā)貨用戶可見(jiàn)物流提醒3已收貨用戶點(diǎn)擊確認(rèn)收貨訂單完成4已取消用戶取消或超時(shí)未付款雙方可見(jiàn)這里建議不要把“已退款”放進(jìn)核心狀態(tài)課設(shè)階段做退款容易牽扯到支付網(wǎng)關(guān)模擬和權(quán)限邊界一句話帶過(guò)即可。狀態(tài)字段用TINYINT而不是VARCHAR是讓排序和條件查詢(xún)更快也更符合數(shù)據(jù)庫(kù)設(shè)計(jì)習(xí)慣。訂單狀態(tài)變更的代碼要有一個(gè)統(tǒng)一入口我一般會(huì)在OrderService里寫(xiě)一個(gè)updateStatus(orderNo, fromStatus, toStatus)方法必須傳入來(lái)源狀態(tài)做校驗(yàn)。比如“已發(fā)貨”的訂單不能被直接改成“已取消”否則業(yè)務(wù)邏輯就亂套了。狀態(tài)機(jī)的好處是讓代碼里的if/else變成可預(yù)期的流轉(zhuǎn)表評(píng)審翻代碼時(shí)一眼能看懂。4. 答辯和調(diào)試現(xiàn)場(chǎng)最常碰到的5個(gè)避坑細(xì)節(jié)4.1 “JSP是不是已經(jīng)過(guò)時(shí)”的應(yīng)對(duì)話術(shù)這是答辯時(shí)出現(xiàn)頻率最高的問(wèn)題幾乎每個(gè)做JSP課設(shè)的學(xué)生都會(huì)被問(wèn)到。最差的表現(xiàn)是慌張承認(rèn)“是的是的這個(gè)技術(shù)老了”。最好的做法是把問(wèn)題接住往課題目標(biāo)上引。我常用的回答結(jié)構(gòu)是先承認(rèn)技術(shù)迭代的事實(shí)再?gòu)?qiáng)調(diào)課程設(shè)計(jì)的考察重點(diǎn)??梢赃@么說(shuō)“JSP在后端渲染領(lǐng)域確實(shí)不如Spring BootVue這類(lèi)前后端分離方案新但我選它的原因是課設(shè)周期內(nèi)需要把數(shù)據(jù)庫(kù)設(shè)計(jì)、Servlet原理、會(huì)話管理、MVC分層這些核心知識(shí)點(diǎn)完整走一遍。JSP能直接展示Java對(duì)象在頁(yè)面上的渲染過(guò)程比前后端分離更容易講清楚請(qǐng)求-處理-響應(yīng)的完整鏈路?!边@個(gè)回答既沒(méi)有回避問(wèn)題又把評(píng)委的關(guān)注點(diǎn)從“技術(shù)新舊”拉回“你學(xué)到了什么”。4.2 中文亂碼從頁(yè)面到數(shù)據(jù)庫(kù)的3層排查中文亂碼在JSP系統(tǒng)里幾乎是必現(xiàn)問(wèn)題而且經(jīng)常是頁(yè)面顯示正常寫(xiě)入數(shù)據(jù)庫(kù)變成問(wèn)號(hào)。排查要按三層進(jìn)行JSP頁(yè)面編碼、Servlet請(qǐng)求編碼、數(shù)據(jù)庫(kù)連接編碼。第一層在JSP文件頂部加% page pageEncodingUTF-8 contentTypetext/html; charsetUTF-8 %第二層在Servlet里對(duì)POST請(qǐng)求加request.setCharacterEncoding(UTF-8)第三層就是前面DBHelper里URL上的useUnicodetruecharacterEncodingutf8。MySQL表本身也要確認(rèn)是utf8建庫(kù)時(shí)指定DEFAULT CHARSETutf8mb4。utf8mb4比utf8多支持emoji和生僻字JSP頁(yè)面里有些測(cè)試數(shù)據(jù)帶著特殊符號(hào)用utf8mb4能少一次返工。一個(gè)血淚經(jīng)驗(yàn)是request.setCharacterEncoding(UTF-8)必須放在第一個(gè)讀取請(qǐng)求參數(shù)的語(yǔ)句之前否則已經(jīng)按默認(rèn)編碼解析的參數(shù)無(wú)法被重新解碼放進(jìn)數(shù)據(jù)庫(kù)照樣是亂碼。這個(gè)位置問(wèn)題非??游乙?jiàn)過(guò)有人排查兩個(gè)晚上最后只是把這一行挪到getParameter前面就解決了。4.3 JSP圖片如何對(duì)坐標(biāo)定位商品主圖與管理員上傳的預(yù)覽問(wèn)題“JSP圖片如何對(duì)坐標(biāo)定位”是服裝商城頁(yè)面里很實(shí)際的需求最常見(jiàn)的是商品列表頁(yè)圖片和文字對(duì)齊、詳情頁(yè)多圖輪播時(shí)的定位。JSP本身是個(gè)模板不負(fù)責(zé)圖片處理圖片路徑的解析才是核心問(wèn)題。我一般把商品圖片上傳到項(xiàng)目的webapp/upload/目錄數(shù)據(jù)庫(kù)里只存相對(duì)路徑upload/xxx.jpg頁(yè)面用下面這種方式輸出img src${pageContext.request.contextPath}/upload/${product.mainImage} stylewidth:120px;height:160px;object-fit:cover;這個(gè)寫(xiě)法解決了兩個(gè)經(jīng)典問(wèn)題。${pageContext.request.contextPath}自動(dòng)拼上項(xiàng)目上下文路徑避免圖片在部署到不同路徑時(shí)404object-fit:cover讓不同比例的服裝圖片在固定尺寸容器內(nèi)按比例裁剪居中而不是被拉伸變形。圖片坐標(biāo)定位出現(xiàn)偏差時(shí)先檢查父容器的position屬性再檢查圖片自身的display屬性。服裝商品圖通常是豎長(zhǎng)條如果容器高度沒(méi)設(shè)固定值多張圖會(huì)互相擠視覺(jué)上就像坐標(biāo)跑了。4.4 刷新頁(yè)面后購(gòu)物車(chē)丟了的排查“JSP頁(yè)面讓加載完后刷新一次”這個(gè)奇怪需求往往是購(gòu)物車(chē)丟失問(wèn)題被繞過(guò)式的解決辦法。刷新就丟購(gòu)物車(chē)根因是Session中對(duì)象丟失通常是兩個(gè)原因Cookie失效導(dǎo)致Session ID變更或者頁(yè)面跳轉(zhuǎn)時(shí)URL里沒(méi)帶jsessionid。排查步驟很固定先看瀏覽器開(kāi)發(fā)者工具里的Cookie確認(rèn)JSESSIONID是否存在且沒(méi)有過(guò)期再檢查web.xml里的session超時(shí)時(shí)間session-timeout單位是分鐘默認(rèn)30分鐘演示時(shí)如果長(zhǎng)時(shí)間停在頁(yè)面不動(dòng)再提交購(gòu)物車(chē)超時(shí)后Session會(huì)被回收最后看跳轉(zhuǎn)方式如果用了response.sendRedirect跳轉(zhuǎn)到外部地址新的瀏覽器上下文可能丟失原會(huì)話改成request.getRequestDispatcher().forward()就能保住Session。另外要注意服務(wù)器重啟會(huì)清空內(nèi)存中的Session課設(shè)演示前一定要先重啟Tomcat再走流程否則會(huì)出現(xiàn)“明明代碼沒(méi)問(wèn)題但購(gòu)物車(chē)突然空了”的假象。4.5 前后端分離項(xiàng)目風(fēng)格的頁(yè)面引入餓了么element圖標(biāo)在JSP里的用法不少同學(xué)拿了個(gè)前后端分離的前端模板想直接套到JSP里結(jié)果發(fā)現(xiàn)Vue組件和Element UI圖標(biāo)要么渲染不出來(lái)要么和JSP標(biāo)簽沖突?!梆I了么elment圖標(biāo)前端jsp”這種搜索詞背后就是想在新式UI和JSP之間找一條可行路徑。我試過(guò)的可行做法是不用Vue的單文件組件直接用Element UI的圖標(biāo)字體文件。把iconfont.css和字體文件放進(jìn)webapp/css和webapp/fonts在JSP頁(yè)面頂部link進(jìn)來(lái)然后直接用i classel-icon-shopping-cart-full/i這類(lèi)圖標(biāo)類(lèi)名。這類(lèi)圖標(biāo)本質(zhì)是字體不依賴(lài)Vue運(yùn)行時(shí)JSP里完全能用。需要注意兩點(diǎn)一是檢查css里font-face的路徑是否正確默認(rèn)相對(duì)路徑可能會(huì)指向項(xiàng)目根目錄以外的位置改成${pageContext.request.contextPath}/fonts/二是不要在同一個(gè)JSP頁(yè)面混用Vue的{{}}插值語(yǔ)法和JSTL的${}取值兩者都用了花括號(hào)會(huì)互相干擾實(shí)測(cè)時(shí)最容易在這里翻車(chē)。5. 把答辯PPT章節(jié)映射到系統(tǒng)模塊一份能閉環(huán)講述的講稿結(jié)構(gòu)5.1 課題背景與意義怎么寫(xiě)才不空答辯PPT的第一部分“選題背景”通常是重災(zāi)區(qū)一眼看過(guò)去全是“隨著電子商務(wù)的發(fā)展人們對(duì)網(wǎng)上購(gòu)物的需求日益增長(zhǎng)”。這句話沒(méi)有錯(cuò)但也沒(méi)有信息量。更好的寫(xiě)法是把背景拆成兩個(gè)層面行業(yè)背景一句話帶過(guò)然后馬上落到問(wèn)題導(dǎo)向。比如這樣寫(xiě)“傳統(tǒng)服裝零售受時(shí)間和門(mén)店覆蓋限制線上商城成為服裝品牌拓展銷(xiāo)售渠道的重要方式。本課題從中小型服裝商戶的實(shí)際需求出發(fā)設(shè)計(jì)并實(shí)現(xiàn)一個(gè)支持商品展示、購(gòu)物車(chē)、訂單流轉(zhuǎn)的B2C商城交易管理系統(tǒng)重點(diǎn)解決服裝多規(guī)格SKU管理與訂單狀態(tài)可視化問(wèn)題?!边@一段包含了行業(yè)背景、課題目的、技術(shù)難點(diǎn)既具體又能順勢(shì)引出后面的模塊設(shè)計(jì)。5.2 需求分析到功能模塊的映射表評(píng)委看PPT時(shí)會(huì)盯著需求分析和功能模塊是否對(duì)得上。需求說(shuō)有用戶注冊(cè)、商品瀏覽、購(gòu)物車(chē)、訂單管理、管理員后臺(tái)后面就必須出現(xiàn)對(duì)應(yīng)的頁(yè)面截圖和核心代碼不能前面說(shuō)了五條后面只做出來(lái)三條。建議用一張表格把功能需求、用例主角、頁(yè)面位置串起來(lái)功能編號(hào)功能名稱(chēng)角色對(duì)應(yīng)頁(yè)面/接口F01用戶注冊(cè)登錄普通用戶register.jsp / login.jspF02商品分類(lèi)瀏覽普通用戶index.jsp / category_list.jspF03服裝詳情與SKU選擇普通用戶product_detail.jspF04購(gòu)物車(chē)增刪改查普通用戶cart.jsp / CartServletF05提交訂單并模擬支付普通用戶order_confirm.jsp / OrderServletF06后臺(tái)商品管理管理員admin_product_list.jspF07后臺(tái)訂單發(fā)貨管理員admin_order_list.jsp這張表最大的用途不是給評(píng)委看而是給自己列出開(kāi)發(fā)檢查清單。每一行都有頁(yè)面和Servlet對(duì)應(yīng)開(kāi)發(fā)時(shí)照著做答辯時(shí)照著講不會(huì)出現(xiàn)缺項(xiàng)。5.3 測(cè)試用例與演示數(shù)據(jù)的設(shè)計(jì)系統(tǒng)測(cè)試部分不能只寫(xiě)“經(jīng)測(cè)試系統(tǒng)運(yùn)行穩(wěn)定”評(píng)委追問(wèn)第一個(gè)測(cè)試用例就能看出有沒(méi)有真正跑過(guò)。建議設(shè)計(jì)5到8條有業(yè)務(wù)含義的測(cè)試用例其中一定要覆蓋正常流程、邊界條件和異常分支。給出一組可以直接抄進(jìn)PPT的測(cè)試用例表用例編號(hào)測(cè)試場(chǎng)景操作步驟預(yù)期結(jié)果T01用戶注冊(cè)提交空用戶名提示用戶名不能為空T02登錄失敗輸入錯(cuò)誤密碼提示密碼錯(cuò)誤并保留用戶名T03無(wú)庫(kù)存商品下單將庫(kù)存為0的商品加入購(gòu)物車(chē)并結(jié)算提示庫(kù)存不足禁止下單T04超賣(mài)場(chǎng)景兩個(gè)會(huì)話同時(shí)購(gòu)買(mǎi)同一SKU最后1件僅一個(gè)會(huì)話成功生成訂單T05訂單狀態(tài)流轉(zhuǎn)付款-發(fā)貨-確認(rèn)收貨狀態(tài)依次從0變?yōu)?T06商品圖片預(yù)覽上傳同一文件名圖片兩次第二次文件名被重命名不覆蓋原圖T03和T04就是前面第3章庫(kù)存扣減邏輯的對(duì)應(yīng)測(cè)試能夠直接證明“為什么用AND stock ?原子更新”。演示時(shí)跑通T04比任何口頭解釋都有說(shuō)服力。5.4 答辯現(xiàn)場(chǎng)演示的數(shù)據(jù)準(zhǔn)備與順序建議演示時(shí)最怕臨時(shí)造數(shù)據(jù)穿一件圖片沒(méi)上傳、庫(kù)存配錯(cuò)、價(jià)格對(duì)不上的衣服點(diǎn)進(jìn)購(gòu)物車(chē)。我建議正式答辯前固定一套演示數(shù)據(jù)數(shù)量控制在6到8件服裝覆蓋兩個(gè)分類(lèi)、不同顏色尺碼、一件庫(kù)存為0的商品、一件庫(kù)存為1的商品。這樣既能展示正常購(gòu)買(mǎi)流程又能隨時(shí)演示庫(kù)存不足攔截。演示順序按業(yè)務(wù)主鏈路走游客瀏覽首頁(yè)分類(lèi)商品注冊(cè)新用戶點(diǎn)開(kāi)某件衣服選顏色尺碼加入購(gòu)物車(chē)去結(jié)算模擬支付管理員登錄后臺(tái)看到新訂單點(diǎn)擊發(fā)貨用戶端確認(rèn)收貨流程閉環(huán)。走完這套動(dòng)作之后再補(bǔ)一個(gè)管理員新增商品的展示把錄入、圖片上傳、上下架也覆蓋到。最后如果時(shí)間允許再現(xiàn)場(chǎng)演示庫(kù)存超賣(mài)攔截這個(gè)場(chǎng)景最容易給評(píng)委留下“系統(tǒng)考慮過(guò)并發(fā)”的好印象。6. 用一份演示自檢清單給系統(tǒng)上最后一層保險(xiǎn)答辯前夜我和所有做課設(shè)的同學(xué)一樣都會(huì)對(duì)著系統(tǒng)把整個(gè)流程走一遍。這里有一套我固定使用的自檢清單比臨時(shí)改代碼管用得多。第一步檢查數(shù)據(jù)。登錄后臺(tái)看商品列表確認(rèn)每件衣服都有圖片、價(jià)格和有效庫(kù)存清空購(gòu)物車(chē)把演示用的用戶賬號(hào)恢復(fù)到初始狀態(tài)訂單列表里不能殘留臟數(shù)據(jù)否則評(píng)委翻歷史訂單會(huì)看到“李四測(cè)試”“111111”這類(lèi)隨手寫(xiě)的記錄。第二步檢查環(huán)境。Tomcat啟動(dòng)后先訪問(wèn)一次首頁(yè)確認(rèn)Session能正常創(chuàng)建數(shù)據(jù)庫(kù)服務(wù)要確認(rèn)沒(méi)有自動(dòng)關(guān)閉演示用的瀏覽器用無(wú)痕窗口避免舊Cookie污染登錄狀態(tài)。如果現(xiàn)場(chǎng)網(wǎng)絡(luò)不穩(wěn)定數(shù)據(jù)庫(kù)連接池的URL里配的serverTimezoneAsia/Shanghai在部分老版本MySQL驅(qū)動(dòng)下會(huì)拋異常建議提前用Tomcat的lib目錄校驗(yàn)驅(qū)動(dòng)版本。第三步檢查頁(yè)面。重點(diǎn)看三個(gè)容易出洋相的地方第一是商品詳情頁(yè)圖片是否按預(yù)期比例顯示第二是點(diǎn)擊“加入購(gòu)物車(chē)”后右上角角標(biāo)是否同步刷新第三是訂單提交后頁(yè)面是否跳轉(zhuǎn)到訂單詳情而不是空白頁(yè)。這三個(gè)位置都是“JSP圖片如何對(duì)坐標(biāo)定位”和“頁(yè)面加載完后刷新一次”這種奇怪需求背后的真實(shí)痛點(diǎn)問(wèn)題不大但很毀演示效果。最后一步是降級(jí)預(yù)案。把核心頁(yè)面做成兩個(gè)入口一個(gè)從主頁(yè)鏈接進(jìn)入一個(gè)直接輸入U(xiǎn)RL進(jìn)入。萬(wàn)一主頁(yè)某個(gè)分類(lèi)查詢(xún)報(bào)錯(cuò)直接改URL進(jìn)商品頁(yè)演示流程可以繼續(xù)走。這套系統(tǒng)做到最后真正拉開(kāi)差距的不是技術(shù)棧新不新而是有沒(méi)有把邊界處理干凈。把腳勤快一點(diǎn)多跑幾遍完整場(chǎng)景比答辯前翻書(shū)有用得多。希望這些從真實(shí)項(xiàng)目里踩出來(lái)的經(jīng)驗(yàn)?zāi)軒湍闵僮邘锥螐澛贰1疚倪€有配套的精品資源點(diǎn)擊獲取