【开源】基于SpringBoot的海南旅游景点推荐系统的设计和实现

news2024/7/6 19:08:47

在这里插入图片描述


目录

  • 一、摘要
    • 1.1 项目介绍
    • 1.2 项目录屏
  • 二、功能模块
    • 2.1 用户端
    • 2.2 管理员端
  • 三、系统展示
  • 四、核心代码
    • 4.1 随机景点推荐
    • 4.2 景点评价
    • 4.3 协同推荐算法
    • 4.4 网站登录
    • 4.5 查询景点美食
  • 五、免责说明


一、摘要

1.1 项目介绍

基于Vue+SpringBoot+MySQL的海南旅游推荐系统,基于协同推荐算法,包括用户网页和管理后台,包含景点类型模块、旅游景点模块、行程推荐模块、美食推荐模块、景点排名模块,还包含系统自带的用户管理、部门管理、角色管理、菜单管理、日志管理、数据字典管理、文件管理、图表展示等基础模块,海南旅游推荐系统基于角色的访问控制,给景点管理员、游客使用,可将权限精确到按钮级别,您可以自定义角色并分配权限,系统适合设计精确的权限约束需求。

项目编号: S 023 ,源码已在 B i l i b i l i 中上架,需要的朋友请自行下载。 \color{red}{项目编号:S023,源码已在 Bilibili 中上架,需要的朋友请自行下载。} 项目编号:S023,源码已在Bilibili中上架,需要的朋友请自行下载。

https://gf.bilibili.com/item/detail/1104039029

为了帮助小白入门 Java,博主录制了本项目配套的《项目手把手启动教程》,希望能给同学们带来帮助。

1.2 项目录屏


二、功能模块

2.1 用户端

  1. 景点推荐:根据用户个性化偏好给用户推荐感兴趣的景点
    【景点信息包含:景点名称、景点类型、评分、收藏量、门票价格、门票预订(提供购买链接,用户可以通过点击链接到其他平台购买门票)、开放时间、景区地址(所在市区、详细地址)、景点介绍】
  2. 景点筛选:用户可通过设置自己想要的景点类型、景点门票价格范围、景区地址(海口市、三亚市、儋州市、三沙市等)来筛选满足自身需求的景点
    筛选:【注:若用户只设置了一个筛选条件则只需满足一个筛选条件就推荐给用户,若设置两个以上,则需都满足才给用户推荐】
  3. 旅游攻略:用户可以通过搜索景点名称来获取景点周边美食以及行程路线的相关信息
    (1)交通指南:起点、终点、交通方式、行程路线
    (2)周边美食:美食图片、名称、类型、简介、人均消费
  4. 景点数据:景点数据可视化
    (1)好评度排名:管理员可以看到好评度高的前十个景点【排名、景点名称、好评度】
    (2)景点收藏量:管理员可以看到收藏量排名前十的景点【排名、景点名称、收藏量】
  5. 个人中心:
    (1)个人信息:账号、姓名、联系方式、身份证号(用户可以更新个人信息、退出登录)
    (2)景点收藏:用户可以查看、取消收藏过的景点

2.2 管理员端

  1. 个人中心:管理员个人信息
  2. 景点信息管理:
    (1)查询:可通过搜索景点名称、地址、景点类型来获取需要的景点数据(搜索到需要的景点数据后可进行查看、修改、删除景点信息操作)
    (2)添加:可以添加新的景点信息
  3. 用户信息管理:
    (1)查询:可通过搜索用户账号来查询需要的用户(查询到需要的用户后可对用户信息进行查看、修改、删除操作)
    (2)添加:可添加新用户信息
  4. 行程信息管理:
    (1)查询:可通搜索景点地址来获取景点行程路线信息(查询到需要的行程信息后可对其进行查看、修改、删除操作)
    (2)添加:可添加信息
  5. 美食信息管理:
    (1)查询:可通搜索景点地址来获取景点周边美食信息(查询到需要的信息后可对其进行查看、修改、删除操作)
    (2)添加:可添加新的美食信息
  6. 景点数据:景点数据可视化(同用户端的景点数据可视化)
    (1)好评度排名:管理员可以看到好评度高的前十个景点【排名、景点名称、好评度】
    (2)景点收藏量:管理员可以看到收藏量排名前十的景点【排名、景点名称、收藏量】

