☰
SpringBoot+大数据+AI电商平台:从架构设计到答辩实战
2026/10/8 3:04:11 网站建设 项目流程

每年毕业季,SpringBoot商城项目大概是最不缺同类品的题目了。你在各种代码仓库里一搜,能翻出上万套“SpringBoot+Vue”的电商系统,功能清单长得跟复制粘贴似的:登录注册、商品管理、购物车、订单、支付。如果只是把这些东西再拼一遍,答辩的时候老师翻两页就困了。真正让题目脱胎换骨的,往往是你怎么理解“智慧商城”“大数据”“AI”这几个词,而不是你堆了多少页面。这套“慧购”一体化电商运营平台,我前前后后做了三个多月,核心心得就一句话:SpringBoot只是底座,数据才是这个项目的灵魂。下面把设计思路、踩坑过程、答辩准备完整拆给大家。

1. 从选题到系统定位:这个毕设为什么值得做、怎么做才不烂大街

1.1 题目拆解:三个关键词背后是三种能力

先说“智慧商城”。很多同学把它理解成“商城 + 推荐位”,数据库里存个商品表、前端放几个轮播图就完事。但“智慧”二字真正的含义,应该是系统能够理解用户行为,并且根据行为动态调整运营策略。也就是说,商品展示、促销活动、客服应答这些环节不再是人拍脑袋配置的静态页面,而是有数据依据的动态结果。

再说“一体化运营平台”。光有用户下单的C端还不够,还得有运营人员使用的B端后台、管理层看的数据大屏、客服用的智能应答模块。这四个端的数据必须打通,用户在手机端的每一次点击、停留、加购、下单,最终都能在运营后台和数据大屏上以指标形式呈现,形成一个从“用户行为”到“运营决策”的闭环。

最后是“大数据与AI”。在春招和毕设里,这两个词被用得很烂,但我认为它们在本项目里承担的是实打实的工程任务:大数据负责处理用户行为日志的采集、清洗、聚合,产出PV、UV、GMV、转化率等指标;AI负责推荐排序、客服意图识别、搜索词联想。它们不是炫技,而是为了让一个传统CRUD商城拥有“自适应”的能力。

1.2 功能边界:哪些做,哪些坚决不做

做毕业设计最容易翻车的地方,是野心太大。一开始我也想过接入支付、接入物流、做秒杀系统,后来冷静下来,把功能边界划成一张表,只保留对“数据闭环”有价值的部分:

模块核心功能数据价值
用户端商城登录、商品浏览、搜索、购物车、下单、评价、客服咨询行为日志、订单事实
运营后台商品管理、类目管理、订单管理、用户管理、营销活动配置运营配置数据、审核链路过账
数据大屏实时GMV、订单量、热销榜、区域分布、转化漏斗数据聚合结果展示
推荐引擎首页猜你喜欢、商品详情相似推荐、搜索联想推荐点击率、曝光日志
智能客服商品咨询、订单状态查询、售后意图识别会话日志、意图分类结果
系统支撑登录鉴权、数据权限、操作审计、限流管控安全保障与追踪

支付和真实物流被我砍掉了。原因很现实:毕设项目不接入真实支付通道,多一个支付模块只会增加联调成本和演示风险;物流同理,既没有真实运单数据,也没有办法模拟全链路状态,对核心数据闭环没有边际贡献。项目名里的“轻量级”不是说功能做得少,而是把资源集中在容易展示、容易被评委追问的数据链路上。

1.3 技术选型:轻量但要有东西可以讲

技术栈我最终定为:Spring Boot 2.7 作为底座,MyBatis-Plus 操作数据库,MySQL 存业务数据,Redis 做缓存与Session,Kafka 做行为日志的削峰缓冲,Flink 做实时指标计算,Hive 做离线数仓的批处理,Vue 3 + Element Plus 搭前端,ECharts 画大屏。这套选型看起来组件不少,但每个组件都能说出明确的职责,评审老师问“为什么引入Kafka/Flink”时,你不至于只会说“业界都在用”。

