1. 为什么我要做这个台球厅计费系统:课时费背后的真实痛点
做这个项目之前,我泡过好几个台球厅。不是为了打球,是真去观察他们的收银台和记单流程。大多数中小型台球厅至今仍停留在"手写开台单+人工掐秒表+下班统一算账"的阶段,老板最怕的不是客人少,而是账对不上、漏单、跑单以及员工交接班时的糊涂账。
基于JavaWeb的台球厅管理收费系统,本质上就是把这套手工流程用Web应用重新实现一遍。它要解决的核心问题,并不是"把信息存进电脑"这么简单,而是三个字:计费准。台球厅的收入模型非常特殊——收入按分钟产生,开台即计费,换台要迁移计时,结账要按峰谷时段切换单价。一旦计时口径出错,差个几分钟,一个月累积下来就是几千块的损失。
这个项目适合的人群也很明确:正在做JavaWeb课程设计或毕业设计的计算机专业学生,想练手完整Web项目(前端+后端+数据库+部署)的初级开发,以及确实想给自己球房搞一套内部管理工具的个体经营者。技术栈是经典的Servlet+JSP+MySQL,没有过度封装,每个环节都能看清来龙去脉,对于学习阶段来说这反而是优点。
我最终实现的系统包含桌台管理、开台与换台、自动计费、会员储值、商品销售、订单结算、营业统计和权限登录八个核心模块。整套代码量不大,但把台球厅运营中最容易出问题的业务节点都覆盖了。下面我按从需求梳理到功能落地的顺序,把整个设计和实现过程完整拆解一遍。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 台球厅计费业务建模:为什么计费规则是系统的灵魂而非附属功能
2.1 时计费与阶梯计费的规则梳理
在写任何一行代码之前,第一件事是去现场搞清楚计费规则。我调研了本地四家球房,发现规则惊人地一致:非会员散客按小时计费或按分钟实时累计;会员享受折扣价,但折扣通常只针对台费,不打折商品;峰谷时段价格不同,一般19点到24点算黄金时段,价格上浮;超时未结账自动续费。真实场景比网上课程设计里的"固定单价"复杂得多。
所以我把计费设计成一个独立的规则引擎类,而不是把价格硬编码在JSP页面里。规则参数全部存数据库的system_config表:
| 参数项 | 示例值 | 说明 |
|---|---|---|
| normal_price | 20.00 | 普通时段每小时单价 |
| peak_price | 35.00 | 高峰时段每小时单价 |
| peak_start | 19:00 | 高峰开始时间 |
| peak_end | 00:00 | 高峰结束时间 |
| member_discount | 0.8 | 会员台费折扣率 |
| min_billing_unit | 30 | 最短计费分钟数(不足30分钟按30分钟) |
| billing_round | 1 | 超过最短时间后按多少分钟向上取整 |
计费逻辑的核心判断,是在任意时刻给定一个时间点,算出当前应该按哪个单价走。这个判断用TimeUtils工具类实现,传入LocalTime,和配置表中的峰谷区间比对,返回当前单价。坑点在于跨天——高峰结束时间是00:00,意味着23:50开台,前10分钟是高峰价,到00:00后自动切到普通价。这个跨天切换如果不在设计阶段考虑清楚,后期改代码非常痛苦。
真正让计费复杂的是超时自动续费。客人打完球不结账,台子空着但系统还在跑时间。我的处理方案是:后台定时任务每5分钟扫描一次所有启用状态的桌台,用当前时间减去开始计费时间得到已计费分钟数,然后和订单表里的预付费时长对比,超出的部分自动生成补缴记录。这样前台收银员只需按系统提示收款,不需要自己心算"超了多久"。
2.2 一桌多时段的价格归属算法
阶梯计费再加峰谷切换,会引出一个实际问题:一局球从20点半打到21点半,横跨了普通时段和高峰时段,怎么算钱?
两种方案摆在我面前。方案一是分段结算:把开场到结束的时间段按峰谷切分,分别累计分钟数,再乘以各自单价相加。方案二化整为零:以当前计费时刻的单价为准,整段时长统一按这个单价算,等下次续费或结账时再刷新单价。
方案一对客人公平,对系统复杂——每次结账都要遍历时段切分点,还要处理跨天和多次峰谷切换的边界。方案二符合大多数球房的实际操作——大多数球房本来就按"结账那一刻的单价"算,客人也基本认同。但方案二有个漏洞:客人如果跨峰谷长时间打球不结账,费用会失真。
最终我采用了折中复杂度可控的方案:按小时整段切换而非按分钟精确切分。具体规则是——按开始计费时刻锁定初始单价;每个整点重新判断当前是否跨时段,若跨时段则从跨入时刻起按新单价计费;计费时长按分钟累加,单价按"时长归属区间"分两段累计。这个逻辑放在BillingEngine.calculate()方法里,传入开台时间、当前时间和会员标识,返回总费用。
光这一段算法,我写了三个版本才稳定。第一版没有处理跨天,第二版处理了跨天但没处理"开台时间/当前时间/峰谷区间"三者叠加的多次切换。最终版本用一条时间线遍历法:把[开台时间, 当前时间]这个区间,按峰谷切换点切割成若干子区间,每个子区间内部单价恒定,累加即可。这也是这个项目里我最满意的一段代码。
2.3 换台与并台:最容易泄漏收入的业务流程
换台是我在需求调研阶段差点漏掉的业务。客人从普通区换到VIP区,台费单价变了,计时却必须无缝衔接。如果只是简单地把桌台A状态改为空闲、桌台B改为使用中,那么之前在A台的时间就全丢了,收银员只能手工重新估算——这就是收入泄漏点。
我的换台实现是操作订单而非操作桌台。订单表设计上,一个订单关联桌台ID,换台时新建一条桌台变更记录,同时把原订单的结束时间打上,费用按变更前的时间段结算到原台,再以原订单剩余预付费为基础,开一个新订单挂在目标桌台下。这样每一次换台都会产生一条可追溯的对账记录,下班汇总时能清楚看到"3号台换到5号台,金额无断裂"。
并台则更少用但必须支持——客人本来开了一个斯诺克台,后来说要换到旁边的大台一起打,两个台子的费用要合并。实现上我选择"先结旧单、再开新单"的策略:把旧台订单做强制结账,生成费用记录;然后新台以实际开台时间倒推补录开台记录;剩余预付费手动转入新订单。这个过程全程有操作日志,避免收银员私下操作。
3. 技术选型与项目搭建:JavaWeb项目在2023年到底该怎么起手
3.1 选型思路:不追新,但求稳定可控
技术选型这块我不打算标新立异。这个项目定位是实用型管理系统,面向的是课设、毕设以及小型球房落地,所以选型原则就三条:资料多、好调试、部署简单。
- 后端:Java 8 + Servlet 4.0 + JSP。这组合看起来老,但生态成熟,遇到问题随便一搜就有答案。更重要的是它能让你理解HTTP请求从Tomcat到Servlet再到JSP渲染的完整链路。用Spring Boot虽然开发快,但对学习Web本质来说反而是黑盒。
- 前端:JSP + JSTL + Bootstrap 5 + jQuery + Ajax。不做前后端分离——这个体量的项目分离架构只会增加部署成本和CORS之类的麻烦。Bootstrap 5负责界面,Ajax负责局部刷新,比如开台操作不需要整页跳转。
- 数据库:MySQL 8.0,InnoDB引擎,UTF-8mb4字符集。涉及金额的字段全部用DECIMAL(10,2)而不是float——float的精度问题在金额计算里是绝对不能碰的红线。
- 服务器:Tomcat 9.x。注意和Servlet版本的兼容性,Tomcat 9对应Servlet 4.0,够用且稳定。
IDE方面,我用的是IDEA 2023版本。新版的IDEA创建JavaWeb项目和以前不太一样,没有直接的"Java Enterprise"向导入口那么直观了,需要在Project Structure里手动添加Web模块和Artifact。这个后面细说,因为光是"IDEA 2023创建JavaWeb项目"这个步骤就能劝退一批新手。
3.2 用IDEA 2023从零创建一个可运行的JavaWeb项目
IDEA 2023里创建一个最基础的JavaWeb项目,步骤和细节如下:
- 新建Project,选择Maven作为构建工具,JDK选1.8。注意不要勾选"从原型创建",直接生成标准Maven骨架即可。
- 在pom.xml里显式声明打包方式为war:
<packaging>war</packaging>。JavaWeb项目最终要部署到Tomcat的webapps目录,必须是war包。这一步漏掉的话,IDEA不会自动帮你打war。 - 关键一步:在Project Structure(Ctrl+Shift+Alt+S)中,选中Modules下面的当前模块,点击加号,添加"Web"模块支持。这样IDEA 2023才会生成webapp目录和web.xml。很多教程里说"右键项目直接Add Framework Support"——在新版IDEA里如果项目是通过Maven骨架创建的,直接右键确实能找到,但你知道它是怎么工作的吗?它其实就是在模块配置里加了Web facet。知道这一点,以后遇到任何奇怪的IDE问题都能从根源排查。
- 配置Artifacts。把项目Artifact选为"Web Application: Exploded",这是开发调试模式,后面只需把它加到Tomcat的Deployment里即可。
- 配置Tomcat。Run → Edit Configurations → 添加Tomcat Server → Local。注意Application server这里要选择Tomcat安装目录。在Deployment标签页里,把上一步的Exploded Artifact加进
