在传统婚纱摄影行业数字化转型的浪潮中,一个集在线展示、客户预约与业务管理于一体的官方网站平台已成为提升竞争力的关键工具。该系统采用经典的SSH(Struts2 + Spring + Hibernate)全栈架构,为中小型摄影工作室提供了完整的线上解决方案。
系统架构与技术栈设计
该平台采用典型的三层架构模式,每一层都选用成熟稳定的技术框架实现。表现层使用Struts2作为MVC框架,通过配置struts.xml文件定义请求映射关系,结合JSP和jQuery实现动态页面渲染和前端交互。业务逻辑层由Spring框架统一管理,通过依赖注入机制解耦各组件,并利用声明式事务管理确保数据一致性。持久层采用Hibernate实现对象关系映射,通过配置hibernate.cfg.xml连接MySQL数据库,极大简化了数据访问操作。
Spring的IoC容器作为系统的核心枢纽,负责管理Action、Service、DAO等各组件的生命周期和依赖关系。以下配置展示了Spring如何整合Struts2 Action和Hibernate事务管理:
<!-- Spring配置文件中定义业务逻辑组件 -->
<bean id="weddingDressService" class="com.maancode.service.impl.WeddingDressServiceImpl">
<property name="weddingDressDao" ref="weddingDressDao"/>
</bean>
<!-- 配置Hibernate事务管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- 使用AOP配置声明式事务 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="get*" read-only="true"/>
</tx:attributes>
</tx:advice>
数据库设计与核心表结构
系统数据库包含8个核心表,涵盖了用户管理、婚纱礼服、摄影套餐、订单预约等业务实体。数据库设计遵循第三范式,确保数据的一致性和完整性。
婚纱礼服表(wedding_dress)设计分析
婚纱礼服作为核心业务实体,其表结构设计体现了详细的产品属性管理:
CREATE TABLE `wedding_dress` (
`dress_id` int(11) NOT NULL AUTO_INCREMENT,
`dress_name` varchar(100) NOT NULL COMMENT '婚纱名称',
`style_id` int(11) NOT NULL COMMENT '风格ID',
`rental_price` decimal(10,2) NOT NULL COMMENT '租赁价格',
`purchase_price` decimal(10,2) DEFAULT NULL COMMENT '购买价格',
`size_range` varchar(50) DEFAULT NULL COMMENT '尺寸范围',
`fabric` varchar(100) DEFAULT NULL COMMENT '面料材质',
`color` varchar(50) DEFAULT NULL COMMENT '颜色',
`inventory` int(11) NOT NULL DEFAULT '0' COMMENT '库存数量',
`description` text COMMENT '详细描述',
`main_image` varchar(255) DEFAULT NULL COMMENT '主图路径',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`dress_id`),
KEY `idx_style` (`style_id`),
KEY `idx_price` (`rental_price`),
CONSTRAINT `fk_dress_style` FOREIGN KEY (`style_id`)
REFERENCES `dress_style` (`style_id`) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='婚纱礼服信息表';
该表设计具有以下技术亮点:
- 价格字段区分:分别设置rental_price(租赁价)和purchase_price(购买价),支持灵活的商业模式
- 库存管理:inventory字段实现库存控制,防止超售
- 时间戳追踪:create_time和update_time自动记录数据变更时间
- 外键约束:通过style_id关联婚纱风格表,确保数据一致性
- 索引优化:为style_id和rental_price建立索引,提升查询性能
订单预约表(reservation_order)设计
订单表的设计重点考虑了业务流转和状态管理需求:
CREATE TABLE `reservation_order` (
`order_id` varchar(32) NOT NULL COMMENT '订单编号',
`user_id` int(11) NOT NULL COMMENT '用户ID',
`package_id` int(11) NOT NULL COMMENT '套餐ID',
`dress_ids` varchar(255) DEFAULT NULL COMMENT '选中的婚纱ID集合',
`order_amount` decimal(10,2) NOT NULL COMMENT '订单金额',
`reservation_date` date NOT NULL COMMENT '预约拍摄日期',
`time_slot` varchar(20) NOT NULL COMMENT '时间段',
`contact_name` varchar(50) NOT NULL COMMENT '联系人姓名',
`contact_phone` varchar(20) NOT NULL COMMENT '联系电话',
`special_requirements` text COMMENT '特殊要求',
`order_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '订单状态',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`order_id`),
KEY `idx_user` (`user_id`),
KEY `idx_date` (`reservation_date`),
KEY `idx_status` (`order_status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='预约订单表';
订单表的技术特点包括:
- 业务主键:使用自定义order_id而非自增ID,便于业务识别
- 状态管理:order_status字段支持多状态流转(待确认、已确认、已完成等)
- 日期索引:reservation_date索引优化档期查询效率
- 灵活存储:dress_ids以逗号分隔存储多件婚纱选择,平衡查询性能与灵活性
核心功能模块实现
婚纱展示与筛选功能
系统前台提供丰富的婚纱浏览和筛选功能,用户可按风格、价格区间、尺寸等多维度查找心仪婚纱。前端通过jQuery实现动态筛选,后端Struts2 Action处理查询请求:
/**
* 婚纱礼服查询Action
*/
public class WeddingDressAction extends ActionSupport {
private List<WeddingDress> dressList;
private Integer styleId;
private BigDecimal minPrice;
private BigDecimal maxPrice;
private String size;
public String execute() {
try {
// 构建查询条件
WeddingDressQuery query = new WeddingDressQuery();
if (styleId != null) query.setStyleId(styleId);
if (minPrice != null) query.setMinPrice(minPrice);
if (maxPrice != null) query.setMaxPrice(maxPrice);
if (size != null) query.setSize(size);
// 调用Service层获取数据
dressList = weddingDressService.findDressesByQuery(query);
return SUCCESS;
} catch (Exception e) {
addActionError("查询失败:" + e.getMessage());
return ERROR;
}
}
// Getter和Setter方法
public List<WeddingDress> getDressList() { return dressList; }
public void setStyleId(Integer styleId) { this.styleId = styleId; }
// ... 其他setter方法
}
对应的Hibernate查询方法通过Criteria API实现动态条件组装:
/**
* 婚纱数据访问层实现
*/
@Repository
public class WeddingDressDaoImpl extends BaseDaoImpl<WeddingDress>
implements WeddingDressDao {
@Override
public List<WeddingDress> findByQuery(WeddingDressQuery query) {
Criteria criteria = getSession().createCriteria(WeddingDress.class);
// 动态添加查询条件
if (query.getStyleId() != null) {
criteria.add(Restrictions.eq("style.styleId", query.getStyleId()));
}
if (query.getMinPrice() != null) {
criteria.add(Restrictions.ge("rentalPrice", query.getMinPrice()));
}
if (query.getMaxPrice() != null) {
criteria.add(Restrictions.le("rentalPrice", query.getMaxPrice()));
}
if (query.getSize() != null) {
criteria.add(Restrictions.like("sizeRange", "%" + query.getSize() + "%"));
}
// 添加库存限制
criteria.add(Restrictions.gt("inventory", 0));
return criteria.list();
}
}

在线预约下单流程
客户选择心仪套餐和婚纱后,可在线提交拍摄预约。系统通过JavaScript进行表单验证,确保必填项完整和日期有效性:
// 前端预约表单验证
function validateReservationForm() {
var reservationDate = $('#reservationDate').val();
var contactName = $('#contactName').val();
var contactPhone = $('#contactPhone').val();
if (!reservationDate) {
alert('请选择预约日期');
return false;
}
// 验证日期是否大于今天
var selectedDate = new Date(reservationDate);
var today = new Date();
if (selectedDate <= today) {
alert('预约日期必须大于今天');
return false;
}
if (!contactName || contactName.trim().length === 0) {
alert('请输入联系人姓名');
return false;
}
// 手机号格式验证
var phoneRegex = /^1[3-9]\d{9}$/;
if (!phoneRegex.test(contactPhone)) {
alert('请输入正确的手机号码');
return false;
}
return true;
}
// 提交预约订单
function submitReservation() {
if (!validateReservationForm()) return;
var formData = $('#reservationForm').serialize();
$.ajax({
url: 'reservation_submit.action',
type: 'POST',
data: formData,
success: function(response) {
if (response.success) {
alert('预约成功!订单号:' + response.orderId);
window.location.href = 'order_detail.action?orderId=' + response.orderId;
} else {
alert('预约失败:' + response.message);
}
},
error: function() {
alert('网络错误,请重试');
}
});
}
后端订单处理Service包含完整的业务逻辑和事务管理:
/**
* 预约订单服务实现
*/
@Service
@Transactional
public class ReservationServiceImpl implements ReservationService {
@Autowired
private ReservationOrderDao reservationOrderDao;
@Autowired
private WeddingDressDao weddingDressDao;
@Override
public ReservationResult submitReservation(ReservationForm form) {
ReservationResult result = new ReservationResult();
try {
// 1. 验证套餐和婚纱可用性
PhotographyPackage package = packageDao.get(form.getPackageId());
if (package == null) {
result.setSuccess(false);
result.setMessage("选择的套餐不存在");
return result;
}
// 2. 检查婚纱库存
List<Integer> dressIds = form.getSelectedDressIds();
if (dressIds != null && !dressIds.isEmpty()) {
for (Integer dressId : dressIds) {
WeddingDress dress = weddingDressDao.get(dressId);
if (dress == null || dress.getInventory() <= 0) {
result.setSuccess(false);
result.setMessage("婚纱 " + dress.getDressName() + " 库存不足");
return result;
}
}
}
// 3. 生成订单号
String orderId = generateOrderId();
// 4. 创建订单实体
ReservationOrder order = new ReservationOrder();
order.setOrderId(orderId);
order.setUser(form.getUser());
order.setPackage(package);
order.setDressIds(StringUtils.join(dressIds, ","));
order.setOrderAmount(calculateOrderAmount(package, dressIds));
order.setReservationDate(form.getReservationDate());
order.setContactName(form.getContactName());
order.setContactPhone(form.getContactPhone());
order.setSpecialRequirements(form.getSpecialRequirements());
order.setOrderStatus(OrderStatus.PENDING);
// 5. 保存订单
reservationOrderDao.save(order);
// 6. 更新婚纱库存
if (dressIds != null) {
for (Integer dressId : dressIds) {
weddingDressDao.decreaseInventory(dressId, 1);
}
}
result.setSuccess(true);
result.setOrderId(orderId);
} catch (Exception e) {
result.setSuccess(false);
result.setMessage("系统错误:" + e.getMessage());
// 事务回滚会自动触发
throw new RuntimeException("订单提交失败", e);
}
return result;
}
private String generateOrderId() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String timestamp = sdf.format(new Date());
Random random = new Random();
return "ORD" + timestamp + random.nextInt(1000);
}
}

