温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系名片!
一、项目背景与意义
随着摄影消费需求的增长,越来越多的用户希望通过线上渠道寻找合适的摄影师完成写真、旅拍、婚礼跟拍等拍摄服务。传统约拍方式多依赖社交平台私信沟通,存在档期不透明、价格不统一、作品交付混乱、版权归属不清等问题。基于 Spring Boot + Vue3 构建摄影约拍与作品交付平台,可以把摄影师展示、档期预约、在线下单、拍摄沟通、成片交付与售后评价整合到同一套系统中,提升交易效率与用户体验。
从技术角度看,该项目覆盖了前后端分离、权限控制、文件存储、订单状态流转、消息通知等典型业务场景,适合作为完整的全栈实战项目。
二、系统功能需求分析
2.1 用户角色划分
- 普通用户(客户):浏览摄影师与作品、预约档期、下单支付、查看订单进度、下载成片、评价摄影师。
- 摄影师:维护个人主页与作品集、设置拍摄套餐与价格、管理档期、接单与沟通、上传并交付成片。
- 平台管理员:审核摄影师入驻资质、管理作品与套餐、处理投诉与退款、统计平台运营数据。
2.2 核心功能模块
| 模块 | 主要功能 |
|---|---|
| 用户与认证 | 注册登录、JWT 鉴权、角色权限、个人资料维护 |
| 摄影师与作品 | 摄影师主页、作品集上传与分类、套餐与价格管理 |
| 档期与预约 | 档期日历、可预约时间设置、预约申请与确认 |
| 订单与支付 | 下单、订单状态流转、支付回调、退款处理 |
| 作品交付 | 成片上传、原图与精修分层交付、下载权限与有效期 |
| 互动与评价 | 站内消息、订单评价、作品点赞与收藏 |
| 后台管理 | 审核管理、数据统计、内容风控 |
三、技术选型
3.1 后端技术栈
- Spring Boot 3.x:快速构建 RESTful 接口,内置 Tomcat 与自动装配。
- Spring Security + JWT:实现无状态认证与基于角色的接口鉴权。
- MyBatis-Plus:简化单表 CRUD,配合分页插件实现列表查询。
- MySQL 8.0:存储用户、订单、档期等核心业务数据。
- Redis:缓存热门摄影师列表、存储登录令牌与档期锁。
- MinIO / 阿里云 OSS:存储作品图片与成片文件,支持预签名 URL 下载。
3.2 前端技术栈
- Vue 3 + TypeScript:使用组合式 API 组织页面逻辑。
- Vite:开发与构建工具,提供快速热更新。
- Pinia:状态管理,保存用户信息与购物车式预约数据。
- Vue Router:路由与权限守卫。
- Element Plus:后台管理与表单组件。
- Axios:统一请求封装、拦截器处理 Token 与错误提示。
四、系统架构设计
系统采用前后端分离架构,前端通过 Nginx 部署静态资源,后端以 Spring Boot 提供接口服务,文件资源独立存储于对象存储中。
flowchart LR A[Vue3 前端] -->|HTTP/JSON| B[Nginx 反向代理] B --> C[Spring Boot 应用] C --> D[(MySQL)] C --> E[(Redis)] C --> F[对象存储 MinIO/OSS] C --> G[支付回调接口]后端按 Controller、Service、Mapper 三层划分,公共能力如鉴权、异常处理、日志、文件上传封装为独立模块,便于复用与测试。
五、数据库设计
核心表设计如下,字段可根据实际业务扩展。
| 表名 | 说明 | 关键字段 |
|---|---|---|
| sys_user | 用户表 | id、username、password、role、avatar、status |
| photographer | 摄影师信息 | id、user_id、intro、city、level、audit_status |
| portfolio | 作品集 | id、photographer_id、title、cover_url、category |
| package | 拍摄套餐 | id、photographer_id、name、price、duration、description |
| schedule | 档期 | id、photographer_id、date、time_slot、status |
| orders | 订单 | id、order_no、user_id、package_id、amount、status、pay_time |
| delivery | 成片交付 | id、order_id、file_url、file_type、expire_time |
| review | 评价 | id、order_id、user_id、score、content |
订单状态建议使用枚举统一管理,例如:待支付、已支付、待拍摄、拍摄中、待交付、已完成、已取消、退款中。
六、核心功能实现
6.1 认证与权限控制
登录成功后由后端签发 JWT,前端将 Token 存入 Pinia 与 localStorage,Axios 请求拦截器统一携带Authorization头。后端通过 Spring Security 过滤器解析 Token,并将角色信息写入安全上下文。
@Configuration @EnableWebSecurity public class SecurityConfig { private final JwtAuthenticationFilter jwtFilter; public SecurityConfig(JwtAuthenticationFilter jwtFilter) { this.jwtFilter = jwtFilter; } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf(csrf -> csrf.disable()) .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/auth/", "/api/portfolio/").permitAll() .requestMatchers("/api/admin/**").hasRole("ADMIN") .anyRequest().authenticated()) .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } }6.2 档期预约与并发控制
档期是典型的稀缺资源,同一时间段只能被一个订单占用。实现时可在 Redis 中对「摄影师 ID + 日期 + 时段」加分布式锁,并在数据库中通过唯一索引兜底,避免超卖。
public boolean lockSchedule(Long photographerId, String date, String slot) { String key = "schedule:lock:" + photographerId + ":" + date + ":" + slot; Boolean ok = redisTemplate.opsForValue() .setIfAbsent(key, "1", Duration.ofMinutes(10)); return Boolean.TRUE.equals(ok); }预约成功后写入订单并更新档期状态,若支付超时则通过定时任务释放档期。
6.3 作品与成片文件上传
作品图片与成片文件统一走对象存储,后端只保存文件 Key。上传接口返回预签名地址,前端直传可减轻服务器压力。
@PostMapping("/upload/presign") public Result<PresignVO> presign(@RequestBody PresignDTO dto) { String objectKey = "portfolio/" + UUID.randomUUID() + "-" + dto.getFileName(); String url = minioService.presignedPutUrl(objectKey, Duration.ofMinutes(15)); return Result.ok(new PresignVO(objectKey, url)); }成片下载需校验订单归属与交付状态,并通过短时效预签名 URL 返回,防止链接被随意传播。
6.4 订单状态流转
订单状态变更集中在 Service 层处理,使用状态机校验合法性,避免出现「未支付却已交付」等异常数据。
public void transit(Long orderId, OrderStatus target) { Orders order = orderMapper.selectById(orderId); if (!order.getStatus().canTransitTo(target)) { throw new BizException("非法的订单状态流转"); } order.setStatus(target); orderMapper.updateById(order); }6.5 前端页面与请求封装
前端按业务拆分为首页、摄影师详情、预约下单、个人中心、摄影师工作台与后台管理六个主要路由区域,通过路由守卫区分角色权限。
import axios from 'axios' import { useUserStore } from '@/stores/user' const request = axios.create({ baseURL: '/api', timeout: 10000 }) request.interceptors.request.use((config) => { const token = useUserStore().token if (token) { config.headers.Authorization = Bearer ${token} } return config }) request.interceptors.response.use( (res) => res.data, (err) => { if (err.response?.status === 401) { useUserStore().logout() } return Promise.reject(err) } ) export default request七、关键难点与解决方案
- 档期并发冲突:Redis 分布式锁 + 数据库唯一索引双重保障,支付超时自动释放。
- 大文件交付:前端直传对象存储,后端仅签发预签名 URL,避免占用应用带宽。
- 成片版权保护:下载链接短时效、绑定订单、限制下载次数,并记录下载日志。
- 状态一致性:订单状态机统一校验,支付回调做幂等处理。
- 图片加载性能:列表页使用缩略图与懒加载,配合 CDN 加速。
八、部署与运行
推荐使用 Docker Compose 一键启动依赖服务,前端构建产物交由 Nginx 托管,后端打包为可执行 Jar。
version: "3.8" services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: photo_platform ports: - "3306:3306" redis: image: redis:7 ports: - "6379:6379" minio: image: minio/minio command: server /data --console-address ":9001" environment: MINIO_ROOT_USER: admin MINIO_ROOT_PASSWORD: admin123 ports: - "9000:9000" - "9001:9001"启动顺序建议为:数据库与中间件 → 后端服务 → 前端静态资源。生产环境需配置 HTTPS、数据库备份与日志采集。
九、总结与展望
本文围绕摄影约拍与作品交付场景,完成了从需求分析、技术选型、数据库设计到核心功能实现的整体方案。基于 Spring Boot + Vue3 的前后端分离架构,能够较好地支撑档期预约、订单流转与文件交付等关键业务。
后续可继续扩展的方向包括:接入在线支付与电子合同、增加 AI 智能选片与修图建议、构建摄影师信用体系,以及引入消息推送提升订单沟通效率。