旅游网项目01

1. 前言

为了巩固web基础知识,提升综合运用能力,故编写此项目,以做练习。

今日内容为登录注册内容。

2. 技术选型

2.1 Web

  1. Servlet:前端控制器
  2. html:视图
  3. Filter:过滤器
  4. BeanUtils:数据封装
  5. Jackson:json序列化工具

2.2 Service

  1. Javamail:java发送邮件工具
  2. Redis:nosql内存数据库
  3. Jedis:java的Redis客户端

2.3 Dao

  1. MySQL:后台数据库
  2. Druid:数据库连接池
  3. JdbcTemplate:jdbc的工具

3. 创建数据库


-- 创建数据库
create database travel;

-- 使用数据库
use travel;

-- 创建表
/*==============================================================*/
/* DBMS name:      MySQL 5.0                                    */
/* Created on:     2019/7/21 20:13:29                           */
/*==============================================================*/
set names utf8;

drop table if exists tab_favorite;
drop table if exists tab_route_img;
drop table if exists tab_route;
drop table if exists tab_category;
drop table if exists tab_seller;
drop table if exists tab_user;

/*==============================================================*/
/* Table: tab_category                                          */
/*==============================================================*/
create table tab_category
(
   cid                  int not null auto_increment,
   cname                varchar(100) not null,
   primary key (cid),
   unique key AK_nq_categoryname (cname)
);

/*==============================================================*/
/* Table: tab_favorite                                          */
/*==============================================================*/
create table tab_favorite
(
   rid                  int not null,
   date                 date not null,
   uid                  int not null,
   primary key (rid, uid)
);

/*==============================================================*/
/* Table: tab_route                                             */
/*==============================================================*/
create table tab_route
(
   rid                  int not null auto_increment,
   rname                varchar(500) not null,
   price                double not null,
   routeIntroduce       varchar(1000),
   rflag                char(1) not null,
   rdate                varchar(19),
   isThemeTour          char(1) not null,
   count                int default 0,
   cid                  int not null,
   rimage               varchar(200),
   sid                  int,
   sourceId             varchar(50),
   primary key (rid),
   unique key AK_nq_sourceId (sourceId)
);

/*==============================================================*/
/* Table: tab_route_img                                         */
/*==============================================================*/
create table tab_route_img
(
   rgid                 int not null auto_increment,
   rid                  int not null,
   bigPic               varchar(200) not null,
   smallPic             varchar(200),
   primary key (rgid)
);

/*==============================================================*/
/* Table: tab_seller                                            */
/*==============================================================*/
create table tab_seller
(
   sid                  int not null auto_increment,
   sname                varchar(200) not null,
   consphone            varchar(20) not null,
   address              varchar(200),
   primary key (sid),
   unique key AK_Key_2 (sname)
);

/*==============================================================*/
/* Table: tab_user                                              */
/*==============================================================*/
create table tab_user
(
   uid                  int not null auto_increment,
   username             varchar(100) not null,
   password             varchar(32) not null,
   name                 varchar(100),
   birthday             date,
   sex                  char(1),
   telephone            varchar(11),
   email                varchar(100),
   status               char(1) ,
   code                    varchar(50),

   primary key (uid),
   unique key AK_nq_username (username),
   unique key AK_nq_code (code)
);

-- 建立表连接
alter table tab_favorite add constraint FK_route_favorite foreign key (rid)
      references tab_route (rid) on delete restrict on update restrict;

alter table tab_favorite add constraint FK_user_favorite foreign key (uid)
      references tab_user (uid) on delete restrict on update restrict;

alter table tab_route add constraint FK_category_route foreign key (cid)
      references tab_category (cid) on delete restrict on update restrict;

alter table tab_route add constraint FK_seller_route foreign key (sid)
      references tab_seller (sid) on delete restrict on update restrict;

alter table tab_route_img add constraint FK_route_routeimg foreign key (rid)
      references tab_route (rid) on delete restrict on update restrict;