有同学建议我上微服务,把用户、商品、订单都拆成独立服务。我犹豫了很久还是没拆。毕设场景下,微服务的注册发现、配置中心、链路追踪会吞噬掉大量本该用于业务逻辑的时间,而且单体多模块架构完全能讲清楚模块边界和依赖方向。反而是一上来就拆十几二十个微服务的项目,最后往往因为服务间调用逻辑混乱而被评委连环追问。

2. SpringBoot工程架构设计:模块边界与自定义自动配置

2.1 Maven多模块:让项目结构本身就是一张架构图

我见过太多“一个SpringBoot项目里塞几百个Controller”的毕业设计,controller、service、mapper全在同一个包里,最后类之间互相引用,看得人头皮发麻。这套“慧购”平台从一开始就按Maven多模块来组织工程结构,因为模块边界本身就是最直接的架构表达。

huigou-parent(父POM:统一版本号与依赖管理) ├── huigou-common(通用工具:统一返回体、异常、脱敏工具、时间工具) ├── huigou-system(核心业务:用户、商品、订单、购物车、营销) ├── huigou-analytics(大数据模块:埋点接收、Kafka生产者、实时/离线任务入口) ├── huigou-ai(AI模块:推荐引擎、意图识别、搜索联想) ├── huigou-admin(B端运营后台接口:商品审核、订单处理、数据查询) └── huigou-web(对外聚合层:Controller入口、鉴权过滤、跨域处理)

模块间的依赖方向是单向的:web 依赖 system、analytics、ai;system 依赖 common;analytics 依赖 common;ai 依赖 common 和 system 的查询接口。谁都不允许反向依赖,否则编译期就报错,这在公司里叫“依赖方向约束”,在毕业设计里同样管用。

2.2 自定义自动配置:看似“炫技”,实则解耦

项目里有个比较容易被忽略但也值得写进简历的点,是自定义自动配置。因为 common、analytics 这些模块要被 web 集成,我不想在每个模块里重复写@ComponentScan,而是直接在 resources 目录下放了一个META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,把每个模块的自动配置类注册进去。

