0%

旅游网站项目

🔰JavaWeb-旅游网站项目🔰

简单的JavaWeb项目,项目地址:🔗JavaProject-TourismWeb

技术选型📌

  1. Web层

    • Servlet:前端控制器
    • Html:视图
    • Filter:过滤器
  2. BeanUtils:数据封装

    • Jackson:json序列化工具
    • Service层
    • Javamail:java发送邮件工具
    • Redis:nosql内存数据库
    • Jedis:java的redis客户端
  3. Dao层

    • Mysql:数据库
    • Druid:数据库连接池
    • JdbcTemplate:jdbc的工具

数据库表结构🔑

使用sql脚本完成数据库表的建立

  • tab_user:用户信息表
  • tab_category:分类条目表
  • tab_route:旅游线路表

数据库表结构

主要实体类结构

User类 ResultInfo类 Category类 PageBean类 Route类 RouteImg类 Seller类 Favorite类
private int uid;//用户id
private String username;//用户名,账号
private String password;//密码
private String name;//真实姓名
private String birthday;//出生日期
private String sex;//男或女
private String telephone;//手机号
private String email;//邮箱
private String status;//激活状态,Y代表激活,N代表未激活
private String code;//激活码(要求唯一)
private boolean flag;//后端返回结果正常为true,发生异常返回false
private Object data;//后端返回结果数据对象
private String errorMsg;//发生异常的错误消息
private int cid;//分类id
private String cname;//分类名称
private int totalCount; //总记录数
private int totalPage; //总页数
private int currentPage;//当前页码
private int pageSize;//每页显示的条数
private List list;//每页显示的数据集合
private int rid;//线路id,必输
private String rname;//线路名称,必输
private double price;//价格,必输
private String routeIntroduce;//线路介绍
private String rflag; //是否上架,必输,0代表没有上架,1代表是上架
private String rdate; //上架时间
private String isThemeTour;//是否主题旅游,必输,0代表不是,1代表是
private int count;//收藏数量
private int cid;//所属分类,必输
private String rimage;//缩略图
private int sid;//所属商家
private String sourceId;//抓取数据的来源id
private Category category;//所属分类
private Seller seller;//所属商家
private List routeImgList;//商品详情图片列表
private int rgid;//商品图片
id private int rid;//旅游商品
id private String bigPic;//详情商品大图
private String smallPic;//详情商品小图
private int sid;//商家
id private String sname;//商家名称
private String consphone;//商家电话
private String address;//商家地址
private Route route;//旅游线路对象
private String date;//收藏时间
private User user;//所属用户

用户模块

用户注册功能

架构 前端 Servlet层 Service层 Dao层
文件 register.html RegistUserServlet UserService UserDao
功能说明 1. 使用js完成表单校验
2. 使用ajax提交表单数据到RegistUserServlet中进行注册
3. 根据ajax的相应数据data判断是否注册成功
4. 注册成功后,跳转到register_ok.html;否则显示注册失败的信息
1. 校验验证码,通过request获取前端的验证码check,与session中的CHECKCODE_SERVER进行比较,如果验证码不一样,直接返回错误信息,不必进行注册功能
获取前端表单的数据
2. 使用BeanUtils将数据封装为User对象
3. 调用service的功能函数完成注册
4. 根据service的返回值判断是否注册成功,并将提示信息存储到ResultInfo的对象info中,用于设置提示信息
5. 使用json将info对象序列化并发送给前端
register()调用Dao层根据用户名查询用户
1. 查询存在,直接返回false说明用户名已经存在;
2.查询不存在,则调用Dao层保存该用户信息,说明注册成功
1. findByUsername()根据用户名查询是否存在该用户,返回查询结果user对象,如果不存在则为null
2. save()用于保存用户信息
  • 在register.html中编写js代码进行表单校验,在点击提交按钮或是某一输入框失去焦点时触发对应事件。
    1. checkUsername()校验用户名:单词字符,8-20位;
    2. checkPassword()校验密码:单词字符,8-20位;
    3. checkEmail()校验email:邮件格式;
    4. checkName()校验姓名:非空;
    5. checkTelephone()校验手机号:手机号格式;
    6. checkBirthday()校验出生日期:非空;
    7. checkCheck()校验验证码:非空