三、系统展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


四、核心代码

4.1 随机景点推荐

@RequestMapping(value = "/getRecommendList2OnWeb", method = RequestMethod.GET)
@ApiOperation(value = "查询推荐的景点")
public Result<List<ScenicSpot>> getRecommendList2(){
    List<ScenicSpot> spotList = iScenicSpotService.list();
    int[] arr = new int[spotList.size()];
    for(int i = 1; i < spotList.size(); i ++) {
        arr[i - 1] = i;
    }
    int[] ints = selectM(arr, 10);
    List<ScenicSpot> ans = new ArrayList<>();
    for (int i : ints) {
        ans.add(spotList.get(i));
    }
    return new ResultUtil<List<ScenicSpot>>().setData(ans);
}

public static int[] selectM(int[] arr,int m){
    int len=arr.length;
    if(m>arr.length) {
        throw new RuntimeException("xxxxx");
    }
    int[] res=new int[m];
    for(int i=0;i<m;i++){
        int randomIndex=len-1-new Random().nextInt(len-i);
        res[i]=arr[randomIndex];
        int tmp=arr[randomIndex];
        arr[randomIndex]=arr[i];
        arr[i]=tmp;
    }
    return res;
}

4.2 景点评价

@RequestMapping(value = "/addEvaluate", method = RequestMethod.GET)
@ApiOperation(value = "新增评价")
public Result<Evaluate> addEvaluate(@RequestParam String id, @RequestParam BigDecimal level, @RequestParam String message){
    ScenicSpot ss = iScenicSpotService.getById(id);
    if(ss == null) {
        return ResultUtil.error("景点不存在");
    }
    User currUser = securityUtil.getCurrUser();
    QueryWrapper<Evaluate> qw = new QueryWrapper<>();
    qw.eq("spot_id",ss.getId());
    qw.eq("user_id",currUser.getId());
    qw.last("limit 1");
    Evaluate evaluate = iEvaluateService.getOne(qw);
    if(evaluate == null) {
        evaluate = new Evaluate();
        evaluate.setSpotId(ss.getId());
        evaluate.setSpotName(ss.getTitle());
        evaluate.setUserId(currUser.getId());
        evaluate.setUserName(currUser.getNickname());
    }
    evaluate.setLevel(level);
    evaluate.setMessage(message);
    evaluate.setTime(DateUtil.now());
    iEvaluateService.saveOrUpdate(evaluate);
    return ResultUtil.success();
}

4.3 协同推荐算法

@Scheduled(cron = "0 0/1 * * * ?")
@ApiOperation(value = "景点数据更新")
public void job(){
    List<ScenicSpot> spotList = iScenicSpotService.list();
    for (ScenicSpot vo : spotList) {
        Long evaluateSum = 0L;
        QueryWrapper<Evaluate> evalQw = new QueryWrapper<>();
        evalQw.eq("spot_id",vo.getId());
        List<Evaluate> evaluateList = iEvaluateService.list(evalQw);
        for (Evaluate evaluate : evaluateList) {
            evaluateSum += evaluate.getLevel().longValue();
        }
        // 收藏 10分
        QueryWrapper<Collection> coQw = new QueryWrapper<>();
        coQw.eq("spot_id",vo.getId());
        evaluateSum += iCollectionService.count(coQw);
        // 浏览 1分
        String viewStr = redisTemplate.get("SPOT_VIEW:" + vo.getId());
        if(!ZwzNullUtils.isNull(viewStr)) {
            try {
                long viewNumber = Long.parseLong(viewStr);
                evaluateSum += viewNumber;
            } catch (Exception e) {}
        }
        vo.setValue(evaluateSum);
    }
    Collections.sort(spotList, new Comparator<ScenicSpot>() {
        @Override
        public int compare(ScenicSpot o1, ScenicSpot o2) {
            return (int)(o2.getValue() - o1.getValue());
        }
    });
    if(spotList.size() > 10) {
        spotList = spotList.subList(0,10);
    }
    for (ScenicSpot vo1 : spotList) {
        // 评分
        BigDecimal evaluateSum = BigDecimal.ZERO;
        QueryWrapper<Evaluate> evalQw = new QueryWrapper<>();
        evalQw.eq("spot_id",vo1.getId());
        List<Evaluate> evaluateList = iEvaluateService.list(evalQw);
        for (Evaluate evaluate : evaluateList) {
            evaluateSum = evaluateSum.add(evaluate.getLevel());
        }
        if(evaluateList.size() > 0) {
            vo1.setStar(evaluateSum.divide(BigDecimal.valueOf(evaluateList.size()),2, RoundingMode.DOWN));
        } else {
            vo1.setStar(BigDecimal.valueOf(-1));
        }
        // 收藏
        QueryWrapper<Collection> coQw = new QueryWrapper<>();
        coQw.eq("spot_id",vo1.getId());
        vo1.setCollection(iCollectionService.count(coQw));
    }
    redisTemplate.set("SPOT_JOB_DATA", JSON.toJSONString(spotList));
    System.out.println("缓存完毕!");
}