@AutoConfiguration @ConditionalOnClass(RedisTemplate.class) @EnableConfigurationProperties(HuigouCacheProperties.class) public class HuigouCacheAutoConfiguration { @Bean @ConditionalOnMissingBean public CacheManager cacheManager(RedisConnectionFactory factory) { RedisCacheManager.Builder builder = RedisCacheManager.builder(factory); // 设置默认过期时间 30 分钟,支持按缓存名覆盖 builder.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofMinutes(30)) .serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))); return builder.build(); } }

这里最核心的是@ConditionalOnClass和@ConditionalOnMissingBean这两个条件注解。前者保证没有Redis依赖时这个配置直接跳过,不会启动报错;后者保证如果使用方自定义了CacheManager,自动配置不会去覆盖它。用这种方式,每个模块都可以独立维护自己的默认配置,同时又允许业务方按需定制,比把配置全部堆在启动类里干净得多。

2.3 SpringBoot版本:不用追新,但别掉进旧坑

Spring Boot 版本这块我提一句,热词里总有人搜“springboot版本太高”,这种焦虑我太理解了。我一开始用了 Spring Boot 3.0,结果发现 javax 包改成 jakarta 了,Spring Security 的配置方式也变了,很多博客里的旧代码直接跑不起来。后来我老老实实退回 2.7.x,理由很简单:2.7 是 2.x 的最后一个稳定大版本,网上资料最多、踩坑记录最全、mybatis-plus 和各种第三方组件的兼容性最稳。

如果你非要用 3.x,也不是不行,但需要额外处理三件事:一是所有javax.*的依赖注入要改为jakarta.*;二是spring.factories自动配置方式被废弃,改用 AutoConfiguration.imports;三是某些中间件客户端要升级到适配 Spring Boot 3 的版本。作为毕设,这些迁移成本换来的收益并不明显,所以我建议老实待在 2.7。

3. 大数据链路:从埋点到数据大屏,把每个环节跑通

这一章是整套系统最核心的部分,也是答辩时最可能被重点追问的部分。大数据如果只是写几个 SQL 去 count 一下表,那跟普通管理后台没有本质区别。真正的数据链路应该是:前端行为产生日志 → Kafka 传输 → Flink 实时聚合 → MySQL/Redis 结果表 → 数据大屏展示;同时原始日志落地 HDFS → Hive 离线清洗 → 数仓分层 → 离线报表。

3.1 前端埋点与数据接入

埋点是一个容易被轻视的环节。我在用户端页面里引入了一个轻量级埋点 SDK,核心代码其实不复杂,就是拦截点击事件,然后异步把行为数据发给后端。

// analytics.js 简化版埋点SDK function track(eventName, params = {}) { const data = { event: eventName, userId: getUserId() || 'anonymous', page: window.location.pathname, timestamp: Date.now(), params }; // 用sendBeacon保证页面跳转时也能发出 navigator.sendBeacon('/api/analytics/collect', JSON.stringify(data)); } // 商品曝光 document.addEventListener('click', (e) => { const el = e.target.closest('[data-product-id]'); if (el) { track('product_click', { productId: el.dataset.productId }); } });

后端收集接口收到日志后,没有立刻写 MySQL,而是先发送到 Kafka 的user-behavior-topic。这个设计的目的是削峰。如果大量用户同时点击,直接暴打数据库,数据库会瞬间压力很大;引入 Kafka 后,日志先缓冲在生产消息队列里,Flink 按照自己的处理速度拉取数据,数据库就稳了。在真实场景里,这叫“异步削峰”。

我在本地模拟过 200 个并发用户持续点击 5 分钟,Kafka 的生产延迟在 10 毫秒以内,Flink 消费端吞吐量稳定在每秒 3000 条以上。

3.2 Flink实时指标计算:窗口怎么选,口径怎么定

Flink 任务我用了三种窗口类型来计算不同指标:浏览 PV 用滚动窗口(Tumbling),每 10 秒输出一次;UV 用会话窗口(Session),60 秒无新行为就切一个新会话;热销榜用滑动窗口(Sliding),每 5 分钟刷新一次最近 30 分钟的下单商品排名。

// 简化版的实时热销榜逻辑 DataStream<BehaviorEvent> stream = ...; stream.filter(_.eventType.equals("order_create")) .map(e -> Tuple2.of(e.productId, 1L)) .keyBy(t -> t.f0) .window(SlidingEventTimeWindows.of(Time.minutes(30), Time.minutes(5))) .aggregate(new SumAggregator()) .keyBy(t -> t.f0) .process(new TopNProcessFunction(50));

这里有一个特别容易被问住的细节:实时计算里的 GMV,和离线数的 GMV 不一致怎么办?我在设计里定了这么一套对齐口径:实时 GMV 统计的是订单支付成功事件,离线 GMV 统计的是数据库订单表中支付状态为已支付的数据,两者会存在因数据同步延迟造成的短暂差异。我在数据大屏上特意标注“数据更新延迟不超过1分钟”,这是对自己口径极其重要的一个保护,一要提前想清楚,二要主动写在系统说明里。

3.3 离线数仓:Hive分层,让报表查询不再全表扫描

离线这块,我按标准数仓的四层模型来建:

-- ODS层:原始日志,分区按天 CREATE TABLE ods_user_behavior_log ( user_id STRING, product_id STRING, event_type STRING, page_url STRING, event_time TIMESTAMP ) PARTITIONED BY (dt STRING); -- DWD层:清洗明细,去掉空值和无效事件 INSERT OVERWRITE TABLE dwd_user_behavior_log PARTITION(dt) SELECT user_id, product_id, event_type, page_url, event_time FROM ods_user_behavior_log WHERE dt = '${yesterday}' AND user_id IS NOT NULL AND product_id IS NOT NULL; -- DWS层:按商品维度的日汇总 INSERT OVERWRITE TABLE dws_product_daily PARTITION(dt) SELECT product_id, COUNT(DISTINCT user_id) AS uv, SUM(IF(event_type='add_to_cart',1,0)) AS cart_cnt, SUM(IF(event_type='order_create',1,0)) AS order_cnt FROM dwd_user_behavior_log WHERE dt = '${yesterday}' GROUP BY product_id;

这套分层最大的好处是口径统一。ODS 保留最原始数据,DWD 做清洗,DWS 做汇总,ADS 层再做业务口径的宽表,每一层职责清晰,出问题能回查源头。很多同学直接用 SQL 从原始表 count 出结果,应用层一改口径就要重算,后来排查到哪一步出了问题,完全没有数的血缘关系,这是不应该的。

3.4 数据大屏:不只是图表,更是“数据说服力”

大屏是整个项目最直观的展示出口。我用 ECharts 做了实时订单流、热销商品排行榜和区域地图。数据从后端聚合接口获取,接口先去 Redis 读,读不到再查 MySQL 并回填 Redis,缓存时间设 30 秒。因为大屏接口是高频轮询,直接打 MySQL 会把自己打崩。轮询频率我定在每 5 秒一次,这个节奏刚好不会刷新太快看着眼花,也不会慢到让观众觉得“卡了”。

关于大屏还有一个展示上的建议:一定要给每个图表配上指标说明文字和一个“数据更新时间”。答辩的时候老师看着跳动的数字,第一反应往往是“这数据是真的假的”。如果你的大屏上写着“最近30分钟热销商品榜 每5分钟更新”,这个质疑就消掉大半。

3.5 数据权限:运营后台的行与列控制

热词里也提到了“大数据行列权限设计”,这个问题在运营后台特别现实。不是所有运营都能看全部数据的。我的方案是双层权限:行级别通过部门数据域控制,比如华东运营组只能查华东区域订单;列级别通过字段脱敏控制,比如客服角色查询用户时,手机号中间四位自动打码,收货地址只显示到区和街道。

// 列脱敏的自定义处理示例 @Sensitive(type = SensitiveType.MOBILE_PHONE) private String mobile; // 行级过滤,MyBatis-Plus 拦截器注入 @InterceptorIgnore public class DataScopeInterceptor implements InnerInterceptor { @Override public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { // 根据当前登录用户的数据域,拼接 SQL 条件 String sql = boundSql.getSql(); String filteredSql = "SELECT * FROM (" + sql + ") t WHERE t.region IN (...权限范围...)"; // 使用反射修改 BoundSql 中的 sql 字段 } }

这块代码看着不多,但完成壮观的权限演示效果很好。演示的时候我先用超管账号看全部数据,再切换到区域运营账号,屏幕上的订单列表立刻少了一半。老师一眼就能看懂你做了数据权限控制,这一点比写一百行权限接口代码都直观。

4. AI功能落地:推荐系统的冷启动与智能客服的工程化实现

AI 模块是最容易被评委“刨根问底”的地方。如果只写一个“接入了大模型API”,老师很可能会追问:你的 API Key 是怎么管理的,如果断网了怎么办,这算你自己的工程成果吗?所以我的策略是:推荐算法自行实现,客服走检索式问答与规则意图识别,把可解释性放在第一位。

4.1 推荐引擎:为什么选 ItemCF,而不是复杂的深度学习

我用的主推荐算法是物品协同过滤(Item-Centric Collaborative Filtering,简称 ItemCF)。它的核心思想很简单:如果用户A同时喜欢商品X和商品Y,说明X和Y有一定相似性;那么给喜欢X的用户B推荐Y,就有较大概率被接受。

计算商品相似度的公式是:

sim(i, j) = |喜欢商品i的用户集合 ∩ 喜欢商品j的用户集合| / sqrt(|喜欢i的用户数| * |喜欢j的用户数|)

选择 ItemCF 而不是 UserCF,是因为商城场景下用户量远大于商品量,商品数量稳定在几千到几万,计算商品间的相似度矩阵成本可控,而且实时性好。深度学习召回模型对数据量和训练资源要求高,在毕设有限样本下反而容易过拟合。选型时我做了个对比:

方案优点劣势是否适合本项目
UserCF发现用户新兴趣用户量巨大时矩阵计算困难否
ItemCF可解释性强,计算量小无法给用户推荐全新品类是
深度学习召回精度上限高需要大量样本和训练资源否

为了给推荐结果提供解释依据,我在推荐结果接口里返回了推荐理由,比如“因为买了 iPhone 充电器,推荐同品牌数据线”。这个小小设计在演示时非常有说服力,因为它让推荐结果不再是一个玄学黑盒。

4.2 冷启动:新用户和新商品怎么处理

新用户没有任何行为数据,协同过滤直接失效。我做了三层兜底策略:

第一层,新用户登录后先看运营配置的热门榜单,从 Redis 里直接取最近 7 天销量 Top20 的商品;第二层,根据用户注册时选的兴趣标签(数码、服装、美妆等)做品类初筛;第三层,等用户产生了 5 次以上点击行为后,系统才开始为他跑 ItemCF 实时推荐,并随着行为数增加逐渐加大协同过滤结果的权重。

新商品的处理同样棘手,新商品没有用户行为,ItemCF 算出来的相似度是 0。我给新商品打上“新品”标记,在前 7 天优先走类目冷启动池,将其推荐给那些对同类目商品点击较多的用户,等积累起足够行为数据再进入常规推荐流程。

4.3 智能客服:检索式问答比生成式更稳

智能客服我没有上大模型生成式对话,而是做了“意图识别 + 检索问答”的方案。先用 HanLP 对用户提问做分词和词性标注,再通过关键词规则映射到预定义意图:商品咨询、订单查询、退款售后、物流咨询、人工转接。每个意图绑定一个知识库表,通过 BM25 做相关性检索,返回最匹配的答案。

// 意图识别简化逻辑 public Intent matchIntent(String question) { List<String> words = hanlp.segment(question); if (containsAny(words, "退款", "退货", "售后")) { return Intent.AFTER_SALE; } if (containsAny(words, "订单", "发货", "物流")) { return Intent.ORDER_STATUS; } if (containsAny(words, "多少钱", "价格", "优惠")) { return Intent.PRODUCT_PRICE; } return Intent.FALLBACK; }

这样设计的好处是:每一句回答都能溯源到知识库里的哪一条,完全可控。答辩如果问“你这客服翻车怎么办”,你可以直接回答“业务规则之外会转人工,不会让模型自由发挥”。在演示时,我输入“手机什么时候发货”,客服自动回了一段订单状态查询结果,评委的追问方向马上就转向会话逻辑设计,而不是纠结于模型幻觉。

4.4 为什么不在核心链路里硬塞大模型

我不是否定大模型,而是认为在毕设场景里引入大模型需要先想好四件事:一是本地部署大模型的硬件成本,二是生成式结果不可控的演示隐患,三是知识库更新时检索效果不稳定,四是学校答辩环境不一定有稳定的外网连接。我的处理方式是把大模型接口做成了一个预留的ChatProvider抽象,如果将来有条件、有网络,可以随时替换掉规则回答的实现类,不需要动其他代码。这种“预留但不依赖”的做法,在答辩时反而是加分项。

5. 前后端工程化:从Vite本地联调到SpringBoot一体化打包

很多毕设项目前后端用两个端口部署,前端跑 5173,后端跑 8080,最后把两个包一压就交了。实际上这暴露了工程化能力的欠缺。整的系统的最终产物应该是一个可以直接java -jar启动的独立 SpringBoot 应用,前端资源全部打包进 Jar 包。

5.1 开发环境:Vite代理解跨域

开发时前后端分离,前端调用接口需要走代理,不然浏览器会拦截跨域请求。我在vite.config.js里这样配:

export default defineConfig({ server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true, rewrite: path => path.replace(/^\/api/, '') } } } })

