☰
百姓点评网:SpringBoot驱动的本地商户评价与交易系统
2026/9/30 4:39:44 网站建设 项目流程

百姓点评网:SpringBoot驱动的本地商户评价与交易系统,毕业设计这样从0肝到1

1. 选题逻辑拆解:为什么"点评网"是个被低估的毕业设计题目

每年计算机毕业设计选题目,大家翻来覆去就那么几个方向:商城、博客、后台管理系统、预约系统。自己刚开始选"百姓点评网"这个方向时,身边不少人第一反应是"这不就是大众点评吗?会不会太常见了"——但实际上,把"社区生活服务"和"本地商户"绑在一起做,这题的深度和完整度远超一般的管理系统或单商户电商项目。

先说清楚这个题目到底在干什么。百姓点评网的核心业务模型可以拆成三块:

  • 内容侧:用户(消费者)浏览本地商户信息、查看评价、收藏店铺、筛选分类
  • 交易侧:对接商户的优惠券、团购套餐、预约服务,形成"看点评→买套餐→到店消费→回头再评价"的闭环
  • 管理侧:平台管理员审核商户入驻、处理举报、管理分类和推荐位,商户端能回复评价、更新门店信息

这个选题最大的价值在于,它不是单一的业务流,而是"UGC内容 + 电商交易 + 后台审核"三条线交织的平台型项目。对毕业设计而言,够复杂但不失控——每一块都能单独展开讲,合起来又能体现完整的系统设计能力。答辩的时候,你想讲算法有搜索推荐,你想讲工程有权限和事务,你想讲产品有业务闭环,素材非常充足。

同时,Spring Boot做这个题目的性价比也非常高。Spring Boot本身解决的是"配置地狱"问题,它的自动装配、starter机制、嵌入式容器,能让一个学生从开发到部署全程自己搞定,不需要额外搭Tomcat、配Apache。而点评网这种重CRUD、重业务状态流转的系统,正好是Spring Boot + Spring MVC + MyBatis Plus最舒服的舒适区。

2. 技术选型的前因后果:不是因为流行,是因为每一环都接得住

2.1 后端框架:Spring Boot 2.7.x,不追新是对的

Spring Boot版本选择上,我最后落在2.7.x而不是3.x,原因很实在。3.x要求JDK 17以上,而很多机房的机器还停留在JDK 8;另外,成熟教程和依赖兼容性,2.7.x明显更稳。用Spring Boot 2.7.18,Java 8,这个组合跑起来几乎不会踩环境坑。

Spring Boot在这个项目里真正发光的地方是它的自动装配。比如我要集成Redis做缓存、集成RabbitMQ做异步消息,只需要引入starter,配置application.yml里的连接参数,剩下的由框架自动完成。Spring Boot的starter机制就像是一个万能转接头——你只管声明"我要用Redis",它自动帮你把连接工厂、模板Bean全都塞进容器里。

2.2 持久层:MyBatis Plus解决了毕业设计最痛的CRUD

持久层我选了MyBatis Plus而不是纯MyBatis。差别在哪儿?纯MyBatis写一个简单的分页查询,你得写接口方法、写XML、配ResultMap。MyBatis Plus把CRUD的通用方法全都内置了,比如selectPage()、selectById()、updateById(),单表操作几乎不用写SQL。

LambdaQueryWrapper<Shop> wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.isNotBlank(keyword), Shop::getShopName, keyword) .eq(Shop::getStatus, 1) .orderByDesc(Shop::getScore); IPage<Shop> page = shopMapper.selectPage(new Page<>(current, size), wrapper);

这段代码就是商户列表页加上关键词搜索加状态过滤加按评分倒序,换成纯MyBatis至少是三四行XML加一个方法的量。毕业设计期间时间紧,MyBatis Plus这种"少写SQL"的特性是实打实的容错率。

2.3 前端:Vue + Element UI,和一页静态页面的区别在哪