-- 修改表默认字符集,解决1366问题
alter table tab_category convert to character set utf8 collate utf8_general_ci;
alter table tab_favorite convert to character set utf8 collate utf8_general_ci;
alter table tab_route convert to character set utf8 collate utf8_general_ci;
alter table tab_route_img convert to character set utf8 collate utf8_general_ci;
alter table tab_seller convert to character set utf8 collate utf8_general_ci;
alter table tab_user convert to character set utf8 collate utf8_general_ci;

关于报错1366问题:详见文章

4. 注册功能

4.1 功能分析

title

4.2 代码实现

4.2.1 表单校验

// 使用Jquery完成表单校验
// 定义校验方法
// 用户名校验
function checkUsername() {
    // 1. 获取用户名值
    var username = $("#username").val();
    // 2. 定义正则
    var reg_username = /^[\u4e00-\u9fa5]|[0-9a-zA-Z]{2,20}$/;
    // 3. 判断,给出提示信息
    var flag = reg_username.test(username);
    if (flag){
        // 用户名合法
        $("#username").css("border","");
    }else {
        // 用户名非法,加红色边框
        $("#username").css("border","1px solid red");
    }
    return flag;
}
// 密码校验
function checkPwd() {
    // 1. 获取值
    var password = $("#password").val();
    // 2. 定义正则
    var reg_password = /^[\w_-]{6,20}$/;
    // 3. 判断,返回信息
    var flag = reg_password.test(password);
    if (flag){
        // 密码合法
        $("#password").css("border","");
    } else {
        // 非法,加红色边框
        $("#password").css("border","1px solid red");
    }
    return flag;
}
// 邮箱校验
function checkEmail() {
    var email = $("#email").val();
    var reg_email = /^\w+@\w+\.\w+$/;
    var flag = reg_email.test(email);
    if(flag){
        $("#email").css("border","");
    }else {
        $("#email").css("border","1px solid red");
    }
    return flag;
}

$(function () {
    // 当表单提交时,调用所有校验方法
    $("#registerForm").submit(function () {
        return checkUsername() && checkPwd() && checkEmail();
    });

    // 当某一组件失去焦点时,调用对应的校验方法
    $("#username").blur(checkUsername);
    $("#password").blur(checkPwd);
    $("#email").blur(checkEmail);
});

4.2.2 异步提交表单

使用异步提交表单是为了获取服务器响应的数据。因为前台使用的是html作为视图层,不能够直接从servlet相关的域对象获取值,只能通过ajax获取响应数据。

$(function () {
    // 当表单提交时,调用所有校验方法
    $("#registerForm").submit(function () {
        // 1. 发送数据到服务器
        if ( checkUsername() && checkPwd() && checkEmail()){
            // 校验通过,发送ajax请求,提交表单数据
            $.post("registUserServlet",$(this).serialize(),function (data) {
                // 处理服务器响应的数据 data  {flag:true,errorMsg:"注册失败"}

                if (data.flag){
                    // 注册成功,跳转到成功页面
                    location.href = "register_ok.html";
                }else{
                    // 注册失败,给errorMsg添加提示信息
                    $("#errorMsg").html(data.errorMsg);
                }
            })
        }
        // 2. 不让页面跳转,不提交
        return false;
    });

    // 当某一组件失去焦点时,调用对应的校验方法
    $("#username").blur(checkUsername);
    $("#password").blur(checkPwd);
    $("#email").blur(checkEmail);
});

4.3 后台代码实现

4.3.1 注册用户控制器:RegistUserServlet

package cn.itcast.travel.web.servlet;

import cn.itcast.travel.domain.ResultInfo;
import cn.itcast.travel.domain.User;
import cn.itcast.travel.service.UserService;
import cn.itcast.travel.service.impl.UserServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.beanutils.BeanUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;

