新闻详情

新闻详情

首页 / 资讯中心 / 详情

SpringBoot+Vue热门文创推荐平台:热度算法与前后端联调实战

发布时间:2026/9/26 13:03:47来源:尧图网络
SpringBoot+Vue热门文创推荐平台:热度算法与前后端联调实战
简介这是一个基于Spring Boot与Vue.js的热门文创内容推荐平台项目面向需要完成毕业设计、课程设计、大作业或工程实训的开发者也适合希望学习前后端分离架构的入门与进阶者。项目源码已经过调试压缩包约36.31MB内部主要包含Java后端源码、Vue前端代码、SQL初始化脚本及配套说明文档便于导入IDE后结合数据库快速搭建运行环境。整体采用SpringBootVue实现前后端分离覆盖接口调用、业务逻辑处理、数据持久化和页面交互等关键环节目录结构清晰易于按模块阅读与改造。开发环境基于JDK8、MySQL5.7、Tomcat7和Maven3.3.9可复现性较强SQL脚本与说明文档能帮助快速完成数据库初始化及部署配置有效降低环境搭建门槛。目前已有79人浏览学习适合作为毕业设计参照、课程设计素材或全栈实战练手项目也可在其基础上继续扩展推荐功能或后台管理模块进行二次开发与功能迭代。1. 解压后这堆代码到底做了什么基于SpringBootVue的热门文创内容推荐平台的第一个30分钟拿到「5b263基于SpringBootVue的热门文创内容推荐平台.zip」这种压缩包第一件事不是急着解压跑demo而是先弄清楚里面装的是一个能交货的项目还是只供课程作业参考的骨架。前后端分离已经是SpringBootVue项目的默认玩法后端SpringBoot把推荐算法、接口和缓存扛下来前端Vue负责把推荐结果渲染成轮播图、瀑布流或信息流。所谓“热门文创内容推荐”核心不是安卓壳子套网页而是服务器端有一套能对文创商品、展览、文章按热度打分排序的规则前端再通过接口把结果展示出来。如果你正准备接手或者复现这类平台这篇笔记能帮你把运行路径、打分逻辑、联调技巧一次捋清楚。2. 先拆后装从SpringBoot后端理解热门推荐的数据流与打分规则2.1 为什么SpringBootVue足够支撑“热门文创”这个场景很多第一次做推荐平台的人会纠结要不要上Elasticsearch、Redis、Spark这些重型组件。对“热门文创内容推荐”这个场景我的结论是SpringBoot加MySQL加Redis够用Vue负责展示不需要一上来就引入大数据计算。文创内容的特点是更新频率中等、数量不是特别大热门推荐本质上是把用户行为数据浏览量、收藏量、点赞量按照时间衰减规律计算出一个热度值再做倒序排列。这个计算量用SpringBoot的定时任务加SQL分页就能顶住。相对而言如果上了Spark或者Flink反而把部署和运维复杂度拉高对中小型文创平台来说不划算。SpringBoot在这个项目里的职责是提供REST接口、执行打分定时任务、管理MySQL中的数据、用Redis缓存热门榜单结果。Vue则负责向用户呈现“今日热门”“本周文创精选”等内容位。前后端分离的好处在于后端接口和前端页面可以分别迭代热门算法调整不牵扯前端重构页面改版不需要动后端逻辑。2.2 后端模块与数据模型评分表、点击表、内容表的关系在正式写代码前先看数据模型。一个典型的热门文创推荐平台至少需要四张核心表CREATE TABLE creative_item ( id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 文创内容ID, title varchar(200) NOT NULL COMMENT 标题, category varchar(50) DEFAULT NULL COMMENT 分类展览/非遗/文创商品/文章, cover_url varchar(500) DEFAULT NULL COMMENT 封面图URL, publish_time datetime DEFAULT NULL COMMENT 发布时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT文创内容表; CREATE TABLE user_behavior ( id bigint(20) NOT NULL AUTO_INCREMENT, item_id bigint(20) NOT NULL COMMENT 文创内容ID, user_id bigint(20) NOT NULL COMMENT 用户ID, behavior_type tinyint(4) NOT NULL COMMENT 行为类型1浏览 2收藏 3点赞 4分享, create_time datetime DEFAULT NULL COMMENT 行为时间, PRIMARY KEY (id), KEY idx_item_time (item_id, create_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户行为表; CREATE TABLE hot_score_cache ( id bigint(20) NOT NULL AUTO_INCREMENT, item_id bigint(20) NOT NULL COMMENT 内容ID, score decimal(10,2) DEFAULT NULL COMMENT 计算出的热度分, calc_time datetime DEFAULT NULL COMMENT 计算时间, PRIMARY KEY (id), KEY idx_calc_time (calc_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT热度分缓存表;这三张表的关系非常直白creative_item存的是文创内容本身比如一件非遗手作、一篇文创深度文章或一场线上展览user_behavior记录用户对这些内容做过什么操作hot_score_cache是定时任务算出来的临时热度结果。其中user_behavior表的索引很关键。很多新手抄作业时只建主键索引到线上跑热门榜单统计时一张百万级行为表按item_id分组汇总能卡到好几秒。idx_item_time这个联合索引是让定时任务从“最近7天行为数据”里捞出统计结果的核心。这里有个经验行为表的create_time一定要用datetime类型且默认CURRENT_TIMESTAMP否则前端联调时传时间格式会有各种幺蛾子。3. 被浏览权重怎么玩SpringBoot热门榜单接口的最小实现3.1 热门评分公式用衰减和权重把“最近的热门”算出来所谓热门不能简单按总量排序否则老内容永远压在头上新内容永无出头之日。常见的做法是引入“时间衰减因子”让越久远的行为权重越低此分数随时间推移以指数衰减。公式可以简化为热度分 浏览量权值 收藏量权值 点赞量权值 分享量权值其中每个权值还要乘以时间衰减系数。对应的Java实现如下/** * 计算单个文创内容的热度分 */ public Double calculateHotScore(Long itemId, ListUserBehavior behaviors) { // 行为权重配置实际开发中放到application.yml里更方便调整 double browseWeight 1.0; double favoriteWeight 3.0; double likeWeight 2.5; double shareWeight 4.0; // 衰减系数通常取0.95~0.98表示每过一天的权重折损 double decayFactor 0.96; double score 0.0; for (UserBehavior behavior : behaviors) { long daysBetween Duration.between( behavior.getCreateTime().toInstant(), LocalDateTime.now().toInstant() ).toDays(); // 超过30天的行为直接忽略减少计算量 if (daysBetween 30) { continue; } double ageWeight Math.pow(decayFactor, daysBetween); switch (behavior.getBehaviorType()) { case 1: // 浏览 score browseWeight * ageWeight; break; case 2: // 收藏 score favoriteWeight * ageWeight; break; case 3: // 点赞 score likeWeight * ageWeight; break; case 4: // 分享 score shareWeight * ageWeight; break; default: break; } } return score; }这个实现的关键参数有三个行为权重、衰减系数decayFactor、时间窗口。行为权重决定哪种营销动作对热度拉升最有效decayFactor决定热门的“新鲜度”时间窗口决定参与计算的数据范围。这里有一个新手常踩的坑Duration.between()对时间敏感如果数据库中create_time存的是本地时间而服务器时区设置成UTC算出daysBetween会差8小时导致评分出现很离谱的边界抖动。解决办法是在SpringBoot的application.yml中把JDBC连接串显式加上serverTimezoneAsia/Shanghai并保证MySQL会话时区与JVM时区一致。3.2 热门榜单接口Redis缓存定时任务Controller三层实现评分逻辑写完之后需要把它包装成一个可调用的接口。常见做法是三层结构定时任务批量算分、Redis缓存结果、Controller接收参数并返回榜单。/** * 定时任务每30分钟重算一次热度分避免每次请求都去跑全量行为表 */ Component public class HotScoreScheduler { private final String HOT_LIST_KEY hot:creative:list; Autowired private StringRedisTemplate redisTemplate; Autowired private CreativeItemMapper itemMapper; Scheduled(cron 0 */30 * * * ?) public void refreshHotList() { // 1. 查出所有在售/上架的文创内容 ListCreativeItem items itemMapper.selectAllPublished(); // 2. 对每个item计算热点分并缓存到Redis for (CreativeItem item : items) { ListUserBehavior behaviors behaviorMapper.selectByItemAndTime( item.getId(), LocalDateTime.now().minusDays(30)); Double score calculateHotScore(item.getId(), behaviors); // 3. ZSet存储score直接作为排序权重 redisTemplate.opsForZSet().add(HOT_LIST_KEY, item.getId().toString(), score); } // 4. 保留前100条淘汰尾部数据 redisTemplate.opsForZSet().removeRange(HOT_LIST_KEY, 100, -1); } }/** * 热门榜单接口支持分页与分类过滤 */ RestController RequestMapping(/api/creative) public class CreativeRecommendController { Autowired private StringRedisTemplate redisTemplate; GetMapping(/hot) public ResultListCreativeItemVO hotList( RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size, RequestParam(required false) String category) { // 从Redis ZSet倒序取出当前热门ID SetString hotIds redisTemplate.opsForZSet().reverseRange( hot:creative:list, (long) (page - 1) * size, (long) page * size - 1); if (hotIds null || hotIds.isEmpty()) { return Result.success(Collections.emptyList()); } ListLong ids hotIds.stream().map(Long::valueOf).collect(Collectors.toList()); ListCreativeItemVO items itemMapper.selectByIds(ids); // 按Redis中的顺序重新排列避免MySQL in查询乱序 MapLong, CreativeItemVO itemMap items.stream() .collect(Collectors.toMap(CreativeItemVO::getId, item - item)); ListCreativeItemVO sorted new ArrayList(); for (String idStr : hotIds) { CreativeItemVO vo itemMap.get(Long.valueOf(idStr)); if (vo ! null (category null || category.equals(vo.getCategory()))) { sorted.add(vo); } } return Result.success(sorted); } }第一个代码块说明了定时任务的核心调用逻辑。用Redis的ZSet有序集合来存热度榜单好处是ZSet天然支持按分数倒序排列reverseRange()接口直接可以取分页数据不用再额外做排序。第二个代码块暴露接口给前端。注意看第3章代码中的Result类这是SpringBoot项目里常见的统一响应包装实际实现时用HashMap也可以但在大项目中提前统一结构能省很多联调扯皮。参数说明方面cron 0 */30 * * * ?表示每30分钟执行一次。文创内容热度的实时性要求不高半小时的缓存窗口完全能接受。如果换成新闻类推荐这个时间间隔就要缩到5分钟以下。4. Vue端怎么把推荐做成轮播路由、组件、请求与联调细节4.1 Vue路由与页面骨架把“推荐首页”接入VueRouter并设置动态参数SpringBoot接口准备就绪后Vue端要做的第一件事是搭路由。热门文创推荐平台一般会把首页、文创详情页、分类页分开其中首页的推荐位是最核心的。// router/index.js import { createRouter, createWebHistory } from vue-router const routes [ { path: /, name: Home, component: () import(../views/HomeView.vue), meta: { title: 热门文创推荐 } }, { path: /detail/:id, name: Detail, component: () import(../views/DetailView.vue), meta: { title: 文创详情 } }, { path: /category/:type, name: Category, component: () import(../views/CategoryView.vue), meta: { title: 分类浏览 } } ] const router createRouter({ history: createWebHistory(), routes }) export default router这里使用路由懒加载() import()这是大型SpringBootVue项目的基本操作。懒加载的效果是首屏只加载首页需要的JS文件而不是把整个项目所有页面的代码一次性打包给浏览器。对于文创推荐平台这种以图片为主的场景首屏加载速度直接决定用户会不会留下来多看几个推荐位。动态路由参数/detail/:id与/category/:type是联调时的常见关注点。比如点击轮播图进入详情页detail/123中的123就是后端返回的itemId。很多新手从详情页返回首页时会发现滚动位置丢失那是没有在HomeView组件里做keep-alive处理这个后面踩坑部分还会提到。4.2 推荐位组件轮播图或瀑布流的请求与渲染首页推荐位的组件化写法是这套东西里最容易出效果的地方。这里给出一个基于Element Plus的轮播图封装方案它替代了传统“图片文字链接”的堆砌让推荐位有“运营感”。template div classhot-recommend el-carousel height320px :interval5000 indicator-positionoutside el-carousel-item v-foritem in hotItems :keyitem.id div classrecommend-card clickgoDetail(item.id) img :srcitem.coverUrl :altitem.title / div classrecommend-info h3{{ item.title }}/h3 span classcategory-tag{{ item.category }}/span span classscore热度值 {{ item.score }}/span /div /div /el-carousel-item /el-carousel /div /template script setup import { ref, onMounted } from vue import { useRouter } from vue-router import axios from axios const router useRouter() const hotItems ref([]) const loading ref(false) // 注意这里的baseURL应与SpringBoot接口前缀保持一致 const apiClient axios.create({ baseURL: /api, timeout: 10000 }) const fetchHotItems async () { loading.value true try { const response await apiClient.get(/creative/hot, { params: { page: 1, size: 5 } }) hotItems.value response.data.data } catch (error) { console.error(推荐位加载失败, error) } finally { loading.value false } } const goDetail (id) { router.push({ path: /detail/${id} }) } onMounted(() { fetchHotItems() }) /script这一段代码的关键点是baseURL: /api。在开发环境下Vue默认端口是5173SpringBoot接口跑在8080跨域问题就出在这里。常规操作是在vite.config.js中配置代理把/api开头的请求转发到后端// vite.config.js export default defineConfig({ server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })如果生产环境前后端不在同一个域名下则需要在Nginx层做同样的反向代理配置。这里要当心代理只解决开发环境的跨域真正上线时如果前端部署在https://www.example.com后端在https://api.example.com那么必须让后端接口支持CORS且前端请求要带withCredentials: true。4.3 页面接口联调时的时间炫耀格式化后端返回的时间不再头大后端返回的publishTime通常是一串时间戳或者带T的ISO格式字符串。Vue中显示“发布于xx小时前”最常见的方式是写一个formatTime过滤器。这里不争论要不要引入day.js直接用原生Date可以解决大部分需求export function formatTime(dateTimeStr) { const date new Date(dateTimeStr) const now Date.now() const diff now - date.getTime() // 小于1小时 if (diff 3600 * 1000) { return ${Math.floor(diff / 60000)}分钟前 } // 小于24小时 if (diff 24 * 3600 * 1000) { return ${Math.floor(diff / (3600 * 1000))}小时前 } // 小于7天 if (diff 7 * 24 * 3600 * 1000) { return ${Math.floor(diff / (24 * 3600 * 1000))}天前 } // 过久的内容直接返回完整日期 return ${date.getFullYear()}-${date.getMonth() 1}-${date.getDate()} }这里有一个非常隐蔽的坑SpringBoot默认返回的LocalDateTime会被Jackson序列化成2025-02-18T14:30:00这种带T的格式Vue的new Date()可以直接解析。但如果后端字段类型是Date且配置了spring.jackson.date-format返回的可能是时间戳数字那就要注意单位是秒还是毫秒。秒需要乘以1000才能交给new Date()这是联调时最容易出现“显示NaN”的原因。5. 避坑记录SpringBootVue联调中最常翻车的5个位置5.1 页面白屏路由懒加载与Element Plus按需引入冲突现象npm run build后部署到Nginx首页能打开但点击进入详情页时白屏控制台报“Cannot find module chunk”。原因路由懒加载和Element Plus组件库按需引入配合不当。常见于主包引用ElCarousel而子页面引用ElButton当构建分包时公共组件没有被正确提取子路由加载时找不到对应的JS chunk。解决确认使用unplugin-vue-components的ElementPlusResolver实现按需引入同时不要手动在main.js里全量引入ElementPlus两套方案同时用会导致chunk依赖错乱。另外Nginx的try_files也要配index.html否则刷新子路由时返回404页面自然白屏。5.2 前后端联调时“已登录”状态丢失Cookie跨域配置现象前端登录成功切换到其他接口后提示未授权或者SpringBoot后端打印出的HttpSession每次都是新会话。原因前端与后端域名不同请求拦截器里没有携带Cookie。Vue的axios默认跨域请求不带Cookie而后端SpringBoot又没有配置Access-Control-Allow-Credentials: true所以登录态根本传不到后端。解决前端axios实例加上withCredentials: true后端配置CORS时不能让AllowedOrigin为*必须指定具体前端域名同时开启allowCredentials。Nginx代理模式下还要检查proxy_set_header Cookie $http_cookie;是否被注释掉。这个坑非常隐蔽往往查一个下午最后发现是Nginx把Cookie吞了。5.3 热门推荐接口返回404PathVariable和路由参数对不上现象点击详情页请求/api/creative/detail/123SpringBoot返回404或者前端请求发出的是/api/creative/detail?id123。原因前端router.push({ path: /detail/ id })拼的是路径参数但后端Controller写的是RequestParam(id) Long id两者形式不一致。解决后端详情接口统一使用PathVariableGetMapping(/detail/{id}) public ResultCreativeItemDetailVO detail(PathVariable(id) Long id) { ... }联调时最简单的方式是看浏览器Network面板中URL的形状后缀是/123还是?id123一目了然。这个问题本质上是前后端约定的问题建议项目启动时就把接口文档格式定死省得后面反复调。5.4 接口正常但页面显示“数据不在”Long型主键精度丢失现象列表接口正常返回但点击详情页后显示的内容与列表不一致甚至跳到默认404页。原因Java后端主键是Long类型当ID超过JavaScript的Number.MAX_SAFE_INTEGER9007199254740991时JSON序列化后前端拿到的是精度丢失的数字。解决在SpringBoot后端的实体类主键字段上增加注解JsonSerialize(using ToStringSerializer.class) private Long id;如果项目使用Jackson也可以配置全局规则把Long统一转成String输出。主键不参与计算字符串形式对前端更友好。5.5 定时器跑完后榜单没变化Redis序列化策略不一致现象重启SpringBoot后第一次接口返回正常之后修改了数据库数据重跑定时任务榜单依然是重启前的数据。原因SpringBoot默认的RedisTemplate使用JDK序列化而StringRedisTemplate使用String序列化。如果定时任务往Redis写入时用的是StringRedisTemplate读取时用的是RedisTemplate两个客户端存取键值时的编码不同导致实际读不到新数据。解决统一使用StringRedisTemplate假若你要主动用RedisTemplate则需显式设置序列化器。推荐方案是封装一个专用的RedisService内部统一使用StringRedisTemplate这样避免不同开发人员直接操作RedisTemplate造成风格混用。6. 收尾技巧把人工运营权重和后端验证方法拧进推荐逻辑热门平台活下来的关键在于推荐位不只是算法说了算还需要人工干预。文创内容有其特殊性比如某件非遗作品正在参加线下展览短期曝光价值极高但它的基础浏览数据远低于老内容。常见操作是给creative_item表加一个manual_weight字段在打分时叠加一个“运营加权重”让编辑推的内容能临时置顶。public Double calculateFinalScore(CreativeItem item, Double baseHotScore) { // 运营加权范围建议0~10数值越大越优先展示 Double manualWeight item.getManualWeight(); if (manualWeight null) { return baseHotScore; } // 加一个时间窗避免手动置顶内容永久霸榜 if (item.getWeightExpireTime() ! null item.getWeightExpireTime().isAfter(LocalDateTime.now())) { return baseHotScore * (1 manualWeight); } return baseHotScore; }这是我在实际项目里运用的方式运营给出的不是固定置顶而是加权系数保证了推荐列表仍然是“热度为主人工为辅”不会破坏算法整体的排序结构。验证推荐效果是很多团队忽略的一步。上线后看两个指标就够推荐位内容的点击率CTR和用户停留时长。以一周为周期对比推荐位改版前后的数据发现点击率提升超过10%才算有效果。如果只是换了皮肤没有数据变化建议去检查推荐内容是否符合目标用户的喜好而不是继续调权重。最后说一个调试习惯SpringBoot项目排查热门接口问题别只看Controller日志。把application.yml中的日志级别调到debug重点观察HotScoreScheduler的执行时间和Redis操作耗时。我曾遇到过定时任务被Spring的默认单线程执行器阻塞原因是同时跑了多个Scheduled任务且没有配置线程池热门榜单整整两个小时没更新线上用户全都看到了旧数据。后来在配置类里增加TaskScheduler线程池参数问题才彻底解决。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

AI“神仙打架”别焦虑!TaoToken 统一 Key 配置指南,小白也能抓住大模型红利入局 2026/9/26 15:19:47

AI“神仙打架”别焦虑!TaoToken 统一 Key 配置指南,小白也能抓住大模型红利入局

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
VSCode常用插件大全:用TaoToken统一Key接入AI编程插件的config.json配置骨架 2026/9/26 15:19:40

VSCode常用插件大全:用TaoToken统一Key接入AI编程插件的config.json配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
复盘 OpenClaw 惊魂 48 小时:从 CVE-2026-25253 看 WebSocket Token 泄露链路与 TaoToken 配置加固 2026/9/26 15:19:40

复盘 OpenClaw 惊魂 48 小时:从 CVE-2026-25253 看 WebSocket Token 泄露链路与 TaoToken 配置加固

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
DGX Spark GB10 双机部署实战:Qwen3.8-27B 与 DeepSeek-V4-Flash 的 TP=2 推理优化 2026/9/26 15:19:34

DGX Spark GB10 双机部署实战:Qwen3.8-27B 与 DeepSeek-V4-Flash 的 TP=2 推理优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Focusky技术演示实战:从画布规划到多格式导出的完整指南 2026/9/26 15:19:34

Focusky技术演示实战:从画布规划到多格式导出的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
HTOOL-SA6000:可编程射频测量节点的产线集成实战指南 2026/9/26 15:19:34

HTOOL-SA6000:可编程射频测量节点的产线集成实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