如果你只想拿个及格分,前端写个Thymeleaf模板页也能过。但如果想让答辩老师眼前一亮,前端建议走Vue + Element UI这种前后端分离的思路。分离的核心价值在"职责清晰":前端只管渲染和交互,后端只提供JSON接口,两边可以并行开发。

我当时的做法是前端用Vue CLI脚手架,组件化搭建页面。首页是商户列表、分类筛选;详情页是商户信息加评价列表加团购套餐购买入口;用户中心是订单、收藏、我的评价。后台管理端单独一个工程,用同样的技术栈做商户审核、数据统计。

2.4 缓存、搜索和对象存储:真正的"平台感"靠这三样拉起来

一个点评系统,最不能忍的就是热门商户页面每次请求都去查数据库。所以Redis必须上,缓存商户详情、缓存首页推荐列表,设置过期时间防止数据长期不更新。

搜索是点评类平台的核心体验。我没有为了炫技引入Elasticsearch——那个对毕业设计来说过重了,直接用MySQL的全文索引或者like模糊查询足以支撑本地商户规模。不过可以给商户表加一个搜索记录表,把热门关键词存热榜,在答辩时作为"轻量级搜索优化方案"来讲。

图片存储用MinIO,本地服务器一键部署,兼容S3协议,后续想换阿里云OSS也是平滑切换。评价里用户上传的实拍图、商户的门头照都走MinIO,返回的就是一个URL,前端直接渲染。

3. 数据库设计:10张核心表,如何把业务闭环串成一张可以答辩的图

数据库设计是毕业设计答辩的重头戏,评审老师一定会问"表结构为什么这么设计""有没有冗余""怎么保证一致性"。这套点评网的库表设计,我把它串成五条业务线来讲。

3.1 用户与权限线

用户表、角色表、用户角色关联表。角色用简单的RBAC模型,用户角色就是user_role这张关联表。登录用Spring Security + JWT,无状态认证,前端把Token存在localStorage里,每次请求放请求头。

3.2 商户信息线

商户表是最核心的表,字段要覆盖:商户名称、所属分类(外键到分类表)、所在城市、详细地址、经纬度、联系电话、营业时间、人均消费、评分、月销量、状态(待审核/正常/下架)。经纬度这个字段一定要留,因为后面做"附近的人"需求时,用MySQL的ST_Distance()函数就能算距离,没有经纬度就只能靠城市名过滤,太粗糙了。

3.3 评价内容线

评价表、评价图片表。评价表关联用户和商户,核心字段是评分(1-5星)、内容、是否匿名、回复状态。商户回复表单独拆一张,因为一个评价可能被商户回复多次(虽然业务上通常只回复一次,但表结构支持会更灵活)。

用户在详情页看到的是评价列表,后台实际要执行的是:

@Select("SELECT ev.*, u.nickname, u.avatar FROM evaluation ev " + "LEFT JOIN user u ON ev.user_id = u.id " + "WHERE ev.shop_id = #{shopId} AND ev.status = 1 " + "ORDER BY ev.create_time DESC")

联表查用户信息,但评价内容多了以后,这种联表会变慢。线上方案是把用户昵称冗余到评价表的user_nickname字段里,这算是一种典型的口碑系统设计方案,答辩时可以主动讲出来。

3.4 交易订单线

订单表、订单明细表、秒杀或优惠券表。订单表的字段要有:订单号(用时间戳加随机数生成)、用户ID、商户ID、套餐ID、总金额、支付方式、状态(未支付/已支付/已消费/已退款)、支付时间、消费时间。

这里有个容易踩的坑:订单状态不要用简单的字符串,建议用tinyint存状态码,然后在枚举类里定义状态说明。

3.5 平台运营线

分类表、轮播图表、举报表。举报表容易被忽略,但它是平台"治理能力"的体现。用户可以对某个评价、某个商户发起举报,管理员在后台处理。加上这张表,答辩时你就可以理直气壮地说"系统具备完整的内容安全机制"。