邮件激活功能✉️

架构 前端 Servlet层 Service层 Dao层
文件 register_ok.html发送邮件给用户,邮件中附带激活链接 ActivateUserServlet UserService UserDao
功能说明 点击激活该用户,跳转到相关的ActivateUserServlet,并带有参数code,表示对应激活码(唯一标识) 1. 获取激活码
2. 如果激活码不为空,使用service方法进行结果,根据返回值判断是否激活成功并显示相应的信息激活码查询对应的User对象
activate()调用dao层进行对象的激活,首先根据激活码code查询是否存在该用户,若存在则更新该用户状态为激活,不存在则直接返回false 1. findByCode()根据用户的激活码查询用户,返回查询结果user对象
2. updateStatus()更新用户状态status = 'Y’为激活

用户登录功能🔐

架构 前端 Servlet层 Service层 Dao层
文件 login.html LoginServlet UserService UserDao
功能说明 1. 设置登录按钮单击事件
2. 异步发送ajax请求,提交数据到LoginServlet,根据返回信息判断是否登录成功
3. 登录成功则跳转到index.html界面,否则显示错误提示
1. 获取用户信息,使用BeanUtils进行封装
2. 调用service中的方法查询user
3. 根据查询返回的user对象,判断该用户是否存在以及是否激活,并保存对应的信息到info对象中
4. 如果用户存在且激活,则登录成功,需要将该用户对象写入session中
5. 使用json将info对象序列化并发送给前端
login()调用dao层进行用户登录 findByUsernameAndPassword()根据用户名和密码进行登录

显示用户登录状态和登出功能

架构 前端 Servlet层
文件 head.html FindUserServlet
LogoutServlet
功能说明 1.在页面加载完成后,提交请求到FindUserServlet中获取当前登录的用户对象,并显示在页面上方
2.给登出按钮绑定单击事件,点击登出后,跳转到LogoutServlet执行登出操作
1.FindUserServlet接受前端的请求,在页面加载完成后,从当前session中获取存储的已登录的用户对象,并将该对象通过json写回到客户端浏览器中,前端界面通过ajax获取该用户名并显示
2.LogoutServlet销毁当前session并将页面跳转到login.html实现登出功能

优化Servlet ⭐️⭐️⭐️

在开发过程中,完成一个模块时,如用户模块,会产生多个Servlet(注册,登录,登出等),为了减少Servlet文件的数量,将多个Servlet中的功能全部抽象为对应的方法,集合到一个Servlet中。

类继承关系

如图所示:BaseServlet用于实现方法的分发,关于用户的操作,将方法全部写在UserServlet中。

BaseServlet

重写HttpServlet中的service()方法,用于实现方法的分发。

  1. 通过获取URI,如:/project/user/login
  2. 使用字符串方法获取URI中最后的方法名,如login
  3. 使用反射机制获取对应的方法,如:UserServlet.login();
  4. 使用invoke()执行方法。
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //完成方法分发
    //1.获取请求路径
    String uri = req.getRequestURI();
    //2.获取方法名称
    String methodName = uri.substring(uri.lastIndexOf('/') + 1);
    //3.获取方法对象
    try {
        Method method = this.getClass().getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
        //4.执行方法
        method.invoke(this, req, resp);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
}

