項目編號: S 023 ,文末獲取源碼。 \color{red}{項目編號:S023,文末獲取源碼。} 項目編號:S023,文末獲取源碼。
目錄
- 一、摘要
- 1.1 項目介紹
- 1.2 項目錄屏
- 二、功能模塊
- 2.1 用戶端
- 2.2 管理員端
- 三、系統展示
- 四、核心代碼
- 4.1 隨機景點推薦
- 4.2 景點評價
- 4.3 協同推薦算法
- 4.4 網站登錄
- 4.5 查詢景點美食
- 五、免責說明
一、摘要
1.1 項目介紹
基于Vue+SpringBoot+MySQL的海南旅游推薦系統,基于協同推薦算法,包括用戶網頁和管理后臺,包含景點類型模塊、旅游景點模塊、行程推薦模塊、美食推薦模塊、景點排名模塊,還包含系統自帶的用戶管理、部門管理、角色管理、菜單管理、日志管理、數據字典管理、文件管理、圖表展示等基礎模塊,海南旅游推薦系統基于角色的訪問控制,給景點管理員、游客使用,可將權限精確到按鈕級別,您可以自定義角色并分配權限,系統適合設計精確的權限約束需求。
1.2 項目錄屏
源碼下載
二、功能模塊
2.1 用戶端
- 景點推薦:根據用戶個性化偏好給用戶推薦感興趣的景點
【景點信息包含:景點名稱、景點類型、評分、收藏量、門票價格、門票預訂(提供購買鏈接,用戶可以通過點擊鏈接到其他平臺購買門票)、開放時間、景區地址(所在市區、詳細地址)、景點介紹】 - 景點篩選:用戶可通過設置自己想要的景點類型、景點門票價格范圍、景區地址(海口市、三亞市、儋州市、三沙市等)來篩選滿足自身需求的景點
篩選:【注:若用戶只設置了一個篩選條件則只需滿足一個篩選條件就推薦給用戶,若設置兩個以上,則需都滿足才給用戶推薦】 - 旅游攻略:用戶可以通過搜索景點名稱來獲取景點周邊美食以及行程路線的相關信息
(1)交通指南:起點、終點、交通方式、行程路線
(2)周邊美食:美食圖片、名稱、類型、簡介、人均消費 - 景點數據:景點數據可視化
(1)好評度排名:管理員可以看到好評度高的前十個景點【排名、景點名稱、好評度】
(2)景點收藏量:管理員可以看到收藏量排名前十的景點【排名、景點名稱、收藏量】 - 個人中心:
(1)個人信息:賬號、姓名、聯系方式、身份證號(用戶可以更新個人信息、退出登錄)
(2)景點收藏:用戶可以查看、取消收藏過的景點
2.2 管理員端
- 個人中心:管理員個人信息
- 景點信息管理:
(1)查詢:可通過搜索景點名稱、地址、景點類型來獲取需要的景點數據(搜索到需要的景點數據后可進行查看、修改、刪除景點信息操作)
(2)添加:可以添加新的景點信息 - 用戶信息管理:
(1)查詢:可通過搜索用戶賬號來查詢需要的用戶(查詢到需要的用戶后可對用戶信息進行查看、修改、刪除操作)
(2)添加:可添加新用戶信息 - 行程信息管理:
(1)查詢:可通搜索景點地址來獲取景點行程路線信息(查詢到需要的行程信息后可對其進行查看、修改、刪除操作)
(2)添加:可添加信息 - 美食信息管理:
(1)查詢:可通搜索景點地址來獲取景點周邊美食信息(查詢到需要的信息后可對其進行查看、修改、刪除操作)
(2)添加:可添加新的美食信息 - 景點數據:景點數據可視化(同用戶端的景點數據可視化)
(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>() {@Overridepublic 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 留言,看到后會第一時間回復,相關意見會酌情考慮,但沒有一定被采納的承諾或保證。
下載本系統代碼或使用本系統的用戶,必須同意以下內容,否則請勿下載!
- 出于自愿而使用/開發本軟件,了解使用本軟件的風險,且同意自己承擔使用本軟件的風險。
- 利用本軟件構建的網站的任何信息內容以及導致的任何版權糾紛和法律爭議及后果和博主無關,博主對此不承擔任何責任。
- 在任何情況下,對于因使用或無法使用本軟件而導致的任何難以合理預估的損失(包括但不僅限于商業利潤損失、業務中斷與業務信息丟失),博主概不承擔任何責任。
- 必須了解使用本軟件的風險,博主不承諾提供一對一的技術支持、使用擔保,也不承擔任何因本軟件而產生的難以預料的問題的相關責任。