JavaWeb - 黑马旅游网(番外2):用户收藏分页展示
黑马旅游网(番外2):用户收藏分页展示
1 功能描述
接上篇黑马旅游网(番外1):“自动登录”,本篇博客介绍我对黑马旅游网项目的第 2 个扩展功能:用户收藏界面展示。在用户已经登录网站且收藏了一些旅游线路的情况下。点击页面 header 区域中的 我的收藏
即可跳转至收藏界面。
这里需要处理两种特殊情况:1. 用户尚未登陆;2. 用户没有收藏任何旅游线路。在前端都要给与相应的提示信息。
2 功能分析
2.1 业务逻辑
收藏列表中的很多功能细节和之前实现的功能具备较大的重复度,但是整个业务逻辑和之前比还是存在区别,对数据库的查询要比之前稍微复杂一些。先不考虑未登录/无收藏等特殊情况,一般情况下,首先要查询当前登录用户的收藏内容,这一步需要查询 tab_favorite
表,这张表关联了 uid
和 rid
:
将当前登录用户的 uid
作为查询条件,查找满足约束的所有记录的 rid
。再将 rid
作为查询条件去 tab_route
表中做分页查询相应的旅游线路信息。参考黑马旅游网(5):旅游线路分页展示,后端的查询结果是将 PageBean<Route> 转换为 json 形式响应给客户端浏览器,前端再做处理和展示,功能完成。
2.2 实现细节
2.2.1 用户身份获取
用户身份的获取方式我总结为两种:
- 后端:从 Session 中直接获取 User bean 对象,进而获取
uid
; - 前端:利用后端之前返回的登录用户 json 数据,抓取
uid
并以 GET 请求方式提交给后端,后端采用 getParameter() 方法获取到uid
。
在我的代码中使用后端方式获取,实现起来也相对容易。
2.2.2 特殊情况处理
- 用户未登录
Session 中没有 User Bean 对象,无法执行查询,那么后端会向前端传递一个 null 对象。前端可以将此作为判断条件做相应处理。 - 用户无收藏
正确执行查询,但是没有查询到收藏线路,则 PageBean 中的totalCount
属性取默认值 0。前端可以将此作为判断条件做相应处理。
2.2.3 代码编写位置
- 查询收藏线路的执行实体是登录用户,因此在后端的 Servlet 层,将该查询业务定义在 UserServlet 和 UserService 中。
- 用户收藏内容保存在
tab_favorite
中,因此具体的查询逻辑应当定义在 FavoriteDao 中。 - 旅游线路信息保存在
tab_route
中,因此对旅游线路的分页查询应当定义在 RouteDao 中。
可以通过一张简图理解
另外,查询用户收藏内容时,在 Dao 层需要定专门在 domain 包中义一个 Bean 对象接收收藏数据。
public class MyFavorite {
private int uid;
private String date;
private int rid;
/* setter & getter */
}
3 代码实现
3.1 后端
3.1.1 Servlet
UserServlet.java
/**
* 获取`我的收藏`方法
*/
public void findMyFavorite(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 获取session,取得用户对象
HttpSession session = request.getSession();
User user = (User) session.getAttribute("user");
if (user == null) { // 用户未登录
writeValue(null, response);
return;
}
// 获取并处理浏览器请求属性
String currentPageStr = request.getParameter("currentPage"); // 当前页数
String pageSizeStr = request.getParameter("pageSize"); // 每页显示条数
int currentPage = this.parseInt(currentPageStr, 1);
int pageSize = this.parseInt(pageSizeStr, 8);
// 2.获取service,根据uid查rid,再根据rid查详情数据
PageBean<Route> routePageBean = service.favorPageQuery(user.getUid(), currentPage, pageSize);
// 3.将数据回写至浏览器
writeValue(routePageBean, response);
}
3.1.2 Service
UserServiceImpl.java
/**
* 查询`我的收藏`
* @param uid 用户uid
* @return 泛型为Route类的PageBean对象,
* 收藏结果的分页展示
*/
@Override
public PageBean<Route> favorPageQuery(int uid, int currentPage, int pageSize) {
// 创建PageBean<Route>对象
PageBean<Route> routePageBean = new PageBean<>();
// 查询总记录数totalCount
int totalCount = favoriteDao.findCountByUid(uid);
if (totalCount == 0) {// 没有查到收藏记录
return routePageBean;
}
// 计算起始记录数start,计算总页数totalPage
int start = (currentPage - 1) * pageSize;
int totalPage = (totalCount % pageSize == 0) ? (totalCount / pageSize) : (totalCount / pageSize + 1);
// 分页查询rid列表(封装在MyFavorite类中)
List<MyFavorite> pageFavoriteList = favoriteDao.findByUidAndPage(uid, start, pageSize);
// 创新一个空的List<Route>集合
List<Route> routeList = new ArrayList<>();
// 遍历pageFavoriteList组装routeList
for (MyFavorite myFavorite : pageFavoriteList) {
// 根据其rid属性利用routeDao查route对象
Route route = routeDao.findOne(myFavorite.getRid());
// 向routeList中追加route属性
routeList.add(route);
}
// 并组装PageBean<Route>对象
routePageBean.setCurrentPage(currentPage); // 设置当前页码
routePageBean.setPageSize(pageSize); // 设置每页显示条数
routePageBean.setTotalPage(totalPage); // 设置总页数
routePageBean.setTotalCount(totalCount); // 查询并设置总记录数
routePageBean.setList(routeList);
return routePageBean;
}
3.1.3 Dao
FavoriteDaoImpl.java
/**
* 查询用户uid的收藏总数
* @param uid 用户uid
* @return 收藏路线总数
*/
@Override
public int findCountByUid(int uid) {
String sql = "SELECT COUNT(*) FROM tab_favorite WHERE uid = ?";
return template.queryForObject(sql, Integer.class, uid);
}
/**
* 分页查询用户uid的收藏记录
* @param uid 用户uid
* @param start 起始条目
* @param pageSize 每页显示条数
* @return MyFavorite Bean 对象构成的 List 集合
*/
@Override
public List<MyFavorite> findByUidAndPage(int uid, int start, int pageSize) {
String sql = "SELECT * FROM tab_favorite WHERE uid = ? LIMIT ? , ?";
return template.query(sql, new BeanPropertyRowMapper<>(MyFavorite.class), uid, start, pageSize);
}
RouteDaoImpl.java (复用之前的线路信息查询代码)
/**
* 根据线路id查询详细信息
* @param rid 线路id
* @return Route Bean 对象
*/
@Override
public Route findOne(int rid) {
String sql = "SELECT * FROM tab_route WHERE rid = ?";
return template.queryForObject(sql, new BeanPropertyRowMapper<>(Route.class), rid);
}
3.2 前端
前端这里我基本参考之前的旅游线路分页展示的代码实现的。主体逻辑就是页面跳转来后,执行一个 AJAX 请求向后端要数据,提交请求时需要传递 currentPage
,后端默认处理为 1。接收 json 形式的 PageBean 对象:
- PageBean 为空:说明用户没有登录,给与相应的提示信息
- PageBean 中的
totalCount
属性为 0,说明用户没有收藏任何线路,给与相应的提示信息 - 正常情况,参考分页线路展示实现即可,这里对这种情况不再做具体展示,可以参考黑马旅游网(5):旅游线路分页展示,或者访问我的 GitHub 仓库 阅读。
myfavorite.html
/**
* 我的收藏分页展示
*/
$(function () {
let currentPage = getParameter("currentPage");
load(currentPage);
});
function load(currentPage) {
$.get("user/findMyFavorite", {currentPage: currentPage}, function (pageBean) {
// 解析pageBean数据,展示到页面上
if (pageBean == null) {
// 可以添加一些渲染,由于我主学后端,这里就不展开了
$("#favorite_routes").html('<span id="notlogin">您尚未登录,请<a href="login.html"> 【【登录】 </a></span>');
}
if (pageBean["totalCount"] === 0) {
// 可以添加一些渲染,由于我主学后端,这里就不展开了
$("#favorite_routes").html('<span id="nofavorite">您还没有任何收藏哦,去看一看线路列表吧</span>');
}
/*分页栏展示*/
/*旅游线路展示*/
});
}
4 相关链接
项目课程链接:https://www.bilibili.com/video/BV1CE411E7h4
完整课程连接:https://www.bilibili.com/video/BV1uJ411k7wy
《黑马旅游网》系列博客及笔者源码传送门:https://blog.csdn.net/xing123456jl/article/details/109173068
上一篇: 数据清洗
下一篇: Java Web项目中编写定时任务的实现