这里有个细节:后端接口路径统一不带/api前缀,前端代理才需要把/api去掉;后端跨域配置里再允许http://localhost:5173这个来源。双保险,开发时基本不会遇到跨域问题。

5.2 Vue打包进SpringBoot:路由模式的坑

打包前的关键一步,是把 Vue 的编译产物放进 SpringBoot 的src/main/resources/static目录。但首页直接刷新会 404,因为 Vue 用 history 路由模式时,浏览器请求一个不存在于静态文件里的路径,SpringBoot 找不到对应的资源,就会抛 404。

解法有两种:一是改用 hash 路由,地址栏会带#;二是在后端加一个路由转发控制器,把所有非 API 路径请求转发到index.html。

@Controller public class SpaForwardController { @RequestMapping(value = {"/", "/product/**", "/cart/**", "/order/**", "/user/**"}) public String forward() { return "forward:/index.html"; } }

我选了第二种,因为地址栏干净,而且更接近真实生产项目的处理方式。转发配置必须放在 API 接口之外,只拦截前端路由会涉及的路径。

5.3 打包优化:用cdn还是本地依赖

Element Plus 和 ECharts 的体积都不小,打进静态资源后 Jar 包会变得很臃肿。我做了几项优化:组件库按需导入,ECharts 只注册用到的图表类型,路由懒加载,最终 Jar 包大小控制在 120MB 左右。这里我不建议把公共库全部改成 CDN 引入,因为演示环境网络状况不可控,真遇到没网的情况页面全白,那就尴尬了。本地依赖虽然让包大一点,但稳定性才是第一位的。