4.4 网站登录

@RequestMapping(value = "/loginOnWeb", method = RequestMethod.GET)
@ApiOperation(value = "网站前台登陆")
public Result<String> loginOnWeb(@RequestParam String userName, @RequestParam String password){
    QueryWrapper<User> qw = new QueryWrapper<>();
    qw.eq("username",userName);
    List<User> userList = iUserService.list(qw);
    if(userList.size() < 1) {
        return ResultUtil.error("用户不存在");
    }
    User user = userList.get(0);
    if(!new BCryptPasswordEncoder().matches(password, user.getPassword())){
        return ResultUtil.error("密码不正确");
    }
    String accessToken = securityUtil.getToken(user.getUsername(), true);
    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(new SecurityUserDetails(user), null, null);
    SecurityContextHolder.getContext().setAuthentication(authentication);
    return new ResultUtil<String>().setData(accessToken);
}

4.5 查询景点美食

@RequestMapping(value = "/getByPage", method = RequestMethod.GET)
@ApiOperation(value = "查询美食")
public Result<IPage<DeliciousFood>> getByPage(@ModelAttribute DeliciousFood deliciousFood ,@ModelAttribute PageVo page){
    QueryWrapper<DeliciousFood> qw = new QueryWrapper<>();
    if(!ZwzNullUtils.isNull(deliciousFood.getTitle())) {
        qw.like("title",deliciousFood.getTitle());
    }
    if(!ZwzNullUtils.isNull(deliciousFood.getContent())) {
        qw.like("content",deliciousFood.getContent());
    }
    if(!ZwzNullUtils.isNull(deliciousFood.getSpotId())) {
        qw.eq("spot_id",deliciousFood.getSpotId());
    }
    IPage<DeliciousFood> data = iDeliciousFoodService.page(PageUtil.initMpPage(page),qw);
    return new ResultUtil<IPage<DeliciousFood>>().setData(data);
}

五、免责说明

  • 本项目仅供个人学习使用,商用授权请联系博主,否则后果自负。
  • 博主拥有本软件构建后的应用系统全部内容所有权及独立的知识产权,拥有最终解释权。
  • 如有问题,欢迎在仓库 Issue 留言,看到后会第一时间回复,相关意见会酌情考虑,但没有一定被采纳的承诺或保证。

下载本系统代码或使用本系统的用户,必须同意以下内容,否则请勿下载!

  1. 出于自愿而使用/开发本软件,了解使用本软件的风险,且同意自己承担使用本软件的风险。
  2. 利用本软件构建的网站的任何信息内容以及导致的任何版权纠纷和法律争议及后果和博主无关,博主对此不承担任何责任。
  3. 在任何情况下,对于因使用或无法使用本软件而导致的任何难以合理预估的损失(包括但不仅限于商业利润损失、业务中断与业务信息丢失),博主概不承担任何责任。
  4. 必须了解使用本软件的风险,博主不承诺提供一对一的技术支持、使用担保,也不承担任何因本软件而产生的难以预料的问题的相关责任。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1139625.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pk答题小程序怎么做?功能有哪些?为您揭秘!