@WebServlet("/registUserServlet")
public class RegistUserServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取用户输入的验证码
        String check = request.getParameter("check");
        // 从session中获取生成的验证码
        HttpSession session = request.getSession();
        String checkcode_server = (String)session.getAttribute("CHECKCODE_SERVER");
        session.removeAttribute("CHECKCODE_SERVER"); // 确保验证码只能使用一次
        // 如果验证码错误,直接注册失败
        if (checkcode_server == null || !checkcode_server.equalsIgnoreCase(check)){
            // 验证码错误
            ResultInfo info = new ResultInfo();
            // 注册失败
            info.setFlag(false);
            info.setErrorMsg("验证码错误");
            // 将info对象序列化为json
            ObjectMapper mapper = new ObjectMapper();
            String json = mapper.writeValueAsString(info);
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(json);
            return; // todo:为何return?
        }

        // 验证通过
        // 1. 获取前台表单数据
        Map<String, String[]> map = request.getParameterMap();

        // 2. 封装为user对象
        User user = new User();
        try {
            BeanUtils.populate(user,map);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        // 3. 调用service完成注册
        UserService service = new UserServiceImpl();
        boolean flag = service.regist(user);
        ResultInfo info = new ResultInfo();
        // 4. 响应结果
        if (flag){
            // 注册成功
            info.setFlag(true);
        }else{
            // 注册失败
            info.setFlag(false);
            info.setErrorMsg("注册失败!");
        }

        // 将info对象序列化为json
        ObjectMapper mapper = new ObjectMapper();
        System.out.println(info);
        String json = mapper.writeValueAsString(info);
        // 将json数据回写客户端
        // 设置content-type
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(json);

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

4.3.2 用户服务业务层以及对应实现类

服务层接口

package cn.itcast.travel.service;

import cn.itcast.travel.domain.User;

public interface UserService {
    boolean regist(User user);
}

实现类

package cn.itcast.travel.service.impl;

import cn.itcast.travel.dao.UserDao;
import cn.itcast.travel.dao.impl.UserDaoImpl;
import cn.itcast.travel.domain.User;
import cn.itcast.travel.service.UserService;

public class UserServiceImpl implements UserService {

    private UserDao userDao = new UserDaoImpl();

    /**
     * 注册用户服务
     * @param user
     * @return 是否注册成功
     */
    @Override
    public boolean regist(User user) {
        // 1. 根据用户名查询用户对象
        User u = userDao.findByUsername(user.getUsername());
        // 判断是否为null
        if (u != null){
            // 不为空,说明用户名存在,注册失败
            return false;
        }
        // 2. 保存用户信息
        userDao.save(user);
        return true;
    }
}

4.3.3 数据操作层及对应实现类

Dao

package cn.itcast.travel.dao;

import cn.itcast.travel.domain.User;

public interface UserDao {
    User findByUsername(String username);
    void save(User user);
}

实现类

package cn.itcast.travel.dao.impl;

import cn.itcast.travel.dao.UserDao;
import cn.itcast.travel.domain.User;
import cn.itcast.travel.util.JDBCUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

public class UserDaoImpl implements UserDao {

    private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());


    /**
     * 查询用户是否存在
     * @param username
     * @return 返回查询到的用户
     */
    @Override
    public User findByUsername(String username) {
        /*
            queryForObject()方法
            如果查询结果条数为0或者大于1)都会返回异常,我们希望没查到直接返回null
         */
        User user = null;
        try{
            user = template.queryForObject("select * from tab_user where username = ? ",
                    new BeanPropertyRowMapper<User>(User.class),username);
        }catch (Exception e){
            System.out.println("用户未查询到");
        }
        return user;
    }

    @Override
    public void save(User user) {
        // 1. 定义sql
        String sql = "insert into tab_user(username,password,name,birthday,sex,telephone,email) values(?,?,?,?,?,?,?)";
        // 2. 执行sql
        template.update(sql,user.getUsername(),user.getPassword(),user.getName(),
                            user.getBirthday(),user.getSex(),user.getTelephone(),user.getEmail());
    }
}