后台管理系统功能
管理员可通过后台系统管理婚纱信息、处理订单、维护套餐内容。订单管理模块提供多条件筛选和状态更新功能:
/**
* 后台订单管理Action
*/
public class OrderManageAction extends ActionSupport {
private List<ReservationOrder> orderList;
private String orderId;
private Integer status;
private Date startDate;
private Date endDate;
public String list() {
try {
OrderQuery query = new OrderQuery();
if (orderId != null) query.setOrderId(orderId);
if (status != null) query.setStatus(status);
if (startDate != null) query.setStartDate(startDate);
if (endDate != null) query.setEndDate(endDate);
orderList = orderService.findOrdersByQuery(query);
return SUCCESS;
} catch (Exception e) {
addActionError("查询订单失败:" + e.getMessage());
return ERROR;
}
}
public String updateStatus() {
try {
orderService.updateOrderStatus(orderId, status);
addActionMessage("订单状态更新成功");
return SUCCESS;
} catch (Exception e) {
addActionError("状态更新失败:" + e.getMessage());
return ERROR;
}
}
}

实体模型与关系映射
系统通过Hibernate注解实现对象关系映射,以下是核心实体类的设计:
/**
* 婚纱礼服实体类
*/
@Entity
@Table(name = "wedding_dress")
public class WeddingDress implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "dress_id")
private Integer dressId;
@Column(name = "dress_name", nullable = false, length = 100)
private String dressName;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "style_id", nullable = false)
private DressStyle style;
@Column(name = "rental_price", nullable = false, precision = 10, scale = 2)
private BigDecimal rentalPrice;
@Column(name = "inventory", nullable = false)
private Integer inventory = 0;
@Column(name = "description", length = 1000)
private String description;
@Column(name = "main_image", length = 255)
private String mainImage;
@Column(name = "create_time")
@Temporal(TemporalType.TIMESTAMP)
private Date createTime;
@Column(name = "update_time")
@Temporal(TemporalType.TIMESTAMP)
private Date updateTime;
// 关联关系:一件婚纱可以被多个订单选择
@OneToMany(mappedBy = "weddingDress", fetch = FetchType.LAZY)
private Set<OrderDressRelation> orderRelations = new HashSet<>();
// Getter和Setter方法
public Integer getDressId() { return dressId; }
public void setDressId(Integer dressId) { this.dressId = dressId; }
// ... 其他getter/setter方法
}
/**
* 预约订单实体类
*/
@Entity
@Table(name = "reservation_order")
public class ReservationOrder implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "order_id", length = 32)
private String orderId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "package_id", nullable = false)
private PhotographyPackage photographyPackage;
@Column(name = "dress_ids", length = 255)
private String dressIds;
@Column(name = "order_amount", nullable = false, precision = 10, scale = 2)
private BigDecimal orderAmount;
@Column(name = "reservation_date", nullable = false)
@Temporal(TemporalType.DATE)
private Date reservationDate;
@Column(name = "order_status", nullable = false)
private Integer orderStatus;
@Column(name = "create_time")
@Temporal(TemporalType.TIMESTAMP)
private Date createTime;
@Column(name = "update_time")
@Temporal(TemporalType.TIMESTAMP)
private Date updateTime;
// 业务方法:获取选中的婚纱ID列表
public List<Integer> getSelectedDressIdList() {
if (StringUtils.isBlank(dressIds)) {
return new ArrayList<>();
}
return Arrays.stream(dressIds.split(","