黑马旅游网项目详细思路和完整代码整理

前言

由于新冠病毒的原因,无法上学。百无聊赖下自己开始看视频学习,跟着视频做完了这个项目来检验学习成果,顺便写篇博客来记录一下。
话不多说,开始正题。

文档及其源代码

百度网盘
百度网盘链接:https://pan.baidu.com/s/1EfP8ZsQYSPTz80EGwh-1jw
提取码:ovf4

技术选型

表示层(web)

1. HTML、CSS、JS、JQuery、Ajax
2. Servlet  前端控制器
3. Filter  过滤器
4. BeanUtils  数据的封装工具类
5. JackSon  Json数据序列化工具

业务逻辑层(service)

1. JavaMail  java处理电子邮件的接口
2. Redis  key-value存储系统
3. Jedis  Redis的Java连接开发工具

持久层(Dao)

1. Druid连接池  Alibaba提供的数据库连接池
2. JDBCUtils  JDBC的工具类
3. JdbcTemplate  Spring MVC内置的对JDBC的封装
4. MySql数据库  SQL关系型数据库

初始项目的导入

导入初始项目

导入完成后目录结构如图:

pom 可能出现的问题


pom配置文件可能会有部分jar包无法加载或者冲突等问题,我们可以到Maven的中央仓库复制需要的jar包到pom文件中,IDE就可以自动下载jar包。或者找到Maven本地仓库将版本改为我们已经下载好的版本。
关于Maven的搭建和相关问题的处理,可见Maven环境搭建

创建数据库

 -- 创建数据库CREATE DATABASE travel;-- 使用数据库USE travel;--创建表复制给定的SQL即可

创建完毕的数据库如图:

项目实现的功能分析

  • 注册功能
  • 登录功能
  • 退出功能
  • 分类数据展示功能
  • 旅游线路分页展示功能
  • 旅游线路名称关键字检索功能
  • 旅游线路详情展示
  • 旅游线路收藏功能
  • 我的收藏页面展示

项目功能详解

注册

1、表单校验

 当input输入框失去焦点时,使用JS的正则表达式对输入的内容进行判断看其是否符合规则。不符合规则:将边框变成红色符合规则:不做任何改变当所有需要判断的内容都符合规则时,提交按钮才会提交数据。

部分代码如下:

 // email校验function checkEmail(){const s = $("#email").val();//定义正则 itcast@163.comconst check = /^\w+@\w+\.\w+$/;const flag = check.test(s);if(flag){$("#email").css("border","");}else {$("#email").css("border","1px solid red");}return flag;}//校验所有内容以及发送Ajax请求$(function () {$("#registerForm").submit(function () {if(checkPassword() && checkUsername() && checkEmail() && checkName() && checkBirthday() && checkNumber()){//serialize()函数将表单中的数据自动封装转化为键值对的形式$.post("user/register",$(this).serialize(),function (data) {if(data.flag){location.href="register_ok.html";}else {alert(data.errorMsg);}});}return false;});//失去焦点时校验用户名$("#username").blur(checkUsername);//失去焦点时校验密码$("#password").blur(checkPassword);//失去焦点时校验邮箱$("#email").blur(checkEmail);//姓名$("#name").blur(checkName);//手机号$("#telephone").blur(checkNumber);//出生日期$("#birthday").blur(checkBirthday);});

2、验证码校验

 为了减轻服务器的压力,查询数据库之前,先校验验证码。每随机生成一个验证码,就会存入session中。校验验证码时,提取session中的验证码与input中提取的验证码相比较。验证码正确:点击提交按钮时会将所有数据发送到数据库中进行操作。验证码错误:弹出提示框,提示验证码错误。这时候点击提交按钮时无效的。

3、数据库查询

 1、先根据Ucode码查询数据库是否有数据如果有,注册失败。如果没有,将表单的数据存入数据库。2、发送激活邮件。用户点击激活邮件连接,这时改变数据库中的激活状态码。此时用户才算成功注册。

