这个题目拆出来其实远不止“一个商城”这么简单。非遗交流交易平台本质上是把电商前台、内容社区、后台运营管理系统压在一个 Spring Boot 单体项目里。说白了就是你要同时回答三个问题用户凭什么在这个平台上买卖非遗文创手艺人和工作室怎么展示自己的作品平台方又靠什么把内容管起来、把交易捋顺我见过不少同学拿到这类题目直接开写结果写到订单模块就卡壳因为前面商品、用户、社区的表结构互相串了。所以我这篇分享不讲虚的就把这个题目从角色边界、技术选型、核心模块设计到部署调试的完整链路拆给你看适合正在准备毕设、又没什么真实项目经验的人参考。1. 动工前先想清楚角色边界这是非遗平台能否拿高分的地基咱们先把代码放到一边用十分钟把项目里“谁在用”这个问题想透。非遗交流交易平台常见的角色至少有三类普通用户、非遗商家或者叫传承人、工作室、平台运营管理员。如果再把系统管理拆细一点还可以有一个超级管理员来处理管理员本身的账号和维护但毕设一般不需要做这么重。1.1 四类角色和它们的权限范围你可以直接在用户表里加一个role字段用 0/1/2/3 这种数字来表示角色没必要一上来就引入完整的 RBAC 权限框架。因为你的重点是把业务功能做完而不是把权限框架研究得多透彻。但是权限必须做到“后端校验”绝不能只在前端把按钮藏起来。角色能做的事需要考虑的接口/表普通用户浏览商品、下单支付、发帖评论、点赞收藏、申请成为商家cart_item、order_info、post、comment、like_record非遗商家普通用户能力 发布/上下架非遗商品、处理订单发货product、product_sku、order 发货接口运营管理员内容审核、分类维护、商品审核、订单售后处理post 审核、product 上下架、order 状态修改超级管理员管理员账号管理、平台数据统计user 管理、dashboard 统计接口非遗商家不能简单等于“注册用户”。因为涉及“传承人身份”很多平台会设计一个认证流程用户提交名称、简介、所属非遗门类、代表作品图片管理员审核通过后才成为商家。如果不做这个认证答辩时老师一句“商城谁都能开店吗”就容易把你问住。你可以加一张artist_auth表或者干脆在用户表里加一个auth_status字段0 未申请、1 待审核、2 已通过、3 已驳回都是很成熟的简化做法。1.2 后端接口怎么划分才不会越写越乱统一接口前缀是单体项目最实用的规范。我自己的习惯是/api/user/**登录后的用户操作比如购物车、订单、个人资料/api/product/**商品浏览公开可访问/api/community/**帖子、评论、点赞/api/pay/**支付和回调/api/admin/**运营管理后台专用必须做管理员角色校验/api/auth/**登录、注册、刷新 token。这样划分的最大好处是拦截器可以按路径做权限控制。比如配置一个 WebMvc 拦截器/api/admin/**必须要求role 3/api/user/**要求登录。否则你把用户模块和管理员接口混在一起后面想加权限拦截反而要逐个接口去比对非常痛苦。另一个容易被忽略的点是所有接口的返回结构要统一。我建议设计一个ResultT{ code: 0, message: ok, data: {} }前端拿到固定的结构处理异常和弹窗提示会非常干净。code 为 0 表示成功其他值表示各类业务错误比如 401 未登录、403 无权限、500 系统异常。很多毕设项目用着用着就出现有的接口返回String、有的返回Map前端逻辑写成一堆 if else这就是典型的“地基不牢”。2. 技术底座选择Spring Boot 前后端分离的关键判断很多同学问这个题目会不会用 SSM 更简单我的观点是既然题目已经明确写了 Spring Boot那就老老实实用 Spring Boot而且还要能解释清楚“它到底比 SSM 方便在哪”。2.1 Spring Boot 解决了什么问题SSM 是 Spring Spring MVC MyBatis 的手动整合麻烦的点在于大量的 XML 配置、jar 包版本冲突、Tomcat 部署方式。Spring Boot 的出现把这些繁文缛节干掉了内嵌 Tomcat、自动配置、Starter 一键引入依赖。你写一个 controller直接跑main方法就能看到效果这对毕设开发效率的提升是巨大的。版本上要注意一个很现实的坑Spring Boot 2.x 用的包名是javax.servletSpring Boot 3.x 改成了jakarta.servlet。如果你的 JDK 是 8那就老老实实用 Spring Boot 2.7 系列如果学校机房的 JDK 已经是 17 甚至 21那就用 Spring Boot 3.x。这个组合在初始环境检查时就要确认好不然后续可能会因为依赖导入报错浪费大量时间。常用依赖大概是这些spring-boot-starter-web spring-boot-starter-validation mybatis-plus-boot-starter mysql-connector-runtime spring-boot-starter-data-redis sa-token-spring-boot3-starter 或者 jjwt hutool-all springdoc-openapi-starter-webmvc-uiMyBatis-Plus 非常推荐它帮你把单表 CRUD 写好了你只需要把精力放在订单、库存、审核这些真正的业务逻辑上。有的框架会为了追求“真实感”引入很多依赖我个人不太建议因为毕设最重要的是你能把每个依赖的作用讲明白而不是堆一堆自己都没跑通的库。2.2 前端怎么选Vue 3 还是服务端渲染非遗文创商城如果全部用 Thymeleaf 做服务端渲染也不是不行但商城页面需要大量的异步交互加购物车、榜单刷新、搜索筛选用服务端渲染会很别扭。我更推荐 Vue 3 Element Plus Vite 做管理后台和商城前端前后端通过 JSON 交互。前后端分离不是说你必须建两个仓库而是“后端只返回 JSON前端只请求接口”。你可以用 Vite 起一个前端开发服务器本地联调时把/api代理到localhost:8080生产环境把 dist 打包后扔到 Nginx 或直接放进 Spring Boot 的static目录。毕设演示时我更建议用同一个 Spring Boot 进程把后端和前端静态文件一起服务部署起来少一套环境也省去很多跨域问题。如果你对前端不熟也可以买一套现成的后台管理模板或者用 Element Plus 官方组件自己拼页面。核心是你要能说清楚每个页面调用了哪个接口不能只丢一句“这个页面是模板自带的”。2.3 Redis、文件存储这些组件怎么搭MySQL 是绝对的主力存储。Redis 在毕设里通常是加分项常用来做验证码缓存、购物车临时数据、首页热门商品缓存。注意Redis 不是必须的。如果只是想跑通流程购物车完全可以用数据库表实现。如果你决定引入 Redis面试时就要能回答“缓存和数据库不一致怎么办”这种问题哪怕你回答“我采用的是先更新数据库再删除缓存”也比完全不能聊强得多。文件存储方面最简单的办法是把图片上传到本机的一个upload/目录然后通过 Spring Boot 的静态资源映射对外提供访问路径。如果你已经买了云服务器完全没必要额外花钱接对象存储。项目里只需要把上传路径配置到application.yml再写一个WebMvcConfigurer做虚拟路径映射比如访问/files/**实际映射到本地/home/xxx/upload/。这样部署后商城的商品图、用户头像、帖子配图都统一走一个地址规则不会出现前端图片打不开的尴尬。3. 非遗文创商城核心实现从商品建模到订单状态机商城模块是这个题目的大头也是很多同学最容易纠结的地方。纠结来纠结去无非是商品到底有多规格还是没规格、订单状态怎么设计、库存扣减会不会超卖。3.1 商品表从“非遗商品特点”出发非遗文创和普通电商商品的差别在于它有很多手工属性比如产地、工艺、材质、传承人背景。你直接在商品表里加这些字段就行。不过非遗商品往往不会像手机那样有“颜色版本套餐”的大量 SKU 组合大多数是单款少量所以我建议一张product主表加一张product_sku子表就够了甚至很多商家只需要一条 SKU 记录。基础表结构可以参考CREATE TABLE product ( id bigint NOT NULL AUTO_INCREMENT, category_id bigint NOT NULL COMMENT 非遗分类, name varchar(120) NOT NULL COMMENT 商品名称, subtitle varchar(255) DEFAULT COMMENT 副标题/一句话卖点, cover_image varchar(255) DEFAULT COMMENT 封面图, detail text COMMENT 详细图文介绍, origin varchar(100) DEFAULT COMMENT 产地, craft varchar(255) DEFAULT COMMENT 工艺技法, material varchar(255) DEFAULT COMMENT 材质, price_min decimal(10,2) DEFAULT NULL COMMENT 最低价用于列表展示, status tinyint NOT NULL DEFAULT 0 COMMENT 0下架 1上架, seller_id bigint NOT NULL COMMENT 商家用户id, create_time datetime NOT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;price_min的作用是做列表页价格展示实际购买时按选中的 SKU 价格支付。一个商品可能有两个规格普通装和礼盒装礼盒包装更贵一些。这时候product_sku表里就有两条记录下单时前端把skuId传过来后端根据skuId查询价格。商品创建和上下架接口一定要区分角色商家只能操作自己的商品所以UPDATE product SET status? WHERE id? AND seller_id当前用户这个条件不能漏。我见过很多项目只写了where id?结果商家 A 把商家 B 的商品下架了这种细节在答辩现场是很致命的。3.2 购物车和库存扣减别把超卖留到上线才发现购物车我建议直接用数据库表CREATE TABLE cart_item ( id bigint NOT NULL AUTO_INCREMENT, user_id bigint NOT NULL, sku_id bigint NOT NULL, quantity int NOT NULL DEFAULT 1, checked tinyint NOT NULL DEFAULT 1, create_time datetime NOT NULL, UNIQUE KEY uk_user_sku (user_id, sku_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;加购物车时先查唯一键如果已存在就累加数量不存在就插入新记录。用数据库表实现的好处是后端逻辑一眼就能看懂而且不管用户在网页端还是管理端操作数据都一致。库存扣减是商城项目的经典考点。如果库存量不多直接使用“乐观扣减”就足够update iddeductStock update product_sku set stock stock - #{quantity} where id #{skuId} and stock #{quantity} /update这个方法的关键在于 SQL 里的stock #{quantity}条件它能保证库存不足时 update 受影响行数为 0程序借此抛异常回滚事务。你不需要用 Redis 分布式锁那些花活在本科毕设范围内能把这条 SQL 讲清楚已经是相当加分的认知水平。创建订单的流程应该是创建订单主记录 → 插入订单明细 → 扣减 SKU 库存 → 清空购物车对应商品。这四个操作必须放在同一个Transactional方法里任何一个失败都要回滚。尤其要注意库存扣减出现问题时不能让订单记录残留下来。3.3 订单状态一张状态迁移图胜过大段文字订单模块最忌讳把状态写死成字符串比如“待付款、已付款、已发货”这样直接塞到数据库。用一个订单状态枚举来管理更稳常见状态状态码含义可进入的后续状态0待付款1已付款、4取消1已付款/待发货2已发货、5退款中2已发货3已收货、5退款中3已完成无4已取消无5退款/售后处理3退款完成订单表字段至少包括order_no、user_id、total_amount、pay_amount、status、receiver_name、receiver_phone、receiver_address、pay_time、ship_time、finish_time、create_time。订单号生成建议用“日期 用户ID后四位 随机数”的组合长度控制在 20 位以内尽量避免使用数据库自增 ID 作为对外订单号。未支付订单超时关闭功能毕设里可以做一个定时任务每 30 分钟扫一遍 30 分钟前创建且仍未支付的订单将其状态改为取消并回滚库存。注意定时任务里要用create_time now - 30min的判断同时只处理状态为 0 的订单。如果还想再严谨一点就把超时时间做成配置项。4. 非遗交流社区功能设计发帖、评论、点赞和内容审核落地非遗交流社区如果做得太复杂很容易把自己拖死。核心功能就是发帖、评论、点赞、收藏、后台审核。没有互动数据的社区是没有意义的所以页面上至少要能看出“这个帖子有多少人看过、多少人赞过”。4.1 帖子与评论的数据库结构帖子表可以这样设计字段说明id主键author_id发帖用户category_id所属非遗门类如刺绣、陶瓷、剪纸title标题content正文支持纯文本或富文本cover_image封面图view_count / like_count / comment_count冗余计数status0 待审核、1 已发布、2 已驳回audit_remark驳回原因create_time / update_time时间戳评论表不需要设计得无限层级。非遗交流的场景下一级评论加二级回复完全够用parent_id为 0 表示顶级评论否则表示对某条评论的回复。查询评论时按帖子 ID 和 parent_id 查两次第一次查顶级评论第二次把回复按 parent_id 聚合进去即可。内容展示时最重要的一个 SQL 条件是不能漏掉的所有用户端接口只查status 1的帖子。后台审核时才能看到待审核数据。如果你在帖子发布后直接返回“发布成功”前端也能立刻看到那审核就形同虚设了。4.2 点赞、收藏这些动作怎么保证不重复用户对帖子点赞这是一个高频操作。为了不做成“点一次赞点赞数 1再点一次又 1”的样子必须有唯一约束CREATE TABLE post_like ( id bigint NOT NULL AUTO_INCREMENT, user_id bigint NOT NULL, post_id bigint NOT NULL, create_time datetime NOT NULL, UNIQUE KEY uk_user_post (user_id, post_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;点赞接口的逻辑是先尝试插入post_like如果插入成功执行update post set like_count like_count 1 where id ?如果插入抛出重复键异常则执行删除并让点赞数减一。由于唯一键约束的存在你不需要先查再插也不容易出现并发下重复点赞。收藏功能可以复用一模一样的方案只是表名和字段换一下。热门帖子的排序可以在列表查询里直接按一个加权分数排序比如order by (like_count * 2 comment_count * 3 view_count * 1) desc对毕设来说完全够用比引入一套推荐系统实在得多。4.3 内容审核别做形式主义的审核按钮审核功能如果只是让管理员看到一个按钮点一下状态变一下那是没有灵魂的。至少要包含两部分一是管理员能看到待审核列表和帖子详情二是审核通过或驳回时能填写理由驳回后用户端能看到“您的帖子未通过审核原因……”敏感词方案不需要上多么高深的技术。一张sensitive_word表启动时加载到内存发帖时用循环contains校验发现命中就提示“内容包含敏感词”。虽然性能不高级但逻辑清晰。如果想做得更像样一点也可以用 Trie 树或String.indexOf逐个检查重点是你能够解释清楚而不是令人眼花缭乱地复制一段百度来的 AC 自动机。5. 支付环节不要贪多模拟支付加回调把闭环走通毕设里的支付往往是老师最爱追问的部分。很多同学第一反应是接真实支付接口结果卡在商户号申请、营业执照、回调配置一周过去了连支付页面都没看到项目还整得一团糟。5.1 为什么建议用“模拟支付”我的建议很直接做一个模拟支付通道就够了但要把支付流程设计得像真实支付一样完整。你可以在支付页面上展示一个“模拟支付”按钮点击后直接调用后端/api/pay/mock后端根据订单号校验订单状态然后生成一个支付流水号把订单状态从待付款改为已付款。这个过程中要体现三件事用户只能支付自己的订单订单必须处于待付款状态同一笔订单重复支付不会重复改状态。这其实已经模拟了真实支付回调的核心逻辑。答辩时老师关心的是你有没有“幂等”的概念而不是你有没有真正接沙箱。5.2 支付回调的幂等怎么写假设你有一个payment_record表记录了支付单号、订单号、用户ID、金额、支付状态。模拟支付接口的代码核心逻辑可以这样写OrderInfo order orderInfoMapper.selectOne( new LambdaQueryWrapperOrderInfo() .eq(OrderInfo::getOrderNo, orderNo) .eq(OrderInfo::getUserId, currentUserId) ); if (order null) { throw new ServiceException(订单不存在); } if (order.getStatus() ! OrderStatusEnum.UNPAID.getCode()) { // 已经支付过或已取消直接返回不重复操作 return 支付结果订单状态已变更忽略重复回调; } // 模拟第三方回调先生成支付流水再更新订单状态 PaymentRecord record new PaymentRecord(); record.setOrderNo(orderNo); record.setUserId(currentUserId); record.setAmount(order.getPayAmount()); record.setStatus(1); paymentRecordMapper.insert(record); OrderInfo update new OrderInfo(); update.setId(order.getId()); update.setStatus(OrderStatusEnum.PAID.getCode()); update.setPayTime(LocalDateTime.now()); orderInfoMapper.updateById(update);这里面的关键是在更新订单状态之前要先判断当前状态是不是“待付款”。如果状态已经不是待付款说明重复回调已经处理过了直接忽略。这才是支付回调幂等的最朴素写法。财务上还有一个小细节金额计算要用“分”存数据库整数字段不要用double否则累计对账会出现几千分钱的误差。如果表里用了decimal(10,2)程序里也要用BigDecimal运算不能直接拿浮点数计算。5.3 真要接真实支付至少想清楚这两个问题如果老师对支付要求特别高比如必须走真实支付工具你至少要了解沙箱环境和回调验签。真实支付接入时后端需要配置商户私钥支付平台回调时会带上签名你需要用平台公钥验签确认这个回调不是我伪造的再处理订单。毕设里很多人会把回调地址设成本地localhost真实支付平台根本不可能回调到你的电脑这就是“看着代码没问题实际上永远没法跑通”的典型案例。所以我的经验是如果决定接真实支付就把服务部署到云服务器上回调地址写服务器的公网接口调试信息看日志。如果只想做演示模拟支付完全足够而且能节省大量时间。6. 管理后台和运营能力让它能支撑“治理”而非摆设管理后台做得好不好直接决定这个项目能不能从“一堆 CRUD”变成“一个平台”。非遗交流交易平台的管理后台至少要有商品管理、订单管理、用户管理、社区管理、分类管理五个 Tabs再加一个数据统计首页。6.1 分模块看后台都要管什么商品管理商家提交新商品后管理员可以上下架也可以直接下架违规商品还能修改商品分类。这里注意商品状态的下架操作并不删除数据所以商品表要有逻辑删除或状态字段不能一删了之。订单管理管理员只能看不能改价格这一点千万别做成“谁都能改金额”。管理员可以查看所有订单处理售后申请把退款中的订单标记为退款完成。建议后台订单列表里展示“买家联系电话”和“收货地址”方便演示和说明。用户管理列出全部用户支持禁用/启用账号。禁用的用户登录时会被拦截器拒绝这个逻辑要真正在登录接口和鉴权拦截器里生效不能只是改个字段骗自己。社区管理审核帖子是每天都会用到的功能管理员列表里要分三个页签待审核、已通过、已驳回。驳回时要填原因已通过的帖子也能下架处理。如果有举报功能把举报列表也放进来但毕设不一定需要视你的进度而定。分类管理非遗门类分类用一个树形结构表parent_id为 0 表示顶级分类比如“传统技艺”“传统美术”下面挂“陶瓷烧制”“刺绣”“剪纸”等具体门类。前端商城首页的分类菜单和后台分类维护共用同一张表。6.2 统计报表别做成花架子在后台首页做一个简单看板展示商品总数、用户总数、累计订单数、累计成交额建议再画几个趋势图。趋势图不需要自己画前端用图表库后端返回数据即可。统计 SQL 可以用日期分组SELECT DATE_FORMAT(create_time, %Y-%m-%d) AS stat_date, COUNT(*) AS order_count, SUM(pay_amount) AS total_amount FROM order_info WHERE status IN (1, 2, 3) AND create_time DATE_SUB(CURDATE(), INTERVAL 30 DAY) GROUP BY stat_date ORDER BY stat_date ASC;注意这里统计的是“已付款以后”的订单如果把待付款订单也算进去成交额数字会失真。把接口写好后前端拿到数组就能画折线图答辩论“这个图表数据是实时从订单表聚合出来的”比放一个死的静态页面有说服力得多。7. 部署、远程调试以及我从学生项目里看到的坑很多同学开发时一切正常一到部署或演示环境就崩。这里我把最容易踩的坑集中讲一遍至少能帮你在答辩前少熬夜。7.1 从源码到可运行 jar 的部署链路后端打包默认是mvn clean package -DskipTests生成.jar文件放到服务器上。我认为本地环境跑通后直接用java -jar启动并不是最稳的还是要控制一下 profilemvn clean package -DskipTests java -jar platform.jar --spring.profiles.activeprod如果服务器没有图形界面就用nohup让它后台运行日志写到文件里nohup java -jar platform.jar --spring.profiles.activeprod app.log 21 数据库和 Redis 都要配置成服务器本机或云数据库的地址账号的 host 不要限制成localhost。很多人本地跑得很欢一放到服务器上就报数据库连接拒绝大概率就是 MySQL 用户只允许本地连接。前端打包后把dist静态文件替换到 Spring Boot 的static目录或者用 Nginx 托管。演示的时候我比较推荐把前端打进 Spring Boot 的静态目录启动一个进程就完事少一个 Nginx 也少一个故障点。7.2 “远程调试”到底连的是什么这个标题下经常出现“远程调试”这个词但含义有两种得分清楚。一种是“老师/同学远程连你的电脑协助调错”这时候你只需要把项目跑在本地端口开放出去用远程桌面或者会议工具共享屏幕就行。这种方式和代码本身无关重点是网络顺畅。另一种是“IDE 连接到远程服务器上的 Java 进程实时打断点查变量”。这种就需要在服务器启动命令里加上 JVM 调试参数。比如java -agentlib:jdwptransportdt_socket,servery,suspendn,address*:5005 \ -jar platform.jar --spring.profiles.activedev然后在本地 IDE 里新建一个 Remote JVM Debug 配置host 填服务器 IPport 填 5005启动 Debug 后就可以在本地代码里打断点。要注意远程调试只适用于开发阶段不要在生产环境开这个端口因为它会暴露 JVM 的调试通道安全上是隐患。还有一种常见的“远程调试”场景是前端页面在本地而接口在服务器上。这时候你要检查前端 Vite 或 Nginx 的代理配置把/api代理目标从localhost:8080改成服务器 IP:8080。同时服务器安全组要放行 8080 端口不然本地访问不到接口。很多同学卡在这一步以为是代码问题实际上连请求都没发出去。7.3 项目里的高频坑和解决办法我总结了几个反复出现的坑按优先级排个先后顺序注释写在后面第一个是跨域问题。开发阶段前后端分离前端地址是localhost:5173后端是localhost:8080接口被浏览器拦截是常态。解决方法是后端配置全局 CORS但注意两个细节allowedOriginPatterns不能和setAllowCredentials(true)冲突如果生产环境前端和后端同一个域名端口跨域的问题根本不会发生建议部署时直接统一。第二个是上传图片后访问 404。原因通常是上传目录在项目工作目录下重启后路径变化或者文件没找到。解决方式是把上传目录放到一个固定绝对路径比如/data/upload然后通过虚拟路径映射对外访问代码里不要写死相对路径。第三个是 LocalDateTime 序列化成 JSON 格式不对。后端返回的是2025-05-12T10:20:30前端不知道怎么展示导致订单时间、评论时间全都乱掉。最简单的处理是全局配置spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8第四个是 MyBatis-Plus 的逻辑删除和唯一索引冲突。比如post_like表为了保留点赞历史可能加逻辑删除字段但因为有唯一键(user_id, post_id)第一次取消赞再点赞时会违反唯一键。解决方案是点赞历史以物理删除为准或者唯一键改为(user_id, post_id, deleted)。这个坑不遇到很难想到遇到了要能解释清楚。最后一个是改完数据库数据页面总不刷新。如果用了 Redis 缓存商品列表或首页推荐改动数据库后要主动删除对应 key否则旧数据一直留在缓存里。最简单的习惯是代码里凡是“写操作”都顺手删除相关查询缓存先更新数据库再删缓存能应付大多数毕设场景。最后说点个人经验。这类毕业设计最值钱的部分不是代码量有多少而是你能不能在答辩时把“表结构怎么设计的、状态怎么流转的、权限怎么控制的、异常怎么兜底的”讲成一个通顺的故事。哪怕是模拟支付、简单审核能说清楚为什么这么设计比堆十个没人用的功能模块要有用得多。把这个逻辑捋顺了这个项目你才算真正做完。
阅读完成 · 觉得有帮助?