数据库设计的一个核心细节:所有表都要有create_time和update_time两个字段,MyBatis Plus里有@TableField(fill = FieldFill.INSERT),配合MetaObjectHandler可以实现自动填充。这不是可选项,是毕设的必修项,往后的所有查询排序几乎都用得上这两个字段。

4. 核心模块实战:从用户点开首页到完成下单,一次完整调用长什么样

技术架构和数据库都讲完了,这一章进入实操层面。我按一条完整用户链路来还原代码实现逻辑。

4.1 首页商户列表:三级分类 + 缓存 + 分页

用户打开首页,看到的是三级分类(比如"美食→火锅→川渝火锅")。实现上,分类表用parent_id自关联即可。查询时按一级分类分组,返回树形结构。

商户列表接口的设计要提前想好入参:cityId(城市)、categoryId(分类)、keyword(搜索关键词)、sortType(排序方式,综合/距离/评分/销量)、pageNum、pageSize。拿cityId说,如果没有城市维度,这个系统就退化成了全国一锅粥,"本地"两个字名存实亡。

首页热门的商户列表我做了两层缓存:

// 先查Redis Object cache = redisTemplate.opsForValue().get("shop:list:" + categoryId); if (cache != null) { return JSON.parseObject(cache.toString(), new TypeReference<List<ShopVO>>() {}); } // 缓存没有,查数据库 List<Shop> list = shopService.listTopShops(categoryId, 10); // 写回缓存,过期时间5分钟 redisTemplate.opsForValue().set("shop:list:" + categoryId, JSON.toJSONString(list), 5, TimeUnit.MINUTES);

4.2 商户详情页:信息聚合的经典案例

详情页是点评系统复杂度最高的一个页面,因为它聚合了商户基本信息、相册、团购套餐、评价列表、周边推荐五个模块。一开始如果串行查五次数据库,接口响应时间能到800ms往上,在答辩演示时这个速度明显掉价。

解决思路也很直接:拼装VO(视图对象)。新建一个ShopDetailVO,包含ShopInfo、List<Coupon>、List<Evaluation>、List<Shop>这些成员。Service层先查商户,再查套餐,再查评价,最后塞进一个VO返回给前端。虽然还是在Service里做了多次查询,但至少前端只需要一次HTTP请求。如果想更进一步,可以用CompletableFuture把互不依赖的三个查询并行化,响应时间能再降一半。

CompletableFuture<Shop> shopFuture = CompletableFuture.supplyAsync(() -> shopService.getById(shopId)); CompletableFuture<List<Coupon>> couponFuture = CompletableFuture.supplyAsync(() -> couponService.listByShopId(shopId)); CompletableFuture<List<Evaluation>> evalFuture = CompletableFuture.supplyAsync(() -> evaluationService.listByShopId(shopId)); ShopDetailVO vo = new ShopDetailVO(); vo.setShop(shopFuture.join()); vo.setCoupons(couponFuture.join()); vo.setEvaluations(evalFuture.join());

这里抛出一个个使用CompletableFuture优化接口响应时间的案例,在答辩时属于"性能优化"维度的加分项。

4.3 评价模块:事务一致性比得分计算更难搞

用户提交评价时要做的事情是:插入评价记录、更新商户得分、更新商户评价数、可能还有赠送积分。这四个操作不能一个失败一个成功,所以必须加事务:

