
簡介本資源是一套完整的健身房會員管理系統設計源碼面向計算機專業本科生、Web開發初學者及中小型健身場館信息化建設需求者解決會員信息管理、課程預約、教練分配與財務統計等核心運營問題。壓縮包共187個文件含65個Java后端邏輯文件、31個HTML前端頁面、13個JavaScript交互腳本、12個CSS樣式文件、12個XML配置/數據文件以及23個JPEG/JPG圖片素材和1個SQL數據庫初始化腳本整體大小為6.93MB結構清晰、模塊完整便于學習MVC分層開發與前后端協同實踐。已有418人學習下載適合用于課程設計、畢業設計參考或輕量級SaaS系統二次開發。源碼包含完整項目結構含src、resources、webapp目錄、Git版本控制配置.gitignore、Maven構建文件pom.xml及詳細readme說明還預置了Bootstrap、Layui、Font Awesome等主流前端庫的CSS與JS資源開箱即用顯著降低環境搭建門檻。1. 這不是「學生作業演示」而是一套可落地的健身房會員管理前端骨架你打開一個.html文件看到meta charsetutf-8和meta nameviewport contentwidthdevice-width, initial-scale1.0第一反應可能是“又一個靜態頁面”。但 GYMManage 的 31 個 HTML 文件里藏著 65 個 Java 類協同驅動的真實業務流會員注冊要校驗手機號唯一性、私教課程預約需實時鎖定教練排班、繳費記錄必須與財務模塊原子級同步。它沒用 Spring Boot 自動裝配卻用web.xmlServletJDBC實現了完整的 MVC 分層——這不是為交作業寫的“能跑就行”而是用最樸素的技術棧把「健身房日常運營」拆解成可調試、可替換、可監控的 187 個文件單元。適合剛脫離課本的應屆生快速理解 Web 系統真實結構也適合中小健身房技術負責人評估自建系統的最小可行路徑不依賴云服務、不強綁定框架、所有邏輯都攤開在 src 目錄下連pom.xml里每個dependency的版本號都寫得清清楚楚。2. 前端資源組織邏輯從 12 個 CSS 文件看多層樣式治理策略GYMManage 的 CSS 不是簡單堆疊而是按職責分層收斂。12 個 CSS 文件中bootstrap.min.css提供柵格與基礎組件layui.css負責表單與彈窗交互layer.css專管提示層 z-index 與動畫privateCourse.css則只處理私教課預約頁的特定布局。這種拆分不是隨意命名而是對應不同開發階段的協作邊界UI 設計師改styles.css全局配色與字體前端工程師調chat.css消息氣泡與滾動錨點后端開發者只需關注login.css中.form-control:focus的邊框顏色是否與 Java 驗證錯誤提示一致。2.1 樣式加載順序決定渲染優先級關鍵在于link標簽的引入順序。在index.html頭部實際加載順序為link relstylesheet hrefcss/bootstrap.min.css link relstylesheet hrefcss/layui.css link relstylesheet hrefcss/layer.css link relstylesheet hrefcss/font-awesome.css link relstylesheet hrefcss/login.css link relstylesheet hrefcss/privateCourse.css link relstylesheet hrefcss/styles.css注意styles.css放在最后意味著它擁有最高特異性權重。例如當bootstrap.min.css定義了.btn { padding: 6px 12px; }而styles.css中寫.btn { padding: 8px 16px !important; }后者才會生效。但項目中幾乎沒用!important而是靠選擇器精度控制——#courseList .item-card:hover比.item-card:hover優先級更高。2.2 三行模式 CSS 文件的實戰價值所謂“三行模式”指每個 CSS 文件嚴格遵循① 重置與基礎變量如:root { --primary-color: #3a86ff; }② 組件級樣式.member-card { border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,.1); }③ 狀態修飾類.member-card--active { border-left: 4px solid var(--primary-color); }以chat.css為例其核心結構如下/* ① 基礎變量 */ :root { --chat-bg: #f8f9fa; --msg-sent: #06d6a0; --msg-received: #118ab2; } /* ② 組件樣式 */ .chat-container { height: calc(100vh - 120px); overflow-y: auto; padding: 16px; } .chat-bubble { max-width: 70%; padding: 10px 14px; border-radius: 18px; margin-bottom: 12px; font-size: 14px; line-height: 1.4; } /* ③ 狀態類 */ .chat-bubble--sent { background-color: var(--msg-sent); color: white; margin-left: auto; border-bottom-right-radius: 4px; } .chat-bubble--received { background-color: var(--chat-bg); color: #333; margin-right: auto; border-bottom-left-radius: 4px; }這種結構讓團隊協作時無需全局搜索修改氣泡顏色只需改:root變量新增“已讀”狀態只需加.chat-bubble--read類調試時用瀏覽器開發者工具定位樣式來源能直接跳轉到對應 CSS 文件的第 2 行或第 15 行。2.3 CSS 鼠標移入事件的業務耦合設計privateCourse.css中的懸停效果不是炫技而是觸發業務邏輯的視覺開關.coach-item:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,.15); cursor: pointer; } .coach-item:hover .coach-info { opacity: 1; visibility: visible; }配合 JavaScript當用戶 hover 教練頭像時會動態加載該教練的可預約時段通過fetch(/api/coach/available?coachId101)并顯示在.coach-info區域。這里的關鍵參數是visibility: visible而非display: block——因為visibility不觸發重排reflow僅影響重繪repaint在頻繁 hover 場景下性能更優。若換成display每次懸停都會導致瀏覽器重新計算布局滾動卡頓明顯。3. 后端 Java 層65 個源文件如何構建無框架的穩健服務鏈GYMManage 的 Java 代碼沒用 Spring 的Controller或Service注解而是用傳統 Servlet JDBC 自定義 DAO 模式實現。65 個.java文件中com.gymmanage.servlet.*包含 23 個 Servletcom.gymmanage.dao.*包含 18 個 DAOcom.gymmanage.entity.*包含 12 個實體類其余為工具類與配置類。這種結構看似“過時”實則規避了框架黑盒——當會員登錄失敗時你能直接在LoginServlet.java第 47 行看到 SQL 查詢語句SELECT * FROM member WHERE phone ? AND password ?也能在DBUtil.java第 33 行確認連接池最大連接數設為 20。3.1 Servlet 請求路由的顯式映射機制web.xml中的 servlet-mapping 是整個系統流量入口servlet servlet-nameLoginServlet/servlet-name servlet-classcom.gymmanage.servlet.LoginServlet/servlet-class /servlet servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login/url-pattern /servlet-mapping servlet servlet-nameMemberListServlet/servlet-name servlet-classcom.gymmanage.servlet.MemberListServlet/servlet-class /servlet servlet-mapping servlet-nameMemberListServlet/servlet-name url-pattern/admin/member/list/url-pattern /servlet-mapping提示URL 路徑/admin/member/list對應MemberListServlet而非/member/list。這意味著權限校驗必須在 Servlet 內部完成而不是依賴框架攔截器。MemberListServlet的doGet()方法開頭必有if (!ADMIN.equals(request.getSession().getAttribute(role))) { response.sendRedirect(error.jsp); return; }——這種硬編碼校驗雖不優雅但排查權限問題時你永遠知道該去哪個.java文件加日志。3.2 JDBC 連接池的手動管理實踐DBUtil.java是數據庫操作的中樞其核心方法getConnection()如下public static Connection getConnection() throws SQLException { if (dataSource null) { // 初始化 HikariCP 連接池 HikariConfig config new HikariConfig(); config.setJdbcUrl(jdbc:mysql://localhost:3306/gymdb?useSSLfalseserverTimezoneAsia/Shanghai); config.setUsername(root); config.setPassword(123456); config.setMaximumPoolSize(20); // 最大連接數 config.setMinimumIdle(5); // 最小空閑連接 config.setConnectionTimeout(30000); // 連接超時 30 秒 config.setIdleTimeout(600000); // 空閑連接存活 10 分鐘 config.setMaxLifetime(1800000); // 連接最大生命周期 30 分鐘 dataSource new HikariDataSource(config); } return dataSource.getConnection(); }參數說明maximumPoolSize20應對健身房高峰期并發預約實測 15 人同時搶私教課時平均響應時間 800msminimumIdle5保證空閑時總有 5 個連接就緒避免首次請求等待連接創建connectionTimeout30000若 30 秒內無法獲取連接Servlet 拋出SQLException并跳轉至error.jsp3.3 實體類與數據庫表的精準映射Member.java實體類字段與member表完全對齊且包含業務約束public class Member { private int id; private String phone; // VARCHAR(11)帶正則校驗 ^1[3-9]\\d{9}$ private String name; // VARCHAR(50) private Date joinDate; // DATE格式化為 yyyy-MM-dd private BigDecimal balance; // DECIMAL(10,2)余額精確到分 private String status; // ENUM(ACTIVE,INACTIVE,LOCKED) // getter/setter 省略 public boolean canBookPrivateCourse() { return ACTIVE.equals(status) balance.compareTo(BigDecimal.ZERO) 0; } }這個canBookPrivateCourse()方法是典型業務邏輯下沉前端點擊“預約私教課”按鈕前JavaScript 會先調用/api/member/check?memberId1001接口后端返回 JSON{ canBook: true, reason: 余額充足 }。若余額不足前端直接禁用按鈕并提示“請先充值”而非提交后才報錯——減少無效請求提升用戶體驗。4. 前后端協同13 個 JavaScript 文件如何驅動 31 個 HTML 頁面GYMManage 的 JavaScript 不是零散腳本而是按頁面功能聚合成模塊。13 個.js文件中main.js是全局入口member.js處理會員中心邏輯course.js管理課程預約chart.js渲染財務統計圖表。每個 JS 文件都遵循“數據驅動視圖”原則DOM 操作只發生在數據變更后且通過>function validateLogin() { const input document.getElementById(loginInput).value.trim(); const isPhone /^1[3-9]\d{9}$/.test(input); const isEmail /^[^\s][^\s]\.[^\s]$/.test(input); if (!isPhone !isEmail) { showTip(請輸入正確的手機號或郵箱, error); return false; } // 動態設置后端接收參數名 const paramKey isPhone ? phone : email; document.getElementById(loginForm).action /login?${paramKey}${encodeURIComponent(input)}; return true; }邏輯說明先用正則判斷輸入類型避免后端重復校驗根據類型動態拼接actionURL使同一個 Servlet 能區分處理兩種登錄方式showTip()函數封裝了 DOM 操作避免在 HTML 中寫內聯onclickalert(...)4.2 AJAX 請求的統一錯誤處理utils.js提供標準化 fetch 封裝function apiRequest(url, options {}) { const defaultOptions { method: GET, headers: { Content-Type: application/json, X-Requested-With: XMLHttpRequest }, credentials: include // 保持 session }; return fetch(url, { ...defaultOptions, ...options }) .then(response { if (!response.ok) { throw new Error(HTTP ${response.status}: ${response.statusText}); } return response.json(); }) .catch(err { console.error(API 請求失敗:, url, err); // 統一跳轉錯誤頁 if (err.message.includes(401)) { window.location.href /login.jsp; } else if (err.message.includes(403)) { alert(權限不足請聯系管理員); } else { alert(網絡異常請稍后重試); } throw err; }); }參數說明credentials: include確保 Cookie含 JSESSIONID隨請求發送維持登錄態X-Requested-With頭用于后端識別 AJAX 請求可據此返回 JSON 而非重定向錯誤分類處理401 跳登錄頁403 彈權限提示其他統一網絡異常提示4.3 動態表格渲染與分頁控制member.js中的會員列表加載function loadMemberList(page 1, size 10) { apiRequest(/admin/member/list?page${page}size${size}) .then(data { const tbody document.querySelector(#memberTable tbody); tbody.innerHTML data.list.map(m tr td${m.id}/td td${m.name}/td td${m.phone}/td td${formatDate(m.joinDate)}/td td¥${m.balance.toFixed(2)}/td td button onclickeditMember(${m.id}) classbtn btn-sm btn-primary編輯/button button onclickdeleteMember(${m.id}) classbtn btn-sm btn-danger刪除/button /td /tr ).join(); // 渲染分頁控件 renderPagination(data.total, page, size); }); } function renderPagination(total, currentPage, pageSize) { const totalPages Math.ceil(total / pageSize); const pagination document.getElementById(pagination); let html ul classpagination; if (currentPage 1) { html lia href# onclickloadMemberList(${currentPage-1})laquo;/a/li; } for (let i Math.max(1, currentPage-2); i Math.min(totalPages, currentPage2); i) { html li class${i currentPage ? active : }a href# onclickloadMemberList(${i})${i}/a/li; } if (currentPage totalPages) { html lia href# onclickloadMemberList(${currentPage1})raquo;/a/li; } html /ul; pagination.innerHTML html; }關鍵細節data.list是后端返回的 JSON 數組map()生成 HTML 字符串避免頻繁 DOM 操作分頁邏輯在前端計算頁碼范圍當前頁 ±2后端只負責返回total總數和當前頁數據onclickloadMemberList(${i})使用字符串拼接而非addEventListener因表格動態生成事件委托成本高于直接綁定5. 數據庫與部署從 GYMSys.sql 到本地 Tomcat 的完整閉環GYMSys.sql不是簡單的建表語句而是包含初始化數據的生產就緒腳本。它定義了 8 張核心表member會員、coach教練、course課程、schedule排班、booking預約、payment支付、admin管理員、log操作日志。每張表都有符合健身房業務的約束比如booking表的聯合唯一索引確保同一教練同一時段不能被重復預約。5.1 關鍵 SQL 約束解析GYMSys.sql中booking表創建語句CREATE TABLE booking ( id INT PRIMARY KEY AUTO_INCREMENT, member_id INT NOT NULL, coach_id INT NOT NULL, course_id INT NOT NULL, schedule_date DATE NOT NULL, schedule_time TIME NOT NULL, status ENUM(PENDING,CONFIRMED,CANCELLED) DEFAULT PENDING, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_coach_time (coach_id, schedule_date, schedule_time), FOREIGN KEY (member_id) REFERENCES member(id) ON DELETE CASCADE, FOREIGN KEY (coach_id) REFERENCES coach(id) ON DELETE RESTRICT, FOREIGN KEY (course_id) REFERENCES course(id) ON DELETE RESTRICT );約束說明UNIQUE KEY uk_coach_time強制教練在某日某時段只能有一個預約防止超賣ON DELETE CASCADE刪除會員時自動清理其所有預約記錄ON DELETE RESTRICT禁止刪除正在授課的教練避免數據不一致5.2 Tomcat 部署的三項必檢配置將項目部署到本地 Tomcat 需確認三個位置context.xml配置數據源conf/context.xmlResource namejdbc/GymDB authContainer typejavax.sql.DataSource factoryorg.apache.tomcat.jdbc.pool.DataSourceFactory driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/gymdb?useSSLfalseamp;serverTimezoneAsia/Shanghai usernameroot password123456 maxActive20 minIdle5 validationQuerySELECT 1/web.xml中的資源引用WEB-INF/web.xmlresource-ref descriptionGym Database/description res-ref-namejdbc/GymDB/res-ref-name res-typejavax.sql.DataSource/res-type res-authContainer/res-auth /resource-refJava 代碼中的 JNDI 查找DBUtil.javaContext ctx new InitialContext(); DataSource ds (DataSource) ctx.lookup(java:comp/env/jdbc/GymDB); return ds.getConnection();提示若啟動報錯Name [jdbc/GymDB] is not bound in this Context檢查context.xml是否放在conf/目錄而非webapp/META-INF/若報錯Access denied for user確認 MySQL 用戶root有gymdb庫的全部權限GRANT ALL PRIVILEGES ON gymdb.* TO rootlocalhost; FLUSH PRIVILEGES;5.3 一鍵驗證部署成功的三步法部署完成后執行以下命令驗證各層連通性# 1. 檢查數據庫連接在 Tomcat 啟動后執行 curl -s http://localhost:8080/GYMManage/api/member/count | jq .count # 預期輸出{count:0} 或 {count:12}非 500 錯誤即數據庫通 # 2. 驗證靜態資源CSS/JS 加載 curl -s -I http://localhost:8080/GYMManage/css/bootstrap.min.css | grep 200 OK # 必須返回 HTTP/1.1 200 OK否則檢查 webapps/GYMManage/css/ 路徑 # 3. 測試登錄接口模擬表單提交 curl -s -X POST http://localhost:8080/GYMManage/login \ -H Content-Type: application/x-www-form-urlencoded \ -d phone13800138000 -d password123456 \ -c cookies.txt | grep Location: # 若返回 Location: /admin/index.jsp說明登錄成功并重定向參數說明jq .count解析 JSON 提取count字段避免肉眼識別-I參數只獲取響應頭比下載完整 CSS 更快驗證路徑-c cookies.txt保存 Cookie后續請求可用-b cookies.txt攜帶會話部署驗證通過后打開http://localhost:8080/GYMManage/login.jsp即可進入系統。此時你面對的不是一個 Demo而是一個隨時可接入真實健身房 POS 機、對接微信支付、導出 Excel 報表的生產級骨架——所有能力都藏在那 187 個文件里等你按需激活。本文還有配套的精品資源點擊獲取