service层代码如下:

   /*** 注册用户* @param user 从表单中获取并封装的user对象* @return 成功为true,失败为false*/
@Overridepublic boolean register(User user) {if(dao.findUserByUsername(user.getUsername())==null){String code = UuidUtil.getUuid();user.setCode(code);user.setStatus("N");dao.save(user);String url ="http://localhost:8080/travel/activeUserServlet?code="+user.getCode()+"";//激活邮件发送String content ="<h2>【无无@wuliu旅游网】</h2><a href='"+url+"'>点击激活</a><br>网页链接:"+url+"<br>无需回复,祝您一切安好!";return MailUtils.sendMail(user.getEmail(),content,"激活邮件");}else{return false;}}

登录

1、验证码校验

 同上

2、数据库查询

 根据用户名密码查询tab_user表。如果能够返回一个user对象,再判断该user对象是否激活。如果已经激活,则登录成功,跳转到主页。如果没有激活,则给出提示。

servlet代码如下:

public void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {if(!checkCode(request)){info.setFlag(false);info.setErrorMsg("验证码错误!");writeValueToOutputStream(info,response);}Map map = request.getParameterMap();User user = new User();try {BeanUtils.populate(user,map);} catch (IllegalAccessException | InvocationTargetException e) {e.printStackTrace();}UserService service = new UserServiceImpl();User u = service.login(user);if(u == null){info.setFlag(false);info.setErrorMsg("用户名或密码错误!");}if(u != null && !"Y".equals(u.getStatus())){info.setFlag(false);info.setErrorMsg("您还没有激活,请激活!");}if(u != null && "Y".equals(u.getStatus())){info.setFlag(true);//将user存入sessionrequest.getSession().setAttribute("user",u);}writeValueToOutputStream(info,response);}
 前端发送Ajax请求只需要判断回传的json数据中flag是否为true。如果为true,则跳转到主页面。如果为false,则在提示框中打印errorMsg数据即可。

前端代码如下

$(function () {$("#but_login").click(function(){$.post("user/login",$("#loginForm").serialize(),function (data) {//data : {flag:false,errorMsg:''}if(data.flag){//登录成功location.href="index.html";}else{//登录失败$("#errorMsg").html(data.errorMsg);}})});})

退出

 在登录成功时,我们会将user对象存入session,并且在主页上显示 " 欢迎回来,××× " 的字样。当用户点击退出时,我们只需要删除session信息即可```public void exit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {request.getSession().invalidate();response.sendRedirect(request.getContextPath()+"/login.html");}```

servlet的封装

为了避免servlet太多,我们将servlet划分为一个个模块使用反射的方式执行该servlet。具体实施如下:

  1. 创建一个基类BaseServlet,继承HttpServlet
  2. 重写service方法,service方法的功能就是用来完成方法的分发,在http中有7种参数传递的方式,不过我们一般只使用POST和GET两种方式。
  3. 拿到URI最后一个目录层级即要执行的servlet的名称,使用反射技术执行该方法,就完成了一个方法的分发。
@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//完成方法的分发String uri = req.getRequestURI();String methodName = uri.substring(uri.lastIndexOf("/")+1);//通过反射执行userServlet中的方法try {//获取方法Method method = this.getClass().getMethod(methodName,HttpServletRequest.class,HttpServletResponse.class);//执行方法method.invoke(this,req,resp);} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {e.printStackTrace();}}

分类数据展示

1、数据库查询及缓存优化

 从数据库中查询这8种分类数据,并展示在页面上。1、页面加载完毕后发送Ajax请求。2、先查询Redis数据库中是否有一个key 为 category的数据。如果有数据,直接将数据返回转化为Json格式返回给前端。如果没有数据,查询MySql数据库,将数据返回给前端,再将数据存入Redis数据库中。

service层代码如下

    @Overridepublic List<Category> findAll() {//获取Redis对象Jedis jedis = JedisUtil.getJedis();//使用下列方法查询,可以查询出idSet<Tuple> set = jedis.zrangeByScoreWithScores("category",0,-1);List<Category> list = new ArrayList<>();//Redis中没有数据if(set==null || set.size() == 0){//查询数据库list = dao.findAll();for (Category category : list) {jedis.zadd("category",category.getCid(),category.getCname());}}//Redis中有数据else{for (Tuple s : set) {Category c = new Category();c.setCid((int) s.getScore());c.setCname(s.getElement());list.add(c);}}//重写list的比较器方法,将结果按照ID的大小排序list.sort(new Comparator<Category>() {@Overridepublic int compare(Category o1, Category o2) {return o1.getCid()-o2.getCid();}});return list;}

2、前端页面展示

 遍历后端回传的数据,拼接字符串填充为动态的数据。拼接字符串时一定要注意单双引号的问题。

前端代码如下:

$.get("category/findAll",{},function (data) {//[{cid:1,cname:国内游},{},{}]let lis = '<li class="nav-active"><a href="index.html">首页</a></li>';//遍历数组,拼接字符串(<li>)for (let i = 0; i < data.length; i++) {const cid = data[i].cid;const cname = data[i].cname;const li = '<li><a href="http://localhost:8080/travel/route_list.html?cid='+cid+'&rname=">'+cname+'</a></li>';lis += li;}//拼接收藏排行榜的li,<li><a href="favoriterank.html">收藏排行榜</a></li>lis+= '<li><a href="favoriterank.html">收藏排行榜</a></li>';//将lis字符串,设置到ul的html内容中$("#category").html(lis);//获取输入框的值});

旅游线路分页展示

1、数据库查询与PageBean的封装

 分析:1、查询每页旅游线路所需要的条件:- CurrentPage  当前页码(如果不传,默认为1)- PageSize  每页显示条数- Cid  当前旅游分类的id2、根据Cid查询需要返回的数据:- TotalCount  该旅游分类的旅游路线总条数- TotalPage  总页码数(TotalPage =TotalCount%PageSize==0 ? TotalCount/PageSize : TotalCount/PageSize+1)- Start  每页开始的数目 (Start = (CurrentPage-1)* PageSize)- PageList  需要显示的路线的集合3、将数据封装为PageBean对象并转化为Json格式返回给前端页面

Dao层代码如下:
1、查询当前分类下的旅游线路总数

    @Overridepublic int findTotalCount(int cid) {String sql = "select count(*) from tab_route where cid=? ";return template.queryForObject(sql, Integer.class,cid);}

2、查询当前分类下的旅游线路集合

    @Overridepublic List<Route> findByPage(int cid, int start, int pageSize) {String sql = "select * from tab_route where cid = ? limit ? , ?";return template.query(sql,new BeanPropertyRowMapper<Route>(Route.class),cid,start,pageSize);}

3、计算各个数值,封装PageBean对象

   @Overridepublic PageBean<Route> pageQuery(int cid, int currentPage, int pageSize) {//封装PageBeanPageBean<Route> pb = new PageBean<Route>();//设置当前页码pb.setCurrentPage(currentPage);//设置每页显示条数pb.setPageSize(pageSize);//设置总记录数int totalCount = routeDao.findTotalCount(cid);pb.setTotalCount(totalCount);//设置当前页显示的数据集合int start = (currentPage - 1) * pageSize;//开始的记录数List<Route> list = routeDao.findByPage(cid,start,pageSize);pb.setList(list);//设置总页数 = 总记录数/每页显示条数int totalPage = totalCount % pageSize == 0 ? totalCount / pageSize :(totalCount / pageSize) + 1 ;pb.setTotalPage(totalPage);return pb;}

2、旅游线路页面展示与分页条优化。

分析

 页面加载完成之后向“ route/pageQuery ”发送Ajax请求,获取PageBean对象。1、遍历PageBean对象的List集合拼接字符串。2、遍历PageBean对象的TotalPage ,拼接字符串展示在分页栏。3、分页栏优化- 当页面数小于10页时,展示所有页码。- 当页面数大于10页时,只展示8个页码。- 当页码大于4时,页码按照前4后3的模式排列。- 当前页码前后不足时,补齐8个页码。

1、旅游线路列表展示

//2.列表数据展示var route_lis = "";for (var i = 0; i < pb.list.length; i++) {//获取{rid:1,rname:"xxx"}var route = pb.list[i];var li = '<li>\n' +'                        <div class="img"><img src="'+route.rimage+'" style="width: 299px;"></div>\n' +'                        <div class="text1">\n' +'                            <p>'+route.rname+'</p>\n' +'                            <br/>\n' +'                            <p>'+route.routeIntroduce+'</p>\n' +'                        </div>\n' +'                        <div class="price">\n' +'                            <p class="price_num">\n' +'                                <span>&yen;</span>\n' +'                                <span>'+route.price+'</span>\n' +'                                <span>起</span>\n' +'                            </p>\n' +'                            <p><a href="route_detail.html">查看详情</a></p>\n' +'                        </div>\n' +'                    </li>';route_lis += li;}

2、旅游路线页码栏代码

    //解析PageBean对象//页码展示const totalCount = pb.totalCount;const totalPage = pb.totalPage;$("#totalPage").html(totalPage);$("#totalCount").html(totalCount);const currentPage = pb.currentPage;//分页栏展示let bePage = currentPage - 1;if(bePage < 0){bePage = 1;}let laPage = currentPage + 1;if(laPage > totalPage){laPage = totalPage;}let lis = '';const fPage = '<li οnclick="load('+cid+','+1+',\''+rname+'\');"><a href="javascript:void();">首页</a></li>\n' +'<li οnclick="load('+cid+','+bePage+',\''+rname+'\');" class="threeword"><a href="javascript:void();">上一页</a></li>';lis += fPage;let begin ;let end ;if(totalPage<8){begin = 1;end = totalPage;}else{begin = currentPage - 4;end = currentPage + 3;if(begin <= 0){begin = 1;end = begin + 7;}if(end > totalPage){end = totalPage;begin = end - 7;}}for (let i = begin; i <= end ; i++) {let li;if(currentPage === i){li = '<li class="curPage" οnclick="load('+cid+','+i+');"><a href="javascript:void();">' + i + '</a></li>';}else {li = '<li οnclick="load('+cid+','+i+');"><a href="javascript:void();">' + i + '</a></li>';}lis += li;}const lPage ='<li οnclick="load('+cid+','+laPage+')" class="threeword"><a href="javascript:void();">下一页</a></li>\n' +'<li οnclick="load('+cid+','+totalPage+');" class="threeword"><a href="javascript:void();">末页</a></li>';lis += lPage;$("#pageNum").html(lis);

旅游线路名称关键字检索

1、提取名称关键字

  • header.html页面查询参数的传递
$("#search-button").click(function () {//线路名称var rname = $("#search_input").val();var cid = getParameter("cid");// 跳转路径 http://localhost/travel/route_list.html?cid=5,拼接上rname=xxxlocation.href="http://localhost/travel/route_list.html?cid="+cid+"&rname="+rname;
});
  • route_list.html页面查询参数的传递
var cid = getParameter("cid");//获取rname的参数值var rname = getParameter("rname");//判断rname如果不为null或者""if(rname){//url解码rname = window.decodeURIComponent(rname);}

2、根据条件查询数据库

 Dao层查询小技巧Sql可以先写 “select * from tab_××× where ”Dao层Sql的编写可以用StringBuilder字符缓冲区进行Sql的拼接

Dao层代码如下

 //查询满足条件的数据条数@Overridepublic int findTotalCount(int cid,String rname) {String sql = "select count(*) from tab_route where 1=1 ";List params = new ArrayList<>();StringBuilder sb = new StringBuilder(sql);if(cid != 0){sb.append(" and cid=?");params.add(cid);}if(rname != null && rname.length()>0){sb.append(" and rname like ?");params.add("%"+rname+"%");}sql = sb.toString();return template.queryForObject(sql, Integer.class,params.toArray());}//查询满足条件的Route对象并封装为List集合@Overridepublic List<Route> findByPage(int cid, int start, int pageSize,String rname) {String sql = null;List<Route> list = null;boolean idEmpty = cid==0;boolean nameEmpty = rname.equals(" ");if(!idEmpty && nameEmpty){sql = "select * from tab_route where cid=? limit ? , ? ";list = template.query(sql,new BeanPropertyRowMapper<>(Route.class),cid,start,pageSize);return list;}if(idEmpty && !nameEmpty){sql = "select * from tab_route where rname like ? limit ? , ? ";rname = "%"+rname+"%";list = template.query(sql,new BeanPropertyRowMapper<>(Route.class),rname,start,pageSize);return list;}if(idEmpty && nameEmpty){sql = "select * from tab_route limit ?,?";list = template.query(sql,new BeanPropertyRowMapper<>(Route.class),start,pageSize);return list;}else{sql = "SELECT * FROM tab_route WHERE cid=? AND rname LIKE ? LIMIT ?,?";rname = "%"+rname+"%";list = template.query(sql,new BeanPropertyRowMapper<>(Route.class),cid,rname,start,pageSize);return list;}}

3、将数据库查询的数据展示在页面上
前端代码类似以上代码

旅游线路详情展示

1、点击查看详情按钮时传递当前路线的id
2、根据id查询数据库并封装为Route对象返回
3、根据Route对象的内容填充页面数据
HTML页面填充Route详情代码如下

$(function () {const rid = getParameter("rid");$.post("route/routeInfo",{rid:rid},function (route) {$("#rname").html(route.rname);$("#price").html("¥"+route.price);$("#routeIntroduce").html(route.routeIntroduce);$("#sname").html(route.seller.sname);$("#consphone").html(route.seller.consphone);$("#address").html(route.seller.address);$("#dt").html('<img alt="" class="big_img" src="'+route.rimage+'">');const count = 100+route.count;$("#fCount").html("已收藏"+count+"次");let dds ='';let last = '<a class="up_img up_img_disable"></a>';dds += last;for (let i = 0; i <route.routeImgList.length ; i++) {const rImgList = route.routeImgList[i];let dd = '';if(i<=4){dd = '<a title="" class="little_img" data-bigpic="' + rImgList.bigPic + '">\n' +'         <img src="' + rImgList.smallPic + '">\n' +'     </a>';}else{dd = '<a title="" class="little_img" data-bigpic="' + rImgList.bigPic + '" style="display:none;" >\n' +'         <img src="' + rImgList.smallPic + '">\n' +'     </a>';}dds += dd;}let next = '<a class="down_img down_img_disable" style="margin-bottom: 0;"></a>';dds += next;$("#dd").html(dds);show();fButton();});});

旅游线路收藏

1、点击收藏按钮,发送异步请求

 根据 Uid和Rid查询 tab_favorite 表-如果有数据,那么表示该线路已被该用户收藏将收藏按钮置灰,移除Onclick()事件,并将按钮变成不可点击状态。-如果没有数据,表示该线路没有被该用户收藏将收藏按钮变成红色,并且加上Onclick()事件。点击按钮时发送异步请求更新数据库。

2、将数据库回传的数据写回到前端页面
代码同上

我的收藏页面展示

 直接添加的功能,大致思路同 旅游线路分页展示 只是每页显示的数量变成了12.其他大同小异 ,这里不做赘述。

黑马旅游网项目详细思路和完整代码整理 -附源码相关推荐

  1. DSP:6678开发板NDK网口通信完整实现(附源码)

    如果出现图片打不开,或是显示异常,请点击下方链接阅读原文!!! DSP:6678开发板NDK网口通信完整实现(附源码) - 子木的文章 - 知乎 https://zhuanlan.zhihu.com/ ...

  2. Java毕设项目大学生校园兼职系统计算机(附源码+系统+数据库+LW)

    Java毕设项目大学生校园兼职系统计算机(附源码+系统+数据库+LW) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ E ...

  3. Java毕设项目超市会员积分管理系统计算机(附源码+系统+数据库+LW)

    Java毕设项目超市会员积分管理系统计算机(附源码+系统+数据库+LW) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ ...

  4. Java毕设项目保险公司风险测评管理系统计算机(附源码+系统+数据库+LW)

    Java毕设项目保险公司风险测评管理系统计算机(附源码+系统+数据库+LW) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行) ...

  5. Java毕设项目茶店订购管理系统2021计算机(附源码+系统+数据库+LW)

    Java毕设项目茶店订购管理系统2021计算机(附源码+系统+数据库+LW) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行) ...

  6. Java毕设项目学生二手书籍交易平台计算机(附源码+系统+数据库+LW)

    Java毕设项目学生二手书籍交易平台计算机(附源码+系统+数据库+LW) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ ...

  7. Java毕设项目社区团购系统计算机(附源码+系统+数据库+LW)

    Java毕设项目社区团购系统计算机(附源码+系统+数据库+LW) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ Ecli ...

  8. Java毕设项目羽毛球馆场地管理系统计算机(附源码+系统+数据库+LW)

    Java毕设项目羽毛球馆场地管理系统计算机(附源码+系统+数据库+LW) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ ...

  9. Java毕设项目早教课程管理系统计算机(附源码+系统+数据库+LW)

    Java毕设项目早教课程管理系统计算机(附源码+系统+数据库+LW) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ Ec ...

最新文章

  1. 基于强化学习的自动化剪枝模型
  2. object-c中的继承
  3. H3C路由器的备份与还原(1)
  4. 转载:linux环境下搭建discuz论坛
  5. 动图图解C语言插入排序算法,含代码分析
  6. MySQL 内连接、左连接、右连接、外连接、多表查询
  7. dao-service-servlet-jsp构建简易web通讯录(三层开发)预备知识
  8. html右边距无法调整,**css+html为什么右侧菜单的字体大小和边距设置在失效?谁能看下*...
  9. BerryNet: Deep Learning Gateway on Raspberry Pi
  10. 拓端tecdat|GIS遥感数据可视化评估:印度河流域上部的积雪面积变化
  11. JAVA写的模拟收发器设备面板工作状态并输出图形界面
  12. 音乐专业如何利用计算机思维,太神奇了!带学生“玩音乐”居然可以打开思维创新...
  13. python培训班心得_Python学习心得
  14. FS2120双节锂电池保护 IC
  15. ABBYY FineReader 15 安装教程
  16. vue 开发的微信公众号网页 添加背景音乐,以及音乐图标旋转动画
  17. SD从零开始38-40
  18. 2015062001 - 诸葛亮
  19. java 音频 合成_java 音频 合成
  20. 美国康奈尔大学BioNB441元胞自动机MATLAB应用

热门文章

  1. PartMaker 走心机车削 车铣编程视频教程 PartMaker资料
  2. java创建与删除文件(文件夹)
  3. 2021 python 求职 常见面试题(北京 爬虫/后端)
  4. 网络游戏 洗钱 复制手法过程!!!
  5. Android牛人博客集合
  6. 功能:crypto-js加密解密
  7. 第四十四讲基于PreScan的V2X仿真建模
  8. python爬取网易云音乐评论分析_python爬取网易云音乐评论
  9. C语言程序设计 谭浩强 顺序程序设计 知识点
  10. [高级项目管理师]信息系统集成专业技术知识