4.3.4 邮件激活

为什么要进行邮件激活?为了保证用户填写的邮箱是正确的。将来可以推广一些宣传信息,到用户邮箱中。

4.3.5 发送邮件

  1. 打开邮箱
  2. 开启授权码,并生成一个
  3. MailUtils中设置自己的邮箱账号和密码(授权码)

MailUtils是邮箱工具类,调用其中的sendMail方法可以完成邮件发送

4.3.6 用户点击邮件激活

4.3.6.1 功能分析

title

4.3.6.2 发送邮件代码实现
@Override
public boolean regist(User user) {
    // 1. 根据用户名查询用户对象
    User u = userDao.findByUsername(user.getUsername());
    // 判断是否为null
    if (u != null){
        // 不为空,说明用户名存在,注册失败
        return false;
    }
    // 2. 保存用户信息
    // 2.1 设置激活码,唯一字符串
    String uuid = UuidUtil.getUuid();
    System.out.println(uuid);
    user.setCode(uuid);
    // 2.2 设置激活状态
    user.setStatus("N"); // 默认未激活
    userDao.save(user);

    // 3. 激活邮件发送,邮件正文
    // 邮件正文 todo:后期项目部署需要将href改为服务器域名
    String content = "<a href='http://localhost/travel/activeUserServlet?code="+user.getCode()+"'>点击激活账户</a>";
    // 发送邮件
    MailUtils.sendMail(user.getEmail(),content,"激活邮件");

    return true;
}
4.3.6.3 修改保存Dao代码,以及存储statuscode代码逻辑

title

4.3.6.4 激活Servlet实现
package cn.itcast.travel.web.servlet;

import cn.itcast.travel.service.impl.UserServiceImpl;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/activeUserServlet")
public class ActiveUserServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 获取激活码
        String code = request.getParameter("code");
        if (code != null){
            // 2. 调用service完成激活
            UserServiceImpl service = new UserServiceImpl();
            boolean flag = service.active(code);
            // 3. 判断标记
            String msg = null;
            if (flag){
                // 激活成功
                msg = "激活成功,请<a href='login.html'>登录</a>";
            }else{
                // 激活失败
                msg = "激活失败,请联系管理员!";
            }
            // 回写数据
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().write(msg);
        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}
4.3.6.5 激活Service代码
@Override
public boolean active(String code) {
    // 1. 根据激活码查询用户对象
    User user = userDao.findByCode(code);
    if (user != null){
        // 2. 调用dao的修改激活状态的方法
        userDao.updateStatus(user);
        return true;
    }else{
        return false;
    }
}
4.3.6.6 激活Dao代码:findByCode&updateStatus
/**
 * 根据激活码查询用户对象
* @param code
* @return 查询到的对象
*/
@Override
public User findByCode(String code) {
    User user = null;
    try{
        String sql = "select * from tab_user where code = ?";
        user = template.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), code);
    }catch (DataAccessException e){
        e.printStackTrace();
    }
    return user;
}

/**
* 修改指定用户激活状态
* @param user
*/
@Override
public void updateStatus(User user) {
    template.update("update tab_user set status = 'Y' where uid = ?",user.getUid());
}

5. 登录

5.1 分析

title

5.2 前台代码实现

<script>
    $(function () {
    // 1. 给登录按钮绑定单击事件
    $("#btn_sub").click(function () {
        // 2. 发送ajax请求,提交表单数据
        $.post("loginServlet",$("#loginForm").serialize(),function (data) {
            // data: {flag:false,errorMsg:""}
            if (data.flag){
                // 登录成功
                location.href = "index.html";
            } else{
                // 登录失败
                $("#errorMsg").html(data.errorMsg);
            }
        });
    });
});
</script>

