ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

百姓点评网:SpringBoot驱动的本地商户评价与交易系统

百姓点评网:SpringBoot驱动的本地商户评价与交易系统 百姓点评网SpringBoot驱动的本地商户评价与交易系统毕业设计这样从0肝到11. 选题逻辑拆解为什么点评网是个被低估的毕业设计题目每年计算机毕业设计选题目大家翻来覆去就那么几个方向商城、博客、后台管理系统、预约系统。自己刚开始选百姓点评网这个方向时身边不少人第一反应是这不就是大众点评吗会不会太常见了——但实际上把社区生活服务和本地商户绑在一起做这题的深度和完整度远超一般的管理系统或单商户电商项目。先说清楚这个题目到底在干什么。百姓点评网的核心业务模型可以拆成三块内容侧用户消费者浏览本地商户信息、查看评价、收藏店铺、筛选分类交易侧对接商户的优惠券、团购套餐、预约服务形成看点评→买套餐→到店消费→回头再评价的闭环管理侧平台管理员审核商户入驻、处理举报、管理分类和推荐位商户端能回复评价、更新门店信息这个选题最大的价值在于它不是单一的业务流而是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.18Java 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。LambdaQueryWrapperShop wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.isNotBlank(keyword), Shop::getShopName, keyword) .eq(Shop::getStatus, 1) .orderByDesc(Shop::getScore); IPageShop 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 TypeReferenceListShopVO() {}); } // 缓存没有查数据库 ListShop list shopService.listTopShops(categoryId, 10); // 写回缓存过期时间5分钟 redisTemplate.opsForValue().set(shop:list: categoryId, JSON.toJSONString(list), 5, TimeUnit.MINUTES);4.2 商户详情页信息聚合的经典案例详情页是点评系统复杂度最高的一个页面因为它聚合了商户基本信息、相册、团购套餐、评价列表、周边推荐五个模块。一开始如果串行查五次数据库接口响应时间能到800ms往上在答辩演示时这个速度明显掉价。解决思路也很直接拼装VO视图对象。新建一个ShopDetailVO包含ShopInfo、ListCoupon、ListEvaluation、ListShop这些成员。Service层先查商户再查套餐再查评价最后塞进一个VO返回给前端。虽然还是在Service里做了多次查询但至少前端只需要一次HTTP请求。如果想更进一步可以用CompletableFuture把互不依赖的三个查询并行化响应时间能再降一半。CompletableFutureShop shopFuture CompletableFuture.supplyAsync(() - shopService.getById(shopId)); CompletableFutureListCoupon couponFuture CompletableFuture.supplyAsync(() - couponService.listByShopId(shopId)); CompletableFutureListEvaluation 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(); }注意事务里有个隐蔽的坑当多个用户同时对同一家商户打分时上面这段代码会出现竞态条件——两人同时读到score4.0, count10各自算完再写回最终结果只加了一次评价数。真实的互联网系统会用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共享。JWTJSON Web Token是无状态的用户登录后后端签发一个Token前端存着每次请求带上后端验签即可。这一步直接把认证逻辑从服务器内存解耦了。Spring Security JWT的整合套路比较固定核心四步写一个JwtUtil工具类负责生成和解析Token写一个JwtAuthenticationFilter继承OncePerRequestFilter在请求进入Controller前解析Token配置SecurityConfig放行登录接口、注册接口、商户列表接口其余接口需要认证登录成功生成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.activeprod app.log 21 这样做完后端就在8080端口跑起来了。如果需要让前端通过IP访问注意nginx反代或直接在Spring Boot配置里加server.address0.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面试题突然就能理解为什么说中间件选型取决于业务规模了——因为每一项技术的选用在我这里都有了真实场景做锚点。如果你也正在被毕业设计折磨选这个方向大概率不会后悔。
返回列表