汉服租赁这几年在高校里热度一直很高很多学校的汉服社、动漫社都有活动用衣的需求但自购一整套少则几百多则上千学生很难承受。所以“高校汉服租赁平台”这种项目在毕设选题里越来越常见它业务链路完整、技术栈主流、需求场景清晰很适合拿来作为Java Web方向的综合练手项目。这篇博文我就以这套SpringBoot Vue的高校汉服租赁网站为线索完整拆解它的设计思路、数据库建模、核心功能实现、前端搭建以及部署过程容易踩的坑。不管你是想拿来做毕设、学习前后端分离开发还是准备系统性地过一遍企业级开发流程这篇内容都可以直接参考。1. 项目整体设计与思路拆解1.1 先搞清楚汉服租赁的业务到底是什么拿到这类“某某租赁平台”的题目第一步永远不是写代码而是把业务角色、核心流程、状态变化理清楚。高校汉服租赁平台的核心角色有三种普通学生用户、平台管理员、超级管理员有的系统还会拆出“店铺运营”角色但学生毕设一般不需要过度设计。核心业务闭环是这样的学生注册登录浏览汉服列表可以通过分类、风格、价格区间筛选查看某套汉服的详情包括尺码、库存、租赁价格、押金、图片等。学生选择租赁起止日期提交租赁订单系统计算总价 每日租金 × 天数 押金。订单生成后学生模拟支付毕设阶段通常直接对接一个模拟支付接口或者直接标记已支付。平台管理员在后台查看订单进行“确认发货/确认取衣”操作也可以手动处理归还、验收、退还押金等线下流程。学生确认收货、使用完毕、归还服装管理员验收通过后订单状态变更为已完成同时恢复库存。双方可以互相评价学生端有个人中心查看历史订单、收藏的汉服、个人信息。这个流程看起来简单但真正的复杂度藏在订单状态流转和库存扣减上。比如一套汉服有S、M、L三个码库存可能各只有一两套一个订单可能包含多套汉服归还时还可能存在尺码错乱、破损扣款这些边缘情况。毕设要做到“能答辩、能演示、逻辑自洽”至少要把主流程做完整边缘情况能用状态机和字段冗余来承接。1.2 为什么是SpringBoot Vue这套组合现在高校Java Web方向的毕设SpringBoot Vue已经成了绝对的主流组合替代了十年前经典的SSM JSP。原因很实在SpringBoot让后端开发不再折腾配置。不用手动配一堆XML内嵌Tomcat跑起来就是一个jar包。这对学生来说极大降低了环境搭建成本也让答辩时的运行演示更省心。Vue天然适合做管理后台和学生端这类交互密集型页面。组件化开发、数据驱动视图、路由单页切换体验上比JSP套模板好太多。Element UI提供现成的表格、表单、弹窗、日历组件管理后台可以直接“组装”出来。前后端分离符合行业现状。将来去公司实习大概率也是这种模式提前熟悉接口联调、跨域处理、Token鉴权这一套比死磕JSP有价值得多。毕设评分和面试有话题点。SpringBoot Vue的系统可聊的点实在太多了拦截器怎么写的、JWT怎么签发的、跨域怎么解决的、数据库索引怎么设计的、事务怎么控制的每一个都能在答辩和面试时展开。当然也有一些争议比如有人会问“毕设是不是直接找个开源项目改改就行”。我的看法是选型和模仿开源项目不丢人但你必须真正跑通全链路流程能说清楚每一个关键设计的原因。别人问“订单超卖怎么处理的”“Token过期了前端怎么跳转的”如果你答不上来那这个项目对你来说就只是浪费了时间。1.3 功能模块划分与核心机制设计一个完整可答辩的高校汉服租赁系统至少要包含以下功能模块模块核心功能关键点用户中心注册、登录、个人信息、密码修改加密存储密码、登录后返回Token汉服管理汉服发布、分类、库存维护、上下架多规格库存、图片上传、逻辑删除租赁订单提交订单、支付、取消、归还、退款状态机设计、事务控制库存评论管理用户对汉服和商家的评价、追评订单完成后才能评价公告消息平台公告、订单通知站内信或简单列表后台管理数据看板、订单审核、用户管理、汉服维护权限拦截、操作日志这里的核心机制一个是Token鉴权一个是库存与订单的事务一致性。Token鉴权我选择的是JWT无状态、跨域友好、前后端联调简单库存控制选择的是数据库行锁事务保证并发下不会超卖。这些在后面的章节里逐个展开。2. 数据库设计与核心模块建模2.1 用户角色设计的三种方案直接选最省心的很多人一上来就建“用户表”“角色表”“权限表”三张表做标准RBAC精神可嘉但对毕设系统来说是过度设计。高校租赁平台的角色其实是写死的就三种STUDENT、ADMIN、SUPER_ADMIN。我见过三种做法单表加role字段最简单但扩展性差比如以后要区分“商家”和“普通用户”就得改表结构。用户表 用户角色表支持一个用户多角色但前期查询要join代码量略多。用户表加role字段配合Spring Security的GrantedAuthority兼顾简单和扩展。我的建议是直接采用“用户表加role字段”然后在后端用拦截器做角色判断。对毕设来说一个用户一个角色已经够了硬上RBAC反而显得为了复杂而复杂。用户表大概长这样CREATE TABLE user ( id bigint(20) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 登录账号, password varchar(100) NOT NULL COMMENT BCrypt加密后的密码, real_name varchar(20) DEFAULT NULL, phone varchar(20) DEFAULT NULL, role varchar(20) NOT NULL DEFAULT STUDENT COMMENT 角色, avatar varchar(255) DEFAULT NULL, status tinyint(4) NOT NULL DEFAULT 1 COMMENT 账号状态1启用 0禁用, create_time datetime NOT NULL, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;注意几个细节密码不能明文存必须BCrypt加密。Spring Security自带BCryptPasswordEncoder直接用。username必须唯一索引这是登录的基础。状态字段status必须有。不然封禁用户没法实现答辩时老师一问“用户违规怎么处理”就卡住了。所有表我都统一用utf8mb4因为汉服名称、评价内容里很可能有emoji表情utf8mb4才能存。2.2 汉服与租赁库存建模数量冗余是有原因的汉服表设计是业务核心之一比普通的商品表多了“租赁属性”。我的设计包含基础信息表名称、分类、风格齐胸襦裙、明制、宋制等、颜色、适用性别、尺码区间、图片url、描述、每日租金、押金。库存表每个尺码对应一个库存数量。为什么不直接在汉服表里存“总库存”而是要拆出库存表因为租赁是按尺码来确认的一套汉服可能S码被租走了M码还有货。如果只存总库存下单时就无法精确校验尺码可用性。所以库存表要记录clothing_id、size、stock、locked_stock。这里我加了一个locked_stock锁定库存字段用途是用户下单后但未支付的订单需要先锁定库存防止别人在这段时间内抢走。超过支付时间未支付则自动解锁。这在电商系统里叫“预占库存”它的好处是防止了“超卖”。原理很简单扣减库存时不是直接减stock而是stockstock-1, locked_stocklocked_stock1支付成功后再把locked_stock减掉取消订单则反过来归还。用SQL来表达UPDATE clothing_stock SET stock stock - 1, locked_stock locked_stock 1 WHERE clothing_id ? AND size ? AND stock 0;这条SQL天然带了stock 0的乐观条件并发下同一尺码只有一条记录能更新成功所以行锁解决了超卖。这个设计在答辩里非常加分因为它是真正考虑过并发场景的。2.3 订单表与订单明细表为什么必须拆两张表一个租赁订单可能包含多套汉服如果只建一张订单表把多套汉服塞进一个字段后续查询、统计、状态更新都会痛苦无比。因此必须做标准的一对多订单主表rental_order 订单明细表order_item。订单主表存关键流转信息CREATE TABLE rental_order ( id bigint(20) NOT NULL AUTO_INCREMENT, order_no varchar(32) NOT NULL COMMENT 订单编号, user_id bigint(20) NOT NULL COMMENT 下单用户, total_rent_fee decimal(10,2) NOT NULL COMMENT 总租金, deposit decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT 总押金, start_date date NOT NULL COMMENT 租赁开始日期, end_date date NOT NULL COMMENT 租赁结束日期, status tinyint(4) NOT NULL COMMENT 状态0待支付 1待取衣/待发货 2租赁中 3待归还确认 4已完成 5已取消, create_time datetime NOT NULL, pay_time datetime DEFAULT NULL, return_confirm_time datetime DEFAULT NULL, remark varchar(255) DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT租赁订单主表;订单明细表存下单时的快照信息CREATE TABLE order_item ( id bigint(20) NOT NULL AUTO_INCREMENT, order_id bigint(20) NOT NULL, clothing_id bigint(20) NOT NULL, clothing_name varchar(100) NOT NULL COMMENT 汉服名称快照, size varchar(10) NOT NULL, rent_fee decimal(10,2) NOT NULL COMMENT 租赁时单价, deposit decimal(10,2) NOT NULL COMMENT 单件押金, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单明细表;为什么明细表里要冗余clothing_name、rent_fee这些快照字段因为汉服信息后续可能修改比如名称改了、租金涨了但历史订单必须保持下单时的原样。这也是正规电商系统的标准做法做毕设时加上这个设计答辩的时候能解释出SQL反规范化的必要性与冗余字段的取舍逻辑老师对你的评价会高很多。2.4 订单状态机把租借流程变成有限状态集合状态机是租赁系统最容易做烂的地方。有些人用字符串随便存状态代码里散落着各种if-else判断后面改一个状态都不知道要动多少地方。我强烈建议用整数枚举 统一状态机校验。我的状态定义是状态值含义下一步允许的状态0待支付已支付、已取消1已支付/待取衣租赁中、已取消平台处理2租赁中待归还确认3待归还确认已完成4已完成无5已取消无在代码里我把状态流转抽成一个工具类或者在Order实体里加一个transitionTo()方法任何修改状态的操作都必须走这个方法方法内部校验“当前状态是否允许跳转到目标状态”。这样做的最大好处是状态流转逻辑集中管理不会出现“待支付订单被直接改成已完成”这种脏数据。要知道租赁业务的“归还确认”不只是改状态还涉及库存恢复——只有确认归还后对应尺码的库存才真正加回去。所以归还确认这个动作必须在一个事务里同时做两件事更新订单状态 恢复库存。这个事务性要求在代码实现部分会细讲。3. 后端核心功能实现与关键代码3.1 基于JWT的登录鉴权闭环前后端分离架构下Session方案天然吃亏跨域要配Cookie、分布式环境要共享Session存储、移动端不友好。JWT的优势是自包含——服务端签发一个Token里面带着用户id、用户名、角色、过期时间前端每次请求把Token放在请求头里后端只需验签就能确认身份不需要查库。我的JWT实现分三步签发Token登录成功时String token Jwts.builder() .setSubject(String.valueOf(user.getId())) .claim(username, user.getUsername()) .claim(role, user.getRole()) .setExpiration(new Date(System.currentTimeMillis() 30L * 24 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact();不要用默认的HS256弱密钥SECRET_KEY至少是32字节以上的长字符串。过期时间我设为30天方便学生端反复使用管理端可以单独设短一点的过期时间。拦截器验签Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录、注册等白名单接口 if (isWhiteList(request.getRequestURI())) { return true; } String token request.getHeader(Authorization); if (StringUtils.isBlank(token)) { throw new BizException(未登录); } try { Claims claims Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token.replace(Bearer , )) .getBody(); // 将用户信息放入ThreadLocal方便后续代码获取 UserContext.set(claims); } catch (JwtException e) { throw new BizException(Token无效或已过期); } return true; }ThreadLocal存放当前登录用户信息这是我在实际项目里一直在用的做法好处是Service层不需要每个方法都传一个userId参数直接UserContext.getUserId()即可。跨域处理前端跑在http://localhost:8080后端跑在http://localhost:8081必须解决跨域。我选择在后端统一配置CORS注意要处理Authorization这个请求头的跨域放行。Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(Registry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }3.2 租赁下单的库存扣减与事务控制下单是整个系统最核心的接口涉及的操作包括校验用户登录、校验汉服上下架状态、校验租赁日期是否合法、计算费用、生成订单主表、生成订单明细、扣减库存预占。这些操作必须在一个事务里完成否则就会出现“订单生成了但库存没扣”或“库存扣了但订单没生成”的不一致情况。事务控制我用Spring声明式事务Transactional(rollbackFor Exception.class)。注意一个细节Spring事务默认只回滚RuntimeException如果不指定rollbackFor业务异常自定义BizException继承RuntimeException能回滚但某些自定义CheckedException就不行了所以显式加上rollbackFor Exception.class是最保险的。下单接口核心代码逻辑示意Transactional(rollbackFor Exception.class) public OrderVO createOrder(OrderCreateRequest request) { Long userId UserContext.getUserId(); // 1. 查询汉服和库存for update加行锁 ClothingStock stock stockMapper.selectByClothingIdAndSizeForUpdate( request.getClothingId(), request.getSize()); if (stock null || stock.getStock() 0) { throw new BizException(该尺码暂无库存); } // 2. 计算租金和押金 Clothing clothing clothingMapper.selectById(request.getClothingId()); BigDecimal totalDays countRentDays(request.getStartDate(), request.getEndDate()); BigDecimal totalRent clothing.getRentFee().multiply(totalDays); // 3. 生成订单主表 RentalOrder order new RentalOrder(); order.setOrderNo(generateOrderNo()); // ... // 4. 生成订单明细 // 5. 预占库存stock-1locked_stock1 stockMapper.lockStock(request.getClothingId(), request.getSize()); return assembleVO(order); }这里有一个重要的实际经验查询库存并锁行时要显式使用SELECT ... FOR UPDATE而不是依赖后续的UPDATE ... WHERE stock 0。因为下单过程中还要读取汉服信息和计算价格如果库存记录被并发修改行锁会保证当前事务的读取一致性。当然如果只想做乐观控制可以跳过for update直接执行带条件的Update然后用受影响行数判断是否成功。还有一个生成订单号的问题。订单号字段我用了UNIQUE KEY兜底生成规则是时间戳 随机数 用户id后四位。并发下单时先查数据库是否已存在同号如果冲突就重新生成。大厂一般会用分布式ID但毕设系统用这种方式足够了。3.3 定时任务处理“租赁中到期”与“待支付超时”毕设系统也需要一个定时任务模块。我最常加的两个定时任务是扫描超时未支付订单将“待支付”超过30分钟的订单取消并释放预占库存。扫描“租赁中”但超过归还日期的订单标记为“已超期”同时可以给用户发站内消息提醒。管理员在后台能看到超期列表。实现方式非常简单SpringBoot内置Scheduled注解就够了Component public class OrderTimeoutTask { Scheduled(cron 0 0/5 * * * ?) Transactional(rollbackFor Exception.class) public void cancelTimeoutOrders() { ListRentalOrder orders orderMapper.selectTimeoutUnpaidOrders(30); for (RentalOrder order : orders) { order.setStatus(5); orderMapper.updateById(order); // 释放锁定库存 itemService.releaseLockedStock(order.getId()); } } }这个定时任务的作用不只是让系统更完整更重要的是给数据库加上一层兜底保护。如果用户下单后忘了支付库存一直被锁着其他用户就租不了这套汉服整个平台过几天就会“看起来有货但实际全被锁死”。而在毕设答辩时用定时任务来解决这个业务问题是非常好的亮点。3.4 权限控制区分学生端和管理端的接口访问后端接口需要做两级权限控制登录权限 角色权限。JWT拦截器解决了登录权限角色权限则需要一个简单的注解或校验逻辑。Target({ElementType.METHOD, ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME) public interface RequireRole { String[] value() default {}; }在Controller方法上标注RequireRole({ADMIN, SUPER_ADMIN})切面或拦截器里解析注解判断当前用户的角色是否匹配。这个方案实现成本低又能把权限控制点集中在声明式注解上。管理后台的所有写操作接口都加上这个注解学生端的普通接口则不添加。我踩过的一个小坑是管理端接口不能只在前端靠路由守卫隐藏后端接口必须也做权限校验。因为任何人都可以直接用Postman调后端接口如果后端不校验前端只是“看起来隐藏了”实际上是不安全的。毕设系统虽然不面对真实生产环境但这个习惯一定要养成答辩时老师很可能追问这个点。4. 前端Vue项目实现要点4.1 页面架构与路由设计前端我采用Vue 2 Vue Router Vuex Element UI的组合。虽然Vue 3 Vite Element Plus已经成熟但Vue 2的生态资料更多毕设阶段遇到问题搜起来更顺利——不过如果是从零开始且熟悉Vue 3直接用Vue 3也是很好的选择两种方案都能答辩。页面结构上分为两个布局学生端Layout顶部导航栏 侧边栏分类 主内容区。首页汉服推荐列表、轮播图、公告。汉服列表页分类筛选、搜索、分页。汉服详情页尺码选择、日期选择、下单入口、评价列表。个人中心我的订单、我的收藏、个人信息、押金记录。管理端AdminLayout左侧菜单 右侧内容区。仪表盘今日订单数、租赁中数量、营收统计、超期列表。订单管理列表、筛选、详情、发货/确认归还操作。汉服管理列表、新增、编辑、库存管理、上下架。用户管理用户列表、禁用/启用、角色查看。评价管理评价列表、删除违规评论。路由设计时有一个关键点使用路由守卫控制页面访问权限学生端路由和管理端路由分开用meta里的role字段做匹配。router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) return } if (to.meta.role to.meta.role ! localStorage.getItem(role)) { next(/403) return } next() })4.2 Axios封装与环境切换前端接口调用肯定不能到处写axios.get必须统一封装。我的封装里做了三件事基础URL配置根据开发环境自动切换API地址。Vue 2中可以在.env.development和.env.production里配置VUE_APP_BASE_URL。请求拦截器自动加上 Authorization请求头。响应拦截器统一处理后端返回的{code, message, data}结构遇到401且Token过期时跳转回登录页并清除本地状态。service.interceptors.response.use( response { const res response.data if (res.code ! 200) { if (res.code 401) { // Token失效跳转登录 localStorage.removeItem(token) location.reload() } return Promise.reject(new Error(res.message)) } return res.data }, error { return Promise.reject(error) } )这里有一个很容易被忽略的细节Token过期后不能只弹个报错应该自动清理本地状态并引导用户重新登录。不然用户看到一个莫名其妙的“未登录”错误体验很差。4.3 管理后台的CRUD页面用Element UI组装管理后台的订单管理、汉服管理等页面本质都是“表格 筛选 弹窗表单 分页”的组合。用Element UI的el-table、el-pagination、el-dialog、el-form可以快速搭建。我一般会封装一个通用的分页查询逻辑async function fetchPage() { loading.value true const params { pageNum: pageNum.value, pageSize: pageSize.value, keyword: keyword.value, status: statusFilter.value } const res await api.getOrderPage(params) tableData.value res.records total.value res.total loading.value false }这个封装几乎是所有管理后台页面的模板写一次之后后面汉服管理、用户管理、评价管理页面只需要改接口地址和表字段。我发现毕设开发最大的阻力往往不是技术难点而是“重复劳动太多导致烦躁”所以合理封装组件和接口能节省大量时间。4.4 前后端接口联调时的常见问题联调时最大的坑是日期格式。Java后端默认返回的时间格式是yyyy-MM-dd HH:mm:ss但前端传给后端的日期如果是yyyy-MM-dd两者不匹配就会出现日期偏移或解析失败。我的解决方案是全局配置JackSon的日期格式并在接收入参时使用DateTimeFormat(pattern yyyy-MM-dd HH:mm:ss)或直接统一用前端字符串传参后端用LocalDate解析。另外一个常见的坑是Long类型精度丢失。数据库中订单ID是bigintLong如果ID超过16位前端JS会丢失精度导致详情查询时ID不准。这在高并发系统是个经典问题。解决办法是给Long字段加Json序列化注解转成String返回给前端JsonSerialize(using ToStringSerializer.class) private Long id;4.5 租期为跨天时用日期算法计算租赁天数租赁日期的计算直接关系到租金金额。比如用户选择startDate2025-06-01endDate2025-06-03到底是按2天算还是按3天算我的规则是按自然日差1计算也就是取衣当天算一天、归还当天算一天。我用ChronoUnit.DAYS.between(startDate, endDate) 1得到总天数。这个规则要清楚地写进接口文档不然前后端对租金金额有分歧时排查起来非常难受。5. 项目部署运行与毕设答辩准备5.1 本地跑通全流程的完整步骤拿到这套SpringBoot Vue的项目源码后按以下步骤就可以完整运行起来。以下是我积累下来的最稳妥的运行顺序新建数据库执行项目根目录下的sql脚本按文件名顺序导入。脚本里已经包含了建库、建表、初始数据。注意数据库名和用户名密码要与后端application.yml配置一致。配置后端打开application-dev.yml修改数据库连接串、Redis连接如果项目用到以及JWT密钥。启动SpringBoot应用看到Tomcat端口8081启动成功即为后端就绪。启动前端执行npm install网络慢可以换镜像源然后npm run serve看到App running at http://localhost:8080。验证登录用脚本里自带的初始账号登录比如管理员admin / 123456普通学生student / 123456。前端页面能显示汉服列表和管理后台入口说明前后端联调成功。走一遍核心流程学生端提交订单 - 模拟支付 - 管理员端看到订单 - 确认发货 - 确认归还 - 查看库存恢复。这五步走完主流程就跑通了。我就遇到过同学把后端配在了8080端口前端默认请求也是8080结果前端一直请求到后端Tomcat首页显示一串JSON错误。所以不要想当然前端端口和后端端口必须区分开并且前端Axios的baseURL必须指到后端的端口而不是HTML地址。5.2 部署过程的十大经典异常排查实录这里整理一份高频问题速查表都是实际运行时最常见的坎现象原因解决方案前端请求后显示302/401Token未正确传递或白名单没配检查Axios拦截器是否带了Authorization头检查后端白名单路径是否匹配后端起不来端口被占用netstat -ano查看占用进程改掉application.yml里的server.port导入SQL报错客户端字符集不是utf8mb4在连接URL里加characterEncodingutf8且用utf8mb4执行脚本中文乱码前端页面文件本身不是UTF-8统一IDE的文件编码为UTF-8跨域报错前端端口和后端端口不一致且后端没配置CORS确认CorsConfig生效且前端proxy或axios baseURL正确上传图片失败目录不存在或没有写入权限先在配置里指定上传目录并手动创建上传后需要Config映射静态资源路径每次重启数据库数据丢失没连自己的库连到了测试库检查datasource.url是不是自己创建的库名登录成功后刷新页面又回到登录页刷新时Vuex状态丢失把用户信息和Token持久化到localStorage订单状态操作后不生效只改了前端页面没调后端接口检查管理端调用的是哪个状态变更接口且返回数据是否正确日期计算租金不一致前后端日期格式不同统一枚举写入文档或后端计算为准前端只展示如果你在运行过程中遇到“数据库密码错误”之类的提示通常不是脚本问题而是application.yml里的密码和本机数据库密码不一致。这类问题还有一个通用排查技巧先把后端日志调到DEBUG级别看清楚报错的完整堆栈再根据堆栈往上找第一行业务代码基本能定位。5.3 答辩时老师最爱追问的五个核心问题毕设答辩不是演示完就没事的老师一定会围绕系统设计深挖。以下五个问题我建议你提前准备好为什么选用JWT而不是传统Session答前后端分离下Session需要处理跨域CookieJWT无状态、可服务端验签、易于扩展适合分布式部署。但JWT有无法主动失效的缺点可通过设置短过期时间黑名单机制弥补。订单并发下单怎么防止超卖答库存扣减使用UPDATE ... WHERE stock 0的乐观锁或SELECT ... FOR UPDATE加行锁配合事务保证原子性。订单取消后库存怎么恢复答订单表状态改为已取消同时遍历订单明细表对每个明细中的尺码执行locked_stock - 1, stock 1操作整个过程在一个事务里保证一致性。管理员和学生的权限是怎么控制的答后端通过JWT中的角色字段 RequireRole注解实现接口级权限控制前端通过路由守卫控制页面入口但核心安全以后端为主。如果用户租了汉服不归还怎么办答有定时任务每天扫描超期订单标记状态并发送站内提醒管理员也可以在后台看到超期列表并线下处理。建议在答辩前自己给自己提问“如果我是老师我会怎么为难我”。想一想每个模块出了问题会怎样比如“用户恶意下很多单不支付”“管理员误操作把未归还订单改为已完成”这些都是可以深入展开的追问。6. 源码结构、接口文档与二次开发扩展方向6.1 拿到源码后的目录结构导读不管你是把这份源码作为毕设主体还是作为学习样例第一步都是看目录结构不要一上来就双击运行。合理的目录结构应该是这样的hanfu-rental ├── backendSpringBoot后端 │ ├── src/main/java/com/example/hanfu │ │ ├── controller接口层 │ │ ├── service业务逻辑层 │ │ ├── mapper数据访问层 │ │ ├── entity数据库实体 │ │ ├── dto入参出参对象 │ │ ├── config配置类CORS、拦截器、WebMvc │ │ ├── common公共类统一返回结果、异常处理、枚举 │ │ └── util工具类JWT、订单号生成、日期工具 │ ├── src/main/resources │ │ ├── application.yml │ │ ├── mapper/*.xml如使用MyBatis XML方式 │ │ └── db或根目录sql/ ├── frontendVue前端 │ ├── src │ │ ├── api接口请求封装 │ │ ├── router路由配置 │ │ ├── storeVuex状态管理 │ │ ├── views页面组件 │ │ ├── components公共组件 │ │ └── utils工具 │ ├── public │ └── package.json └── docs含接口文档、部署文档、PPT提纲看目录时重点关注三点统一返回结果的封装是否规范、异常处理是否集中、接口文档是否和实际代码一致。如果源码里这三块都做得整齐说明项目质量有保障二次开发省心得多。6.2 接口文档的定位不只是给前端看的接口文档是这个项目里容易被忽视却很重要的资产。对于前端开发者来说没有接口文档就只能去看Controller源码猜请求参数效率极低。对于后端作者自己来说接口文档其实也是一次“需求复核”——你写文档时发现某个字段缺失、某个接口不知道该返回什么结构说明设计还没有闭环。一个高质量接口文档至少要包含以下内容接口路径、请求方式。请求头要求是否必须带Token。请求参数说明字段名、类型、是否必填、示例值。返回结果示例和错误码说明。关键业务流程的接口调用顺序比如“下单流程先调库存查询再调创建订单最后调支付接口”。我常用的工具是Swagger/SpringDoc自动生成在线文档但毕设项目如果不是很复杂也可以用Markdown静态文档。一个额外的建议把接口文档放到项目根目录的docs文件夹和SQL脚本一起提交。将来你再次打开这个项目或者给别人讲解时不用再费劲回忆“这个接口返回的数据结构到底是什么”。6.3 后续扩展方向从毕设到“可演示的完整平台”如果时间充裕或者你想让这个项目更接近一个真实商业系统以下扩展方向性价比很高对接支付宝沙箱支付学生提交订单后通过沙箱环境完成模拟真实支付流程订单支付回调会自动更新订单状态。这是最保险的亮点之一。增加服饰试穿AR预览技术上可以用简单的人体关键点模型帮用户虚拟试穿汉服。这个对毕设来说可能偏复杂但如果你们团队有算法方向的同学会非常加分。接入微信公众号消息通知归还提醒、订单状态变化通过模板消息推送给学生。主要实现成本在申请测试号、配置接口上但演示效果很好。增加数据统计图表管理后台用ECharts展示汉服租榜、每日营收、用户增长趋势。这个实现简单却能让整个系统的完成度看起来提升一个档次。我之前指导的同学在这个项目基础上加了ECharts仪表盘答辩时评委明显更有兴趣问了好几个关于“租榜排序算法”和“营收统计口径”的问题。所以哪怕只加一个统计页面也要提前想好其中的业务口径比如“营收是已支付订单的总额还是已完成订单的总额”这类问题很能体现你有没有真正理解自己的业务。最后的实际操作体会做了这么多年的Java Web项目我最大的感受是毕设项目不在于“技术多高级”而在于是不是真的把一条完整业务链路跑通了。SpringBoot Vue这套组合之所以常年“霸榜”本质上是因为它把后端开发的复杂配置降到最低把前端交互的开发效率提到最高让学生能把精力放在业务逻辑上。这套高校汉服租赁平台最让我满意的地方在于它的业务场景非常生活化学生自己就是目标用户做起需求分析来有代入感同时它的技术点又不浅JWT鉴权、行锁防超卖、订单状态机、定时任务、前后端分离联调每一个都是工作中真实会用到的东西。如果你准备拿它做毕设或者想通过它来建立自己的Java Web全栈知识体系我建议你重走一遍这个流程先设计数据库再写后端接口再搭前端页面最后联调部署。哪怕中间多踩些坑也值得因为这些坑的经验价值远远超过最终那份“运行通过”的成就感。最后再分享一个小技巧项目里所有日期时间字段统一用create_time、update_time这种命名并让MyBatis-Plus或自定义MetaObjectHandler自动填充。不要每张表都手写插入时间。这样既能保证数据完整性又能在答辩时说一句“插入和更新操作不用手动管理时间字段”很小的优化点却显得很专业。
阅读完成 · 觉得有帮助?