5.3 后台代码实现

5.3.1 登录控制器:LoginServlet

package cn.itcast.travel.web.servlet;

import cn.itcast.travel.domain.ResultInfo;
import cn.itcast.travel.domain.User;
import cn.itcast.travel.service.impl.UserServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.beanutils.BeanUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;

@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 获取用户名和密码
        Map<String, String[]> map = request.getParameterMap();
        // 2. 封装User对象
        User user = new User();
        try {
            BeanUtils.populate(user,map);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        // 3. 调用service查询
        UserServiceImpl service = new UserServiceImpl();
        User u = service.login(user);
        ResultInfo info = new ResultInfo();

        // 4. 判断用户对象是否为null
        if (u == null){
            // 用户名或密码错误
            info.setFlag(false);
            info.setErrorMsg("用户名或密码错误");
        }
        // 5. 判断用户账户是否激活
        if (u != null && !"Y".equals(u.getStatus())){
            // 用户未激活
            info.setFlag(false);
            info.setErrorMsg("您的账户尚未激活,请激活");
        }
        // 6. 判断登录成功
        if (u != null && "Y".equals(u.getStatus())){
            // 登录成功
            info.setFlag(true);
            // 将登陆用户信息存入session中
            request.getSession().setAttribute("user",u);
        }
        // 响应数据
        ObjectMapper mapper = new ObjectMapper();
        response.setContentType("application/json;charset=utf-8");
        mapper.writeValue(response.getOutputStream(),info);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

5.3.2 服务层代码

/**
* 进行登录操作
* @param user
* @return User对象
*/
@Override
public User login(User user) {
    return userDao.findByUsernameAndPassword(user.getUsername(),user.getPassword());
}

5.3.3 Dao层代码

/**
* 根据用户名和密码,查询是否有此用户
* @param username
* @param password
* @return user对象
*/
@Override
public User findByUsernameAndPassword(String username, String password) {
    User user = null;
    try{
        String sql = "select * from tab_user where username = ? and password = ?";
        user = template.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), username,password);
    }catch (DataAccessException e){
        e.printStackTrace();
    }
    return user;
}

5.3.4 用户登录后,主页显示登录用户名功能

header.html代码

<script>
    $(function () {
       $.get("findUserServlet",{},function (data) {
           var msg = "欢迎回来,"+data.name;
           $("#span_username").html(msg);
       })
    });
</script>

控制器代码

package cn.itcast.travel.web.servlet;

import com.fasterxml.jackson.databind.ObjectMapper;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/findUserServlet")
public class FindUserServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 从session中获取登录用户
        Object user = request.getSession().getAttribute("user");
        // 将user回写客户端
        ObjectMapper mapper = new ObjectMapper();
        response.setContentType("application/json;charset=utf-8");
        mapper.writeValue(response.getOutputStream(),user);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

6. 退出

什么情况下算登录了?答:session中有user对象。

  • 实现步骤:
    • 访问servlet,将session销毁
    • 跳转到登录页面
  • 前台代码实现:
<a href="javascript:location.href='exitServlet';">退出</a>
  • 后台代码实现:
package cn.itcast.travel.web.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/exitServlet")
public class ExitServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 销毁session
        request.getSession().invalidate();
        // 2. 跳转到登录页面
        response.sendRedirect(request.getContextPath()+"/login.html");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

  转载请注明: Zero的博客 旅游网项目01

 上一篇
旅游网项目02 旅游网项目02
1. 前言旅游网项目,今日内容主要为优化Servlet、分类数据查询和分页查询。 2. 优化Servlet2.1 目的为了减少Servlet的数量,现在是一个功能一个Servlet,将其优化为相同功能的Servlet为一个模块。相当于在数
2019-07-23
下一篇 
Maven Maven
1. Maven基础概念和配置Maven是一个项目管理工具,它包含了一个项目对象模型(POM:Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Depende
2019-07-16
  目录