5.4 部署:一个jar搞定演示环境

部署我用的是 Dockerfile,基础镜像用eclipse-temurin:8-jre,把 Jar 包放进去,开放 8080 端口。

FROM eclipse-temurin:8-jre WORKDIR /app COPY target/huigou-platform.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-Xmx512m", "-jar", "app.jar"]

在演示用的笔记本上跑这个容器,启动内存控制在 512MB 以内,后台同时跑 MySQL、Redis、Kafka、Flink 本地任务也不至于卡死。这个选型经过深思熟虑,因为毕设现场往往不会给你一台高性能服务器,轻量、省资源是最大的生存法则。

6. 权限、缓存与性能:答辩中躲不开的工程问题

功能做出来了,业务逻辑都通,但如果问你性能优化、安全防护、缓存穿透,你答不上来,整个项目评价就会降级。这一章我自己整理了一份高频追问清单。

6.1 RBAC与数据权限:用户表之外还要有角色和菜单

用户端没什么复杂的权限逻辑,登录后拿到 Token 就能访问商城接口。但运营后台不一样,必须有完整的 RBAC 模型:用户表、角色表、菜单表、用户角色关系表、角色菜单关系表。前端根据接口返回的权限码渲染菜单和按钮,后端在拦截器里校验每个接口的权限码。

