旅游攻略页详解
需求分析
1.排行榜页的显示
2.排行榜的定期更新
3.主题推荐
4.旅游攻略导航
第一部分(排行榜页的显示)
在前端就可以看到需要的攻略一共有三个,海外,国内和热门,需要用一个type字段来区分,然后根据浏览数去排序就好了,具体的排序依据还要看到时候的业务需要
依旧是f12起手去看接口

非常的明显的一个rank字样代表排行榜,对应的是StrategyRanks中的rank方法,传了一个type参数,发送请求的是get方法
接下来确定返回值类型,这个需要去前端看
<div class="nav-title"><h3>海外攻略推荐排行</h3><b class="gn-arrow"></b></div><div class="nav-panel rank-panel"><ol><li v-for="(item, index) in rankList1" :key="item.id"><em class="rank-num">{{ index + 1 }}</em><strong><a :href="`/destination/details/${item.destId}`" target="_blank">{{ item.destName }}</a></strong><a :href="`/strategy/article/${item.strategyId}`" target="_blank">{{ item.strategyTitle }}</a></li></ol></div></div>const rankList1 = ref([])
const rankList2 = ref([])
const rankList3 = ref([])
const themeList = ref([])onMounted(() => {nextTick(() => {getBannerStrategy()getStrategtRank(1)getStrategtRank(2)getStrategtRank(3)getStrategyTheme()})
从这段代码中可以看出前端需要的是一个可以遍历的list,需要的数据是item.后面的字段名,rank表中的数据完全符合,直接返回List<>泛型里面装一个rank的实体类,然后参数type是用直接传的方式,我们直接在参数列表去接,这样controller中的代码就可以确定了,能copy的就不要手搓,不然敲错了是真的难受
@GetMapping("/rank")public R<List<StrategyRank>> rank(Long type) {return R.ok(strategyRankService.queryRank(type));}
下面去业务层,前端传来了一个用来确定状态(海外,国内,热门)的type,直接使用条件构造器首先根据type查询然后,然后根据statisnum字段进行降序排序,这里需要注意的是我们的需求二存在更新排行耪一说,所以我们需要加入对最新时间的筛选,代码如下
//切记insql中的字段是数据库的字段而不是实体类中的数据,不要敲错了return lambdaQuery().inSql(StrategyRank::getStatisTime, "select max(statis_time) from ta_strategy_rank ").eq(StrategyRank::getType, type).orderByDesc(StrategyRank::getStatisnum).last("limit 10").list();
这样就可以成功查询排行榜内容并进行显示了,效果如下

第二部分(排行榜的定期更新)
本节的定期更新涉及定时任务

在若依的后端管理系统可以看到有一个已经写好的定时任务的模块,可以在里面添加定时任务

我们只需要在该页面完成各项的添加,便能在定时任务的界面随时控制定时任务的开关,最关键的便是添加方法,这是后端要去做的
在项目中定时任务一定是有一个专属的服务去完成的,我们本部分想要做的是排行榜的定时更新,也就是将排行榜的更新作为定时任务,我们需要去完成这个定时更新
在完成顺序上个人建议是一定要最后写业务逻辑,先要让这个方法调用能通,如果一下子全写完报错的话会无从下手的
第一步,远程接口中定义对应的远程调用接口,更新不需要返回数据,也没有什么参数,唯一需要的只有请求来源,放在请求头中,用于区分你的请求是从哪里来的,接口代码如下
@PostMapping("/strategies/feign/statisRank")R<?> statisRank(@RequestHeader(SecurityConstants.FROM_SOURCE) String source);
每写一个这种远程调用的接口都需要一个降级类,就是当你这个接口调用失败之后,会作何处理的类,不写的话,降级类(fallback)会报错的,这个写起来也简单,只要去降级类实现对应的方法即可
@Overridepublic R<?> statisRank(String source) {return R.fail(Lists.newArrayList(), "查询攻略排行失败:" + throwable.getMessage());}
这样远程调用的接口部分就写好了
第二步,完成排行榜服务的接口和业务(测试版)
根据上面的远程调用接口直接复制,加一个private权限修饰符,完成方法体,调用service去完成对应的更新方法即可controller接口如下
@PostMapping("/feign/statisRank")public R<?> statisRank(){strategyService.queryRank();return R.ok();};
然后去实现queryRank()方法,这里简单写一个打印的操作,一会儿测试一下是否能通
第三步,在定时任务服务中创建我们对应的任务类,然后添加注解@Component(对应的任务名,首字母小写),最后写方法(权限修饰符是public,不然别人读不到了),这里要用我们远程调用接口写的方法,所以需要添加依赖,然后把接口注入去调用对应的方法
@Component("strategyTask")
public class StrategyTask {@Autowiredprivate RemoteStrategyService remoteStrategyService;public void statisRank(){remoteStrategyService.statisRank("inner");}
}
接下来去后端管理系统中做一个测试,写法如图所示,然后在管理界面把启动按钮打开


在后端的控制台中看到结果成功显示,说明我们的定时任务已经通了,可以成功调用更新方法,接下来就是最后一步,完成排行榜更新业务

再次看这个排行榜,本身的数据是从rank表取出来的,但是更新的数据是从攻略表取出来的,因为排行榜本身的数据就是从攻略表排序取出来的,接下来我们来厘清一下思路,首先确定rank表需要的数据,然后从攻略表按条件查询,最后从查到的每条数据中把rank表需要的数据填满即可。接下来看看代码
Date now = new Date();//查国外List<Strategy> abroadList = lambdaQuery().eq(Strategy::getIsabroad, 1).orderByDesc(Strategy::getViewnum).last("limit 10").list();List<StrategyRank> abroadRank = new ArrayList<>();addRank(abroadList, now,abroadRank,1L);//查国内List<Strategy> domesticList = lambdaQuery().eq(Strategy::getIsabroad, 0).orderByDesc(Strategy::getViewnum).last("limit 10").list();List<StrategyRank> domesticRank = new ArrayList<>();addRank(domesticList, now,domesticRank,2L);//查热门List<Strategy> hotList = lambdaQuery().orderByDesc(Strategy::getViewnum).last("limit 10").list();List<StrategyRank> hotRank = new ArrayList<>();addRank(hotList, now,hotRank,3L);}@Overridepublic List<ThemeVo> queryTheme() {QueryWrapper<Strategy> queryWrapper = new QueryWrapper<>();queryWrapper.groupBy("theme_name");queryWrapper.select(" theme_name themeName, GROUP_CONCAT(DISTINCT dest_name) names,GROUP_CONCAT(DISTINCT dest_id) ids");List<Map<String, Object>> mapList = baseMapper.selectMaps(queryWrapper);List<ThemeVo> themeVoList = new ArrayList<>();for(Map<String, Object> map : mapList){String themeName = (String) map.get("themeName");String ids = (String) map.get("ids");String names = (String) map.get("names");String[] namearr = names.split(",");String[] idarr = ids.split(",");List<Destination> dests = new ArrayList<>();for(int i = 0;i<namearr.length;i++){Long id= Long.parseLong(idarr[i]);String name = namearr[i];Destination destination = new Destination();destination.setId(id);destination.setName(name);dests.add(destination);}ThemeVo themeVo = new ThemeVo(themeName, dests);themeVoList.add(themeVo);}return themeVoList;}
