尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于SpringBoot+Vue的婚纱摄影预约系统设计与实现

基于SpringBoot+Vue的婚纱摄影预约系统设计与实现 简介这份资源是一篇完整的Java婚纱摄影预约系统设计与实现论文文档面向计算机专业学生、软件工程课程设计者及需要完成毕业设计的开发者帮助解决传统婚纱摄影预约中信息管理不便、效率低下的问题。文档围绕Java语言、SpringBoot框架、Vue框架与MySQL数据库展开系统梳理了个人中心、套系风格管理、用户管理、摄影师管理、婚纱套系管理、订单管理、客片欣赏管理、客户样片管理、摄影咨询管理、客户选片管理及系统管理等十余个功能模块的设计思路与实现过程。资源包内共1个docx文件约3.49MB内容涵盖摘要、技术选型、系统分析与数据库设计等章节结构完整可直接作为论文写作模板或项目开发参考。目前已有305人学习下载适合需要快速搭建预约类管理系统、理解前后端分离架构或撰写同类论文的读者借鉴使用。1. 婚纱摄影预约系统为什么值得用 SpringBoot Vue 重写一遍很多影楼和摄影工作室的预约流程还停留在微信聊天加 Excel 表格的阶段客户问档期、客服翻表格、摄影师排期靠脑子记、定金收没收到要单独问财务。单量少的时候能撑住一旦旺季一天几十组咨询漏单、撞档、重复排期就会集中爆发。婚纱摄影预约系统要解决的核心问题是把「选套餐—看档期—锁摄影师—付定金—改期—出片」这条链路收敛到一个可查询、可校验、可追溯的后台里。这个标题里的技术组合是 Java 生态里最稳的一套SpringBoot 做后端接口和业务校验Vue 做前后端分离的交互层MySQL 存套餐、档期、订单和用户数据。它适合两类人一类是课程设计或毕业设计需要完整跑通一套业务系统的学生另一类是真的想给工作室搭一套内部预约工具的开发。下面按「数据怎么设计—后端怎么排期—前端怎么交互—上线怎么排错」的顺序把能复现的做法讲清楚。2. 婚纱摄影预约系统的 MySQL 表结构设计与档期冲突校验2.1 从业务对象倒推表结构婚纱摄影预约系统里真正会变的实体只有几个套餐、摄影师、档期、订单、客户。套餐和摄影师相对静态档期和订单是高频写入的。常见做法是把「档期」单独抽成一张表而不是塞进订单表里用时间字段硬算因为一个摄影师一天可能接多组一组又可能跨半天。-- 套餐表婚纱摄影的拍摄套系 CREATE TABLE package ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(64) NOT NULL COMMENT 套餐名如轻奢海景, price DECIMAL(10,2) NOT NULL COMMENT 套餐价格, shoot_hours INT NOT NULL DEFAULT 4 COMMENT 拍摄时长(小时), status TINYINT NOT NULL DEFAULT 1 COMMENT 1上架 0下架 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 档期表摄影师某天某个时段是否可约 CREATE TABLE schedule ( id BIGINT PRIMARY KEY AUTO_INCREMENT, photographer_id BIGINT NOT NULL, shoot_date DATE NOT NULL COMMENT 拍摄日期, time_slot TINYINT NOT NULL COMMENT 1上午 2下午 3全天, order_id BIGINT DEFAULT NULL COMMENT 被哪个订单占用, UNIQUE KEY uk_photo_date_slot (photographer_id, shoot_date, time_slot) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;uk_photo_date_slot这个唯一索引是整个系统防撞档的底线。哪怕后端并发判断写漏了数据库这一层也会直接拒绝重复占用同一摄影师同一天同一时段。time_slot用枚举值而不是存具体时间点是因为婚纱摄影的排期粒度通常就是上午/下午存成 datetime 反而让查询和展示都变复杂。2.2 用 SQL 查出可预约档期客户在前端选日期时后端要返回「这天哪些摄影师还有空」。不要用循环一条条查直接一条 SQL 把已占用和全部摄影师做差集。-- 查询指定日期仍可预约的摄影师 SELECT p.id, p.name FROM photographer p WHERE p.status 1 AND p.id NOT IN ( SELECT s.photographer_id FROM schedule s WHERE s.shoot_date 2025-06-01 AND s.time_slot 2 AND s.order_id IS NOT NULL );子查询里order_id IS NOT NULL是关键档期表可以预先生成空档记录只有被订单真正占用时才写 order_id。这样查询逻辑和「预占—确认」的流程能对上避免客户下单未付款时档期被永久锁死。2.3 下单时的并发校验两个客户同时点同一个档期靠前端置灰是不够的。后端在创建订单时要先尝试占用档期用受影响行数判断是否抢到。// 尝试占用档期返回是否成功 Transactional public boolean lockSchedule(Long photographerId, LocalDate date, Integer slot, Long orderId) { int rows scheduleMapper.lockSlot(photographerId, date, slot, orderId); // rows 0 说明该档期已被占用或不存在 return rows 0; }对应的 Mapper SQL 用条件更新把「未被占用」作为 where 条件UPDATE schedule SET order_id #{orderId} WHERE photographer_id #{photographerId} AND shoot_date #{date} AND time_slot #{slot} AND order_id IS NULL;Transactional保证占用档期和写订单在同一个事务里任何一步失败都回滚。这里要注意 MySQL 默认隔离级别是 REPEATABLE READ条件更新本身会加行锁所以不需要额外 select for update少一次查询也少一次锁竞争。3. SpringBoot 后端接口分层与预约状态机实现3.1 接口按业务动作划分而不是按表划分新手容易把 Controller 写成PackageController、ScheduleController这种按表命名的形式结果一个下单动作要跨三个 Controller。婚纱摄影预约系统的接口应该按动作走查档期、锁档期、下单、改期、取消。这样前端调用路径清晰权限也容易挂。RestController RequestMapping(/api/booking) public class BookingController { Autowired private BookingService bookingService; // 查询某天可约摄影师 GetMapping(/available) public ResultListPhotographerVO available( RequestParam DateTimeFormat(pattern yyyy-MM-dd) LocalDate date, RequestParam Integer slot) { return Result.ok(bookingService.listAvailable(date, slot)); } // 提交预约 PostMapping(/submit) public ResultLong submit(RequestBody Valid BookingDTO dto) { return Result.ok(bookingService.submit(dto)); } }Valid配合 DTO 上的注解做参数校验比如手机号格式、日期不能早于今天。校验失败会抛MethodArgumentNotValidException用一个RestControllerAdvice统一转成Result结构前端就不用处理两种错误格式。3.2 订单状态用状态机而不是散落的 if预约订单会经历「待付定金—已付定金—已拍摄—已出片—已取消」。如果每个接口里都写if (status 1) ... else if (status 2)改一个状态要翻遍所有方法。常见做法是定义一个状态枚举加允许的迁移表。当前状态允许的下一步触发动作待付定金已付定金、已取消支付、超时取消已付定金已拍摄、已取消到店拍摄、客户取消已拍摄已出片后期完成已出片无流程结束public enum OrderStatus { PENDING(1), DEPOSIT_PAID(2), SHOOTED(3), DELIVERED(4), CANCELED(9); private final int code; OrderStatus(int code) { this.code code; } // 判断能否从当前状态迁移到目标状态 public static boolean canTransfer(int from, int to) { if (from PENDING.code) return to DEPOSIT_PAID.code || to CANCELED.code; if (from DEPOSIT_PAID.code) return to SHOOTED.code || to CANCELED.code; if (from SHOOTED.code) return to DELIVERED.code; return false; } }每次改状态前调一次canTransfer不合法就抛业务异常。这样状态规则集中在一处加新状态只改枚举和迁移判断不会漏改。3.3 定金超时未付自动释放档期客户锁了档期但一直不付定金档期就被占着。用 Spring 的定时任务扫超时订单把档期释放回可约状态。Scheduled(fixedDelay 60_000) // 每分钟扫一次 public void releaseTimeoutOrders() { // 查出创建超过30分钟且仍待付定金的订单 ListOrder timeout orderMapper.listTimeout(30); for (Order o : timeout) { orderMapper.updateStatus(o.getId(), OrderStatus.CANCELED.getCode()); scheduleMapper.release(o.getPhotographerId(), o.getShootDate(), o.getTimeSlot()); } }fixedDelay表示上一次执行结束后再等 60 秒避免任务堆积。释放档期就是把 schedule 的 order_id 置回 NULL配合前面的唯一索引释放后别人就能重新占用。生产环境如果多实例部署这个定时任务要加分布式锁否则两个实例会重复处理同一批订单。4. Vue 前端预约流程与档期日历组件落地4.1 用 Vue 3 组合式 API 组织预约页状态预约页要维护的状态不少当前选中的日期、时段、套餐、摄影师、客户信息。用reactive把这一组状态收在一起比散落十几个 ref 好维护。import { reactive, computed } from vue export function useBooking() { const form reactive({ date: , slot: 1, packageId: null, photographerId: null, customerName: , phone: }) // 必填项是否齐全控制提交按钮 const canSubmit computed(() form.date form.packageId form.photographerId form.phone ) return { form, canSubmit } }computed依赖 form 里的字段任一字段变化都会重新计算canSubmit模板里直接绑定按钮的 disabled 即可不需要手动监听。4.2 档期日历的请求时机日历组件不要一打开就把整月每天的可约情况都拉回来数据量大且大部分用不上。常见做法是用户切换月份时请求该月有档期的日期集合点具体某天再请求当天可约摄影师。// 切换月份时拉取该月可约日期 async function loadMonth(year, month) { const res await axios.get(/api/booking/month, { params: { year, month } }) // res.data 形如 [2025-06-01, 2025-06-03] availableDates.value res.data } // 点击某天再拉当天可约摄影师 async function onPickDate(date) { const res await axios.get(/api/booking/available, { params: { date, slot: form.slot } }) photographers.value res.data }两次请求分开的好处是首屏快用户不点具体日期就不会触发摄影师查询。availableDates用来给日历上有空的日期加高亮样式没有的置灰。4.3 提交预约与错误处理提交时后端可能返回「档期已被占用」前端要能把这个错误明确展示出来并刷新档期。async function submit() { try { const res await axios.post(/api/booking/submit, form) router.push(/order/${res.data}) } catch (e) { if (e.response?.data?.code 409) { alert(该档期刚被占用请重新选择) await onPickDate(form.date) // 刷新可约摄影师 } else { alert(e.response?.data?.msg || 提交失败) } } }后端在档期被占用时返回 HTTP 409 和业务码前端据此区分「档期冲突」和「参数错误」。刷新可约列表让用户立刻看到最新状态而不是停留在过期数据上反复提交。5. 婚纱摄影预约系统的联调排错与上线前检查5.1 跨域和日期格式这两个高频坑前后端分离开发时Vue 跑在 5173SpringBoot 跑在 8080浏览器会拦跨域。开发阶段在 SpringBoot 加全局配置Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE) .allowCredentials(true); } }注意allowedOriginPatterns而不是allowedOrigins后者在allowCredentials(true)时不允许写*。上线后要把来源收窄到实际域名不要留着通配。日期格式是另一个高频问题前端传2025-06-01后端用LocalDate接收必须加DateTimeFormat用LocalDateTime接收则要加JsonFormat(pattern yyyy-MM-dd HH:mm:ss)。少一个注解就会报 400且错误信息不直观联调时优先检查这里。5.2 上线前的数据与索引检查上线前把几条关键 SQL 用EXPLAIN过一遍确认走索引。档期查询依赖uk_photo_date_slot订单列表按客户手机号查要单独建索引。-- 确认档期查询走唯一索引 EXPLAIN SELECT * FROM schedule WHERE photographer_id 3 AND shoot_date 2025-06-01 AND time_slot 2; -- 订单按手机号查询的索引 CREATE INDEX idx_order_phone ON order (customer_phone);EXPLAIN结果里type是ref或const、key显示用到的索引名才算正常。如果出现ALL全表扫描数据量上来后档期查询会明显变慢。5.3 一个容易被忽略的细节档期预生成档期表如果只在被预约时才插入记录前面的条件更新order_id IS NULL就永远匹配不到行占用会失败。常见做法是提前把未来 30 天每个摄影师的上午、下午档期批量生成空记录order_id 留空。生成脚本可以放在定时任务里每天补一批保证可约日期始终有数据可查。这一步不做前端日历会显示所有日期都不可约排查时容易误以为是接口问题实际是数据没铺。本文还有配套的精品资源点击获取
返回列表