“微信答题小程序&#xff08;仿头脑王者源码&#xff09;在线教育考试pk答题小程序”是一款专业性的答题小程序&#xff08;软著登记号&#xff1a;4078210号&#xff09;&#xff0c;主要功能有&#xff1a;个人每日答题、邀请好友一对一PK答题、排位升级PK答题、专题1V1pk答…

ASO优化之关于Google Play中的搜索引擎优化

在进行Google搜索时&#xff0c;被显示的结果数量淹没的情况并不少见&#xff0c;我们可以优化搜索引擎&#xff0c;排除特定单词或术语并获得更相关的结果。 1、关键词搜索。 排除与搜索查询不相关的关键词。通过使用搜索运算&#xff0c;我们可以指示Google忽略包含某些字词…

Lego-Laom算法深度解析

文章目录 参考链接系统概述1.点云分割模块1.1 生成距离图像1.2 地面点的初步筛选1.3 基于BFS的点云分割 2.特征检测2.1 特征提取2.1.1 点云去畸变 2.2 特征提取 3雷达里程计3.1 特征匹配3.2 位姿估计 4.地图构建4.1 全局地图4.2 局部地图 5.性能评价-对标LOAM算法5.1特征点数量…

Find My钱包|苹果Find My技术与钱包结合,智能防丢,全球定位

传统钱包就是人们随身携带装钱或者其他小件物品的东西。钱包可以把钱和银行卡、信用卡等货币工具收集到一起装在一个小包内&#xff0c;便于随身携带使用&#xff0c;通常有巴掌大小。如今钱包除了以上作用外还可以是随身装饰品或当家庭照片夹使用。 如今我们大多数用的是电子…

国产CAN总线收发芯片DP1042 兼容替换TJA1042

说明 1 简述 DP1042是一款应用于 CAN 协议控制器和物理总线之间的接口芯片&#xff0c;可应用于卡车、公交、小汽车、工业控制等领域&#xff0c;支持 5Mbps CAN FD 灵活数据速率&#xff0c;具有在总线与 CAN 协议控制器之间进行差分信号传输的能力&#xff0c;完全兼容“ISO…

使用BufferWriter进行文件的写入操作