UserServlet

  1. 将之前实现各自功能的Servlet全部抽象成一个方法,如:LoginServlet ->login(),写入得到UserServlet中;
  2. 需要将这些方法的修饰符从默认的protected修改为public。因为在BaseServlet中调用getMethod()执行对应方法,获取的是public方法,包括继承的方法,如果使用getDeclaredMethods(),可以取得所有自己声明的方法,包括 public protected default private
  3. 一些方法中多次定义使用的变量,如UserService,可以声明为UserServlet的成员变量。
  4. UserServlet的请求路径修改为/User/*,代表User下的所有方法都会请求该Servlet,然后根据方法名,调用父类BaseServlet中的service()进行方法的分发执行。
@WebServlet(urlPatterns = "/User/*")
public class UserServlet extends BaseServlet {
    private UserService userService = new UserServiceImpl();
    /**
     * 登录功能
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    public void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Map<String, String[]> map = request.getParameterMap();
        User user = new User();
        try {
            BeanUtils.populate(user, map);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        // UserService service = new UserServiceImpl();
        User u = userService.login(user);
        
        ResultInfo info = new ResultInfo();
        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())) {
            request.getSession().setAttribute("user", u);
            info.setFlag(true);
        }
        ObjectMapper mapper = new ObjectMapper();
        response.setContentType("application/json; charset=UTF-8");
        mapper.writeValue(response.getOutputStream(), info);
    }
}

修改相关路径

最后还需要在请求Servlet的地方将原来的路径修改为新的路径,如:/LoginServlet ->/User/login

分类显示模块💻

页面顶端分类栏显示

在页面加载完成时,顶端显示分类栏,包括:首页,门票,酒店,香港车票,出境游,国内游,港澳游,抱团定制,全球自由行,收藏排行榜。

架构 前端 Servlet层 Service层 Dao层
文件 header.html CategoryServlet CategoryService CategoryDao
功能说明 在页面加载完成后,提交ajax请求,获取所有的分类,并显示在页面上方
findAll()调用service查询所有的分类条目,并将结果通过json写回客户端。 findAll()调用dao层进行查询 findAll()查询tab_category表中的所有数据并返回

⭐️ 优化

分析发现,分类的数据在每一次页面加载后都会重新请求数据库来加载,对数据库的压力比较大,而且分类的数据不会经常产生变化,所有可以使用redis来缓存这个数据。

方法:在service中查询时首先到redis中查询,如果查询结果为空,说明是第一次访问,将查询数据存入redis中;如果不为空,则表示不是第一次访问。最后返回查询结果集合即可。

修改CategoryServiceImpl中的findAll()方法:

public List<Category> findAll() {
        //1.获取Jedis客户端
        Jedis jedis = JedisUtil.getJedis();
        //2.使用sortedset作为数据结构,key为category,0到-1表示所有
        Set<Tuple> redisCategories = jedis.zrangeWithScores("category", 0, -1);
        
        List<Category> categories = null;

        //3.判断redis缓存中的集合是否为空
        if (redisCategories == null || redisCategories.size() == 0) {
            //如果缓存中为空,从数据库中查询,并将结果写入缓存
            categories = categoryDao.findAll();
            for (int i = 0; i < categories.size(); i++) {
                jedis.zadd("category", categories.get(i).getCid(), categories.get(i).getCname());
            }
        } else {
            //4.如果不为空,将set的数据存入list
            categories = new ArrayList<Category>();
            for (Tuple tuple : redisCategories
            ) {
                Category category = new Category();
                category.setCname(tuple.getElement());
                category.setCid((int) tuple.getScore());
                categories.add(category);
            }
        }
        return categories;
}

分类内容的分页展示🗼

点击不同的分类条目,进入具体类别的内容展示页面,进行分页显示。

分类条目cid的传递

在查询分类对象category时,保存其中的cid属性值,用于传递给前端页面,使前端页面在点击相应分类条目时能够根据id进行跳转。

  1. 在header.html页面中,对于每一个分类条目,传递cid的值

    $.get("Category/findAll", {}, function (data) {
        var lis = '<li class="nav-active"><a href="index.html">首页</a></li>';
        for (var i = 0; i < data.length; i++) {
            var li = '<li><a href="route_list.html?cid= ' + data[i].cid + '">' + data[i].cname + '</a></li>';
            lis += li;
        }
        lis += '<li><a href="favoriterank.html">收藏排行榜</a></li>';
        $("#category").html(lis);
    });
    
  2. 在route_list.html中获取点击的分类条目的cid的值,location.search方法会返回返回当前 URL 的查询部分,即问号 ? 之后的内容,在此处为?cid=xx

    $(function () {
        var search = location.search;
        // 切割字符串,拿到第二个值
        var cid = search.split("=")[1];
    });
    

分页展示功能

架构 前端 服务器端 Service层 Dao层
文件 route_list.html RouteServlet RouteService RouteDao
功能说明 1. 客户端页面发送ajax请求用于请求Pagebean对象数据
2. 携带数据:currentPage当前页码,pageSize每页显示的条数,cid分页id
1. 接受前端的参数,包括currentPage当前页码,pageSize每页显示的条数以及cid分页id;
2. 对于参数做合法性的验证和处理;
3. 调用service中的方法进行分页查询;
4. 查询结果为pageBean对象,将该对象通过json写回客户端。
pageQuery()根据进行分页查询。
分别调用dao中的方法,查询总记录数以及当前页的数据集合,与其他数据一起封装到pageBean对象中并返回。
findTotalCount查询总的记录数;
findByPage查询当前页的记录。

搜索功能🔍

架构 前端 服务器端
文件 route_list.html
header.html
RouteServlet
RouteService
RouteDao
功能说明 1. 在header.html中,给搜索按钮绑定单击事件,获取当前页面的cid以及搜索框的内容rname,拼接到跳转路径中
2. route_list.html中的分页显示代码中加入rname条件
对已有的查询相关方法进行重构,添加rname参数

商品详情页面

架构 前端 服务器端 Service层 Dao层
文件 route_list.html
route_detail.html
RouteServlet RouteService RouteDao
RouteImgDao
SellerDao
功能说明 1. route_list.html中点击单个商品详情时执行跳转,跳转路径为:route_detail.html?rid=xxx
2. route_detail.html中当页面加载成功时发送ajax请求,根据rid查询对应的route对象
findOne()方法:
1. 接受客户端的数据rid
2. 根据rid调用service查询对应的route对象
3. 将查询结果通过json写回客户端
findOne()方法:
1. 根据rid调用RouteDao查询route对象
2. 根据rid调用RouteImgDao查询tab_route_img表,将结果集合写入到route对象中
3. 根据sid查询tab_seller查询卖家信息,将结果集合写入到route对象中
实现具体的查询方法去对应表中查询数据

商品收藏功能❤️

判断是否收藏

架构 前端 服务器端 Service层 Dao层
文件 route_detail.html RouteServlet FavoriteService FavoriteDao
功能说明 1. 当页面加载完成后,发送ajax请求,传递rid,获取用户是否收藏的标记
2. 根据标记展示不同的按钮样式
1.获取rid
2. 获取当前登录的用户对象,如果user对象为null,设置uid=0
3. 调用FavoriteService查询,传递rid,uid
4. 写回客户端flag
isFavorite()方法:使用rid和uid判断该route是否在该user的favorite中 findByUidAndRid()

点击收藏

架构 前端 服务器端 Service层 Dao层
文件 route_detail.html RouteServlet FavoriteService FavoriteDao
功能说明 发送ajax请求判断用户是否登录:如果登录则可以点击按钮,否则提示没有登录 1. 获取rid和uid
2.调用service查询
add()方法增加一条收藏信息 add()
-------------------本文结束 感谢您的阅读-------------------

本文标题:旅游网站项目

文章作者:Sucre

发布时间:2020年08月13日 - 18:58:48

最后更新:2020年08月13日 - 19:30:54

原始链接:https://tangtangsama.github.io/article/5d874fe.html/

非商业性使用-转载请保留原文链接及作者。

感谢您的支持和鼓励!