@Transactional(rollbackFor = Exception.class) public Long submitEvaluation(EvaluationDTO dto) { Evaluation evaluation = new Evaluation(); BeanUtils.copyProperties(dto, evaluation); evaluation.setStatus(1); evaluationMapper.insert(evaluation); // 查询并更新商户平均分 Shop shop = shopMapper.selectById(dto.getShopId()); Double newScore = (shop.getScore() * shop.getEvalCount() + dto.getScore()) / (shop.getEvalCount() + 1); shop.setScore(BigDecimal.valueOf(newScore).setScale(1, RoundingMode.HALF_UP).doubleValue()); shop.setEvalCount(shop.getEvalCount() + 1); shopMapper.updateById(shop); return evaluation.getId(); }

注意事务里有个隐蔽的坑:当多个用户同时对同一家商户打分时,上面这段代码会出现竞态条件——两人同时读到score=4.0, count=10,各自算完再写回,最终结果只加了一次评价数。真实的互联网系统会用Redis原子操作或者数据库乐观锁来解决,但毕业设计里主动讲出这个数据竞争问题,并给出用@Version字段做乐观锁的改进方案,比闷头写能用要高一个段位。

4.4 下单模块:库存扣减和订单状态机

用户购买了某个团购套餐后,要生成订单并扣减库存。库存扣减最朴素的做法是:

UPDATE coupon SET stock = stock - 1 WHERE id = ? AND stock > 0

这条SQL原子地完成了"库存充足才扣减",避免了超卖。扣完库存再插入订单记录,这两步必须在一个事务里。支付环节我不建议真的去接支付宝或微信支付——即使支付宝有沙箱环境,毕业设计刷答辩时间也折腾。直接用"模拟支付":用户点击支付,订单状态从"待支付"变成"已支付"。在答辩时说明"网关支付以第三方接口文档为准,本项目为了完整性采用模拟实现"即可。

5. 前后端分离下的权限控制:Spring Security + JWT的完整闭环

5.1 为什么不用Session,要用JWT

前后端分离架构下,Session方案天然有痛点:后端为了维持会话需要存Session,如果将来做了负载均衡,还要做Session共享。JWT(JSON Web Token)是无状态的,用户登录后后端签发一个Token,前端存着,每次请求带上,后端验签即可。这一步直接把认证逻辑从"服务器内存"解耦了。

Spring Security + JWT的整合套路比较固定,核心四步:

  1. 写一个JwtUtil工具类,负责生成和解析Token
  2. 写一个JwtAuthenticationFilter,继承OncePerRequestFilter,在请求进入Controller前解析Token
  3. 配置SecurityConfig,放行登录接口、注册接口、商户列表接口,其余接口需要认证
  4. 登录成功生成Token返回给前端,前端放在Header的Authorization字段里

5.2 认证与授权的区别,必须分开说

认证是"你是谁",授权是"你能干什么"。点评网里三种角色:普通用户、商户管理员、平台管理员。普通用户能提交评价、下单;商户管理员能维护门店信息、回复评价;平台管理员能审核商户、删评价。

实现上我用的是Spring Security的@PreAuthorize("hasRole('ADMIN')")注解加在Controller方法上。这是权限控制里最直观的写法。

5.3 权限校验容易忽略的一个细节

商户要修改自己门店信息,只判断"已登录且是商户角色"是不够的,还要判断这个商户是不是属于当前登录账号。很多同学会漏掉这一层,结果商户A可以改商户B的信息。这个用简单的userId比对即可:

Shop shop = shopService.getById(shopId); if (!shop.getOwnerId().equals(currentUserId)) { throw new BusinessException("无权操作该商户"); }

这一行是自己真实项目中一定不能少的"资源级权限校验",写进去,并为此准备一段两分钟的解释,答辩时老师追问你会自信很多。

6. 部署上线与性能优化:从"能跑"到"能演示"

6.1 环境搭建:Docker Compose一键拉起全部中间件

本地开发时,MySQL、Redis、MinIO三个东西手动装很费事。我用Docker Compose编排:

version: '3' services: mysql: image: mysql:5.7 ports: "3306:3306" environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: dianping redis: image: redis:6.2 ports: "6379:6379" minio: image: minio/minio ports: "9000:9000" command: server /data --console-address ":9001"

一条docker compose up -d,所有中间件就位。写系统和写配置一样重要,与其花一下午去装MySQL解压版,不如花五分钟写这个文件。

6.2 项目打包与部署:jar包直接怼服务器

Spring Boot内嵌Tomcat的特性让部署变得异常简单,你不需要任何外部服务器。mvn clean package打成jar包,然后:

nohup java -jar dianping-server.jar --spring.profiles.active=prod > app.log 2>&1 &

这样做完,后端就在8080端口跑起来了。如果需要让前端通过IP访问,注意nginx反代或直接在Spring Boot配置里加server.address=0.0.0.0。

6.3 SQL性能排查:用日志说话

系统跑起来后,我习惯在application.yml里打开MyBatis的SQL日志:

logging: level: com.dianping.mapper: debug

每个请求执行了什么SQL,参数是什么,全都打在控制台上。有一次详情页慢,就是因为评价图片是一张张查的,查了30次。改成WHERE eval_id IN (...)一次性查回来,秒开。这种排查思路,比你在答辩PPT上写"本项目对SQL进行了优化"有说服力得多——因为你能讲出具体的排查链路。

7. 答辩演示与开题报告:怎么做才不会在老师面前露怯

7.1 演示准备的三条铁律

第一,灌好演示数据。空数据库点开是灾难现场。要有至少30家商户、每个商户5条以上评价、几个不同城市的分类数据。商户图片、用户头像都要真实一点,我拿MinIO存了一批网上的开源免费图,看起来非常完整。

第二,准备一条"业务主链路"脚本。从注册新用户开始→搜索商户→查看详情→看评价→购买套餐→模拟支付→我的订单→提交评价→查看评分变化,一条龙走完。这条主链路演示完,系统最主要的价值就展示完了。

第三,准备一个"错误演示"。比如故意把Token清掉再访问需要登录的接口,展示统一异常处理返回的JSON信息。这比一路走绿更显真实——一个系统有防御能力,才是合格的系统。

7.2 论文里怎么把项目拔高

本科毕业论文太容易写成"操作说明书"。当年的经验是,论文里硬性加入一个"系统设计难点分析"章节。我写的时候,挑了三个难点:

  • 用户提交评价时商户评分的并发一致性
  • 前后端分离下的无状态JWT认证流程安全
  • 本地商户数据的多条件聚合检索优化

每个难点都用标准的"问题定义→原因分析→解决方案→对比验证"结构来写。老师的直觉就是越具体的系统设计,越不可能造假,也越容易给高分。

7.3 互联网上能抄的作业尽量抄

光靠自己闷头看不现实,必要的路径是站在前人肩膀上。网上搜"SpringBoot毕业设计选题 + 点评系统",其实有很多开源项目基础版本。我的建议是可以用来参考数据库设计、接口设计规范,但绝不要原封不动。毕业设计的核心是"你掌握并呈现了一套系统的完整实现逻辑",哪怕你的代码写得朴素一点,分类讲解的时候逻辑清晰,价值远大于复制来的华丽仓库。

8. 写在最后:这个选题还能往哪些方向续命

如果你答辩完之后还想继续打磨这个项目(或者下一届学弟学妹想在此基础上做进阶),这几条路都走得通:

  • 接入地图API做基于定位的LBS推荐,把"附近的人"从列表变成地图模式
  • 用协同过滤算法做个性化商户推荐,基于用户的浏览记录和评价偏好算相似度
  • 引入Redis布隆过滤器拦截恶意刷评价请求,给系统增加风控维度
  • 把报表模块做厚,按天、周、月维度统计平台GMV和评价趋势,配置ECharts大屏

百姓点评网这个题目最舒服的地方在于:它既有内容社区的生长逻辑,又有交易系统的严谨流程,往上接推荐算法、往下接基础架构都有抓手。你拿它做毕业设计,认真学习两三个月,最后收获的绝不是一个能跑的项目,而是一整套"从需求分析到上线运维"的工程方法论。

最后分享一个自己的实际体会:做这个题之前,我连@Transactional什么时候生效、JWT的过期时间该设多少都不确定。等我把整条链路跑通,再回头看那些Spring Boot面试题,突然就能理解为什么说"中间件选型"取决于业务规模了——因为每一项技术的选用,在我这里都有了真实场景做锚点。如果你也正在被毕业设计折磨,选这个方向,大概率不会后悔。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询