黑马旅游网项目学习笔记

初始项目的导入
导入初始项目
导入完成后目录结构如图:


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.com
		const 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
    */
@Override
    public 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存入session
            request.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。具体实施如下:

创建一个基类BaseServlet,继承HttpServlet。
重写service方法,service方法的功能就是用来完成方法的分发,在http中有7种参数传递的方式,不过我们一般只使用POST和GET两种方式。
拿到URI最后一个目录层级即要执行的servlet的名称,使用反射技术执行该方法,就完成了一个方法的分发。

@Override
    protected 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层代码如下

   

    @Override
    public List<Category> findAll() {
        //获取Redis对象
        Jedis jedis = JedisUtil.getJedis();
        //使用下列方法查询,可以查询出id
        Set<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>() {
            @Override
            public 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  当前旅游分类的id
            2、根据Cid查询需要返回的数据:
                    - TotalCount  该旅游分类的旅游路线总条数
                    - TotalPage  总页码数(TotalPage =TotalCount%PageSize==0 ? TotalCount/PageSize : TotalCount/PageSize+1)
                    - Start  每页开始的数目 (Start = (CurrentPage-1)* PageSize)
                    - PageList  需要显示的路线的集合
            3、将数据封装为PageBean对象并转化为Json格式返回给前端页面
Dao层代码如下:
1、查询当前分类下的旅游线路总数

   

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


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

   

    @Override
    public 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对象

   @Override
    public PageBean<Route> pageQuery(int cid, int currentPage, int pageSize) {
        //封装PageBean
        PageBean<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=xxx
    location.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层代码如下    

	//查询满足条件的数据条数
	@Override
    public 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集合
	@Override
    public 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.其他大同小异 ,这里不做

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>cn.itcast.parent</groupId> <artifactId>itcast-parent</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>cn.itcast</groupId> <artifactId>travel</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </dependency> <!-- 连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <!-- Jackson Json处理工具包 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.4.7</version> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> </dependency> </dependencies> <build> <plugins> <!-- 配置Tomcat插件 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <configuration> <port>8080</port> <path>/</path> </configuration> </plugin> </plugins> </build> </project>
黑马Node.js学习笔记是一本关于Node.js的学习资料,其中包含了关于模块化规范的内容。模块化规范是对代码进行模块化的拆分与组合时需要遵守的规则。在Node.js中,模块可以分为内置模块、自定义模块和第三方模块。内置模块是由Node.js官方提供的,例如fs、path、http等。自定义模块是用户创建的每个.js文件,可以通过require方法加载。第三方模块是由第三方开发的模块,需要先下载后才能使用。在每个.js自定义模块中都有一个module对象,它存储了和当前模块有关的信息。注意,在同一个模块中不要同时使用exports和module.exports。在Node.js中,加载某个模块实际上是加载该模块的module.exports属性。npm是Node.js中的包管理工具,可以用于下载和管理第三方模块,而第三方模块也被称为包。总之,黑马Node.js学习笔记提供了关于Node.js模块化规范的详细解释和使用方法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [黑马程序员node.js学习笔记](https://blog.csdn.net/weixin_50523986/article/details/129937301)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值