数据权限单独设计。部门表里存数据域编码,比如华东、华南;用户所属部门决定了能查哪些区域的数据。查询订单时通过 MyBatis-Plus 的拦截器自动拼接区域条件,业务代码无感知。这个组合拳在答辩时讲五分钟都不重样,关键是你确实实现了,不是背概念。

6.2 Redis缓存:别让数据库裸奔

商品详情是热点数据,查询量最大。我用 Redis 做了两级保护:第一层,热门商品详情直接缓存 30 分钟;第二层,用逻辑过期解决缓存击穿,即缓存中存一份带过期时间的商品数据,如果发现过期,先返回旧数据,同时异步去数据库刷新缓存。

缓存穿透也做了防护。查询一个不存在的商品 ID 时,Redis 查不到,数据库也查不到,请求全打在数据库上就穿透了。我采用布隆过滤器拦截不存在的 ID,再用“缓存空结果 + 短过期时间”兜底,双管齐下。

压测结果:没有缓存时,商品详情接口 300 并发下数据库连接池直接饱和,平均响应 3.2 秒;加了 Redis 缓存后,平均响应降到 20 毫秒以内,数据库只承受了业务写入压力。

6.3 接口安全与限流:参数校验、SQL注入、防刷

接口安全这块,我做了三件事。第一,所有前端查询接口参数强制用 DTO 接收,@NotBlank、@Size这类注解做基础校验;第二,MyBatis 全部使用#{}预编译参数,禁止字符串拼接SQL;第三,针对发送验证码、搜索这类可被刷的接口,用 Guava 的 RateLimiter 做单机限流,每用户每分钟最多请求 10 次。