public class BufferedWirter_ {public static void main(String[] args) throws IOException {String filepath "e:\\ABC.txt";//创建BufferedWrite对象BufferedWriter bufferedWriter new BufferedWriter(new FileWriter(filepath,true));//BufferedWriter 并没有…

ENSP模拟CE12800 SSH一键配置小工具

平时做自动化开发实验都是以SSH连接为基底的,经常做完一个实验新建实验又要去重新配置SSH,就会比较麻烦,所以自己做了个偷懒小工具分享给大家 使用方法: 1.小工具图 2.拓扑图 拓扑图 保证1/0/0口和云连接 3.小工具各个控件介绍 3.1 串口号:右键点击拓扑图中的12800交换机,…

MySQL篇---第一篇

系列文章目录 文章目录 系列文章目录一、数据库的三范式是什么二、MySQL数据库引擎有哪些三、说说InnoDB与MyISAM的区别一、数据库的三范式是什么 第一范式:列不可再分 第二范式:行可以唯一区分,主键约束 第三范式:表的非主属性不能依赖与 其他表的非主属性 外键约束 且三…

物流数字化转型有什么意义?

物流领域的数字化转型具有深远的意义&#xff0c;因为它彻底改变了供应链中的货物和信息流动方式。这一转变是由数字技术集成推动的&#xff0c;旨在提高效率、可见性和客户满意度。以下是其重要性的一些关键方面&#xff1a; 1.提高效率和降低成本&#xff1a;数字化转型通过…

tinymce新增多图片上传功能

效果&#xff1a; /*** 文件路径&#xff1a;/src/tinymce/plugins/images/index.js*/ import request from "/utils/request"; tinymce.PluginManager.add(images, function (editor) {let pluginName 多图上传let bodyId editor.getParam(body_id, , hash)var…

windows使用cygwin编译Xyce

windows使用cygwin编译Xyce 整体流程如Xyce构建教程大致类似&#xff0c;我这里把我编译的步骤写下来&#xff0c;方便大家食用 环境准备&#xff1a; win10及以上操作系统 cygwin安装包&#xff1a;Cygwin trillinos源码&#xff1a;Trillinos Xyce源码&#xff1a;Xyce …

TimeGPT:时间序列预测的第一个基础模型

时间序列预测领域在最近的几年有着快速的发展&#xff0c;比如N-BEATS、N-HiTS、PatchTST和TimesNet。 大型语言模型(llm)最近在ChatGPT等应用程序中变得非常流行&#xff0c;因为它们可以适应各种各样的任务&#xff0c;而无需进一步的训练。 这就引出了一个问题:时间序列的…

21款奔驰GLE450升级23P驾驶辅助 缓解开车疲劳

驾驶辅助和自动驾驶的区别就是需要人为去接管&#xff0c;虽然车辆会根据道路自己行驶&#xff0c;弯道上也能居中自动修正行驶&#xff0c;长时间不接管方向盘&#xff0c;系统会提示人为接管&#xff0c;这就是奔驰的23P驾驶辅助系统&#xff0c; 很多车友升级23P驾驶辅助系…

跨境商城开发秘籍揭密:如何选择最适合你的技术方案?

在全球化的商业环境下&#xff0c;跨境电商正蓬勃发展&#xff0c;成为许多企业拓展国际市场的首选方式。而跨境商城开发作为实现这一目标的关键&#xff0c;选择合适的技术方案显得尤为重要。本文将揭示跨境商城开发的秘籍&#xff0c;为您提供权威的指导&#xff0c;助力您选…

Mysql数据库 4.SQL语言 DQL数据查询语言 查询

DQL数据查询语言 从数据表中提取满足特定条件的记录 1.单表查询 2.多表查询 查询基础语法 select 关键字后指定要查询到的记录的哪些列 语法&#xff1a;select 列名&#xff08;字段名&#xff09;/某几列/全部列 from 表名 [具体条件]&#xff1b; select colnumName…

如何助力企业出海?未来发展趋势是什么?尽在「云通信」Tech专场

2023杭州云栖大会 倒计时4天&#xff01; 阿里云云通信 2大并行Session 6场话题演讲 今日「云通信」Tech 议程内容抢先知晓 01 「云通信」Tech • 国内企业出海&#xff0c;如何更高地提升市场营销的ROI&#xff0c;提升客户的转化率&#xff1f; • 面对海外存量客户&a…

数据库简介和sqlite3安装

数据库就是存储数据的仓库&#xff0c;其本质是一个文件系统&#xff0c;数据按照特定的格式将数据存储起来&#xff0c;用户可以对数据库中的数据进行增加&#xff0c;修改&#xff0c;删除及查询操作。 严格意义上来说,"数据库"不能被称之为"数据库",而…

GoLong的学习之路(十四)语法之标准库 time(时间包)的使用

文章目录 time包跨时区时间戳时间间隔时间操作addSubEqualBeforeAfter 定时器时间格式化解析字符串格式的时间 time包 时间和日期是我们编程中经常会用到的&#xff0c;本文主要介绍了 Go 语言内置的 time 包的基本用法。 time 包提供了一些关于时间显示和测量用的函数。time…

Mac 安装nvm

安装方案&#xff1a; 1. 从github下载nvm仓库到 ~/目录 地址&#xff1a;https://github.com/nvm-sh/nvm.git git clone https://github.com/nvm-sh/nvm.git 2. 进入nvm目录中执行install.sh等待执行完成&#xff0c;执行的操作方法就是直接将文件拖入到终端然后回车。 3.…

微信小程序 slot 不显示

问题:创建组件&#xff0c;使用带名字的slot&#xff0c;页面调用组件使用slot不显示 源码&#xff1a; 组件xml <view class"p-item br24" style"{{style}}"><slot name"right" wx:if"{{!custBottom}}"></slot>&l…