在体育产业数字化浪潮下,传统篮球场馆的运营模式正面临严峻挑战。电话预约的占线拥堵、现场登记的手忙脚乱、场地状态的更新滞后以及财务对账的复杂繁琐,构成了制约场馆服务效率与用户体验提升的核心瓶颈。为解决这些痛点,我们设计并实现了一套基于SSH(Struts2 + Spring + Hibernate)集成框架的篮球场馆智能预订平台——"CourtSync 场馆通"。该系统通过全流程数字化管理,为场馆方提供了高效的运营工具,同时为用户带来了便捷透明的预订体验。
系统采用经典的三层架构设计,各层职责分明,通过框架整合实现了高内聚低耦合的工程目标。表现层由Struts2框架主导,负责接收用户HTTP请求、封装表单数据并控制页面导航。其核心机制是通过配置struts.xml映射文件,将特定的URL请求路由至对应的Action类进行处理。业务逻辑层构建于Spring框架之上,利用其控制反转(IoC)和依赖注入(DI)容器,统一管理Service组件、DAO对象及事务边界。数据持久层则依托Hibernate实现对象关系映射(ORM),将Java实体对象与数据库表结构无缝衔接,极大简化了数据持久化操作。三层之间通过接口抽象进行通信,确保了系统的可测试性与可维护性。
数据库架构设计与核心表解析
数据库作为系统的数据基石,其设计直接决定了系统的性能与稳定性。本系统共设计11张核心数据表,以下重点分析其中三张关键表的结构与设计亮点。
场地信息表(b_court) 是系统的核心资源表,其结构设计体现了对业务细节的精准把握:
CREATE TABLE `b_court` (
`courtId` int(11) NOT NULL AUTO_INCREMENT,
`courtName` varchar(50) DEFAULT NULL,
`area` varchar(50) DEFAULT NULL,
`price` decimal(10,2) DEFAULT NULL,
`introduce` text,
`courtDate` date DEFAULT NULL,
`state` int(11) DEFAULT '1',
PRIMARY KEY (`courtId`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
该表设计中,price字段采用decimal(10,2)类型,确保金额计算的精确性,避免浮点数精度问题。state字段使用整型标识场地状态(如1-可用,0-停用),为后续状态扩展预留了空间。courtDate字段独立存储,支持按日期动态调整场地可用性,满足了场馆特殊日期管理的需求。
订单主表(b_order) 的设计展现了复杂业务关系的处理能力:
CREATE TABLE `b_order` (
`orderId` int(11) NOT NULL AUTO_INCREMENT,
`orderNo` varchar(100) DEFAULT NULL,
`userId` int(11) DEFAULT NULL,
`totalMoney` decimal(10,2) DEFAULT NULL,
`createTime` datetime DEFAULT NULL,
`status` int(11) DEFAULT '1',
`payTime` datetime DEFAULT NULL,
`payType` int(11) DEFAULT NULL,
PRIMARY KEY (`orderId`),
KEY `FK_order_user` (`userId`),
CONSTRAINT `FK_order_user` FOREIGN KEY (`userId`) REFERENCES `b_user` (`userId`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8;
订单表通过orderNo字段存储唯一订单号,采用业务自定义规则生成,便于追踪查询。status字段完整记录了订单生命周期(待支付、已支付、已完成、已取消等)。外键userId关联用户表,确保了数据引用完整性。payTime与payType的分离设计,支持多种支付方式接入与支付时间记录。
订单明细表(b_order_detail) 采用主从表结构实现复杂订单的灵活管理:
CREATE TABLE `b_order_detail` (
`detailId` int(11) NOT NULL AUTO_INCREMENT,
`orderId` int(11) DEFAULT NULL,
`courtId` int(11) DEFAULT NULL,
`startTime` datetime DEFAULT NULL,
`endTime` datetime DEFAULT NULL,
`total` decimal(10,2) DEFAULT NULL,
PRIMARY KEY (`detailId`),
KEY `FK_detail_order` (`orderId`),
KEY `FK_detail_court` (`courtId`),
CONSTRAINT `FK_detail_court` FOREIGN KEY (`courtId`) REFERENCES `b_court` (`courtId`),
CONSTRAINT `FK_detail_order` FOREIGN KEY (`orderId`) REFERENCES `b_order` (`orderId`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;
该表通过startTime和endTime精确记录场地使用时段,为时段冲突检测提供数据基础。与主表的解耦设计支持一个订单同时预订多个场地或时段,满足了团体活动的复杂预订需求。total字段冗余存储明细金额,既提高了查询效率,又确保了历史价格的准确性。
核心功能模块深度解析
1. 智能场地预订与冲突检测机制
场地预订是系统的核心功能,其实现涉及复杂的业务逻辑判断。前端通过JSP页面展示场地日历视图,用户选择日期和时段后,系统需实时检测该时段是否已被预订。
// 场地预订冲突检测核心逻辑
public class CourtBookingService {
public BookingResult bookCourt(BookingRequest request) {
// 检测时段冲突
List<OrderDetail> conflicts = orderDetailDao.findConflicts(
request.getCourtId(),
request.getStartTime(),
request.getEndTime()
);
if (!conflicts.isEmpty()) {
return BookingResult.error("该时段已被预订,请选择其他时段");
}
// 创建订单
Order order = new Order();
order.setOrderNo(generateOrderNo());
order.setUserId(request.getUserId());
order.setCreateTime(new Date());
order.setStatus(OrderStatus.PENDING_PAYMENT);
// 创建订单明细
OrderDetail detail = new OrderDetail();
detail.setCourtId(request.getCourtId());
detail.setStartTime(request.getStartTime());
detail.setEndTime(request.getEndTime());
detail.setTotal(calculateAmount(request));
order.getOrderDetails().add(detail);
order.setTotalMoney(detail.getTotal());
// 保存订单
orderDao.save(order);
return BookingResult.success(order);
}
private String generateOrderNo() {
return "CO" + System.currentTimeMillis() +
String.format("%04d", new Random().nextInt(9999));
}
}

冲突检测通过HQL查询实现,精确比对时间区间重叠:
@Repository
public class OrderDetailDaoImpl implements OrderDetailDao {
public List<OrderDetail> findConflicts(Integer courtId, Date startTime, Date endTime) {
String hql = "FROM OrderDetail od WHERE od.courtId = :courtId " +
"AND od.status != :cancelledStatus " +
"AND ((od.startTime BETWEEN :startTime AND :endTime) " +
"OR (od.endTime BETWEEN :startTime AND :endTime) " +
"OR (:startTime BETWEEN od.startTime AND od.endTime))";
return getSession().createQuery(hql, OrderDetail.class)
.setParameter("courtId", courtId)
.setParameter("cancelledStatus", OrderStatus.CANCELLED)
.setParameter("startTime", startTime)
.setParameter("endTime", endTime)
.list();
}
}
2. 多角色权限管理与安全控制
系统支持管理员、收银员、普通用户三级角色,各角色权限严格分离。通过Struts2拦截器实现统一的权限验证:
// 权限拦截器实现
public class AuthorizationInterceptor extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation invocation) throws Exception {
ActionContext context = invocation.getInvocationContext();
HttpSession session = context.getSession();
User user = (User) session.getAttribute("currentUser");
if (user == null) {
return "login"; // 跳转到登录页
}
// 获取请求的Action和Method
String actionName = invocation.getProxy().getActionName();
String methodName = invocation.getProxy().getMethod();
// 检查用户权限
if (!hasPermission(user, actionName, methodName)) {
return "noPermission"; // 权限不足页面
}
return invocation.invoke();
}
private boolean hasPermission(User user, String actionName, String methodName) {
// 基于角色和权限配置的验证逻辑
Set<String> permissions = permissionService.getPermissions(user.getRole());
String requiredPermission = actionName + ":" + methodName;
return permissions.contains(requiredPermission);
}
}

用户登录验证通过Spring Service层实现:
@Service
@Transactional
public class UserService {
@Autowired
private UserDao userDao;
public LoginResult login(String username, String password) {
User user = userDao.findByUsername(username);
if (user == null) {
return LoginResult.error("用户不存在");
}
if (!user.getPassword().equals(md5(password))) {
return LoginResult.error("密码错误");
}
if (user.getStatus() != 1) {
return LoginResult.error("账户已被禁用");
}
// 登录成功,更新最后登录时间
user.setLastLoginTime(new Date());
userDao.update(user);
return LoginResult.success(user);
}
}
3. 订单管理与支付流程集成
订单管理模块采用Struts2 Action处理前端请求,通过Spring注解管理事务:
// 订单管理Action
@Controller
@Scope("prototype")
public class OrderAction extends BaseAction {
@Autowired
private OrderService orderService;
private Order order;
private List<Order> orders;
// 创建订单
public String create() {
try {
order.setUserId(getCurrentUser().getUserId());
Order result = orderService.createOrder(order);
setJsonResult(Result.success(result));
} catch (BusinessException e) {
setJsonResult(Result.error(e.getMessage()));
}
return JSON;
}
// 查询用户订单
public String listByUser() {
Integer userId = getCurrentUser().getUserId();
orders = orderService.findOrdersByUser(userId);
return SUCCESS;
}
// 支付订单
public String pay() {
try {
orderService.payOrder(order.getOrderId(), getCurrentUser());
setJsonResult(Result.success("支付成功"));
} catch (BusinessException e) {
setJsonResult(Result.error(e.getMessage()));
}
return JSON;
}
// Getter和Setter方法
public Order getOrder() { return order; }
public void setOrder(Order order) { this.order = order; }
public List<Order> getOrders() { return orders; }
}

支付服务通过Spring声明式事务确保数据一致性:
@Service
@Transactional
public class PaymentService {
@Autowired
private OrderDao orderDao;
@Autowired
private PaymentRecordDao paymentRecordDao;
public void processPayment(Integer orderId, Integer payType, User user) {
Order order = orderDao.findById(orderId);
if (order == null) {
throw new BusinessException("订单不存在");
}
if (order.getStatus() != OrderStatus.PENDING_PAYMENT) {
throw new BusinessException("订单状态异常,无法支付");
}
// 创建支付记录
PaymentRecord record = new PaymentRecord();
record.setOrderId(orderId);
record.setAmount(order.getTotalMoney());
record.setPayType(payType);
record.setPayTime(new Date());
record.setUserId(user.getUserId());
paymentRecordDao.save(record);
// 更新订单状态
order.setStatus(OrderStatus.PAID);
order.setPayTime(new Date());
order.setPayType(payType);
orderDao.update(order);
// 记录审计日志
auditService.logPayment(orderId, user.getUserId());
}
}
4. 场地信息动态管理与可视化展示
场地管理模块支持管理员对场地信息进行全面维护,包括增删改查、状态管理等:
// 场地管理Action
@Controller
@Scope("prototype")
public class CourtAction extends BaseAction {
@Autowired
private CourtService courtService;
private Court court;
private List<Court> courts;
private File uploadImage; // 上传的图片文件
// 保存或更新场地信息
public String save() {
try {
if (uploadImage != null) {
// 处理图片上传
String imagePath = fileService.saveImage(uploadImage);
court.setImagePath(imagePath);
}
if (court.getCourtId() == null) {
courtService.addCourt(court);
} else {
courtService.updateCourt(court);
}
setJsonResult(Result.success("保存成功"));
} catch (Exception e) {
setJsonResult(Result.error("保存失败:" + e.getMessage()));
}
return JSON;
}
// 获取场地列表
public String list() {
courts = courtService.findAllCourts();
return SUCCESS;
}
// 根据条件查询场地
public String search() {
CourtCriteria criteria = buildCriteriaFromRequest();
courts = courtService.findCourtsByCriteria(criteria);
return SUCCESS;
}
}

实体模型与数据持久化设计
系统通过Hibernate实体映射实现对象关系管理,以下是核心实体类的设计:
// 场地实体类
@Entity
@Table(name = "b_court")
public class Court implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "courtId")
private Integer courtId;
@Column(name = "courtName", length = 50)
private String courtName;
@Column(name = "area", length = 50)
private String area;
@Column(name = "price", precision = 10, scale = 2)
private BigDecimal price;
@Column(name = "introduce", columnDefinition = "text")
private String introduce;
@Temporal(TemporalType.DATE)
@Column(name = "courtDate")
private Date courtDate;
@Column(name = "state")
private Integer state;
@OneToMany(mappedBy = "court", cascade = CascadeType.ALL)
private Set<OrderDetail> orderDetails = new HashSet<>();
// 省略getter/setter方法
}
// 订单实体类
@Entity
@Table(name = "b_order")
public class Order implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "orderId")
private Integer orderId;
@Column(name = "orderNo", length = 100)
private String orderNo;
@ManyToOne
@JoinColumn(name = "userId")
private User user;
@Column(name = "totalMoney", precision = 10, scale = 2)
private BigDecimal totalMoney;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "createTime")
private Date createTime;
@Column(name = "status")
private Integer status;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "payTime")
private Date payTime;
@Column(name = "payType")
private Integer payType;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<OrderDetail> orderDetails = new HashSet<>();
// 业务逻辑方法
public boolean isPayable() {
return OrderStatus.PENDING_PAYMENT.equals(this.status);
}
public boolean isCancellable() {
return OrderStatus.PENDING_PAYMENT.equals(this.status) ||
OrderStatus.PAID.equals(this.status);
}
}
系统配置与框架整合
SSH框架的整合通过Spring的ApplicationContext统一管理,关键配置如下:
<!-- applicationContext.xml 核心配置 -->
<beans xmlns="http://www.springframework.org/schema/beans">
<!-- 数据源配置 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/basketball_court"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
<property name="maxPoolSize" value="50"/>
<property name="minPoolSize" value="5"/>
</bean>
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="com.courtsync.entity"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>