还有日志脱敏。用户手机号、身份证号这类敏感字段,在日志输出时自动打码,避免运维排查问题时数据泄露。这些细节未必会在答辩现场被看到,但写在系统说明里会显得专业。

6.4 数据一致性:订单和库存怎么联动

电商系统里最容易被追问的是超卖问题。我在下单接口里用 SQL 条件更新控制扣减库存,而不是先查库存再扣减:

UPDATE product_sku SET stock = stock - #{count} WHERE sku_id = #{skuId} AND stock >= #{count};

这个语句执行后如果影响行数为 0,说明库存不足或商品下架,直接返回“库存不足”即可,不需要分布式锁,简单可靠。用 Redis 预减库存的策略虽然高性能,但对于毕设来说,一旦缓存和数据库不一致就被评委抓住把柄。追求简单可解释的方案,在工程上是同样被看重的取舍。

7. 演示流程与答辩准备:细节决定最终分数

7.1 演示路径:先C端后B端,最后放大招

演示的顺序很重要。我的建议是:先花 30 秒介绍项目定位和技术栈,然后走一遍用户端完整购物流程,注册、浏览商品、加购、下单、支付模拟、查看订单;接着切到运营后台,展示商品上下架、订单审核、用户管理;最后打开数据大屏,让评委看到之前操作产生的订单实时跳动在屏幕上,再打开推荐结果页,解释为什么给这个用户推荐这些商品。

这套路径本质上是先建立信任感,再展示工程量,最后通过数据埋点把前两个部分串成一个闭环。评委看到你在用户端下单,然后数据大屏的 GMV 数字增长,那种“数据通起来了”的直观感受,比任何 PPT 都有效。

7.2 高频追问与参考答案

我整理了现场最可能被问到的六个问题,每个都有对应的回答思路:

  • 为什么用 Flink 不用 Spark Streaming?答:毕设场景需要快速开发、窗口 API 友好,Flink 的精确一次语义和低延迟更适合实时指标场景;Spark 更偏批处理,流处理适合用于更复杂的生产链路。
  • 实时和离线指标不一致怎么办?答:定义口径和使用场景不同,实时用于大屏监控,离线用于每日复盘,前端标注延迟范围。
  • 商品推荐冷启动怎么解决?答:热榜兜底、行为数据积累后再切协同过滤。
  • 你的系统单机能承受多少并发?答:给出本地压测的具体数据,同时说出性能瓶颈在数据库连接和 GC,不至于空口吹。
  • 为什么不做微服务?答:单体多模块足够承载业务,关注可部署性和可解释性,对毕设评审来说正确取舍比盲目分布式更重要。
  • Kafka 挂了怎么办?答:埋点日志先落在本地缓存,Kafka 恢复后重发;同时订单核心链路不依赖 Kafka,业务主流程照常运行。

7.3 做这个项目我最后悔没早点做的事

如果重来一遍,我会一开始就写好数据处理的口径文档,而不是边写边想,等大屏做完发现实时指标和离线报表对不上,返工了整整一个星期。还有一个建议是:从写第一行代码之前就录屏。我最后录了 15 分钟的完整演示视频,放在项目说明里。答辩现场如果投影仪出问题或者演示环境网络不稳,这段视频能救你一命。

另外一个实际经验是:把每个模块的测试数据做得尽量真实。商品名称不要用“商品1”“商品2”,而是真实品类和品牌(当然不要拿真实品牌做商用暗示),用户注册信息也不要 desist 全是“张三李四”。评委在演示时,看到“华为手机壳”“优衣库基础款T恤”这类数据,会觉得你对业务场景有真实理解,而不是在糊弄。数据质量本身也是毕设的一部分,这个细节很多人会忽略。

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

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

立即咨询