web综合项目练习

版权声明:--------------------------新手上路,如有不正确的地方,还请多多指出不足之处--------------------------- https://blog.csdn.net/qq_43386754/article/details/86757815

1. 项目的开发说明和准备

  • 项目github仓库:https://github.com/Xiemaoshu/EMProject.git

  • 这个项目的案例的核心模块只有两大块,但是这两大块会涉及到许多验证以及业务相关的操作处理部分.在整个项目中关注的点有如下几点

    1. 了解整个项目的开发的流程
    2. 所有的项目核心就是crud,单表,一对多,多对多;
    3. 感受如果做一个全栈工程师.
    4. 本次项目不使用流行框架完成,观察不使用框架的问题所在.
  • 核心功能

  • 做一个公司内部的人事管理系统,主要是负责雇员的操作操作管理,也就是说所有的操作都围绕着雇员的crud操作,但是这一过程之中需要注意一下几点:

    1. 公司的工资需要进行严格的等级控制一定要注意如果要新增员工,那么新员工必须在指定的等级范围之中.
    2. 公司的所有员工都要求有部门,但是每一个部门都有人员上限(上限有管理员定义好的)那么就意味着在进行员工添加的时候需要考虑到部门上限的控制,当员工离职的时候需要考虑到部门人数的减少.
    3. 考虑到员工的信息安全,必须有人是管理员负责员工的添加以及修改等操作,但是需要将每一次的修改记录进行保存.
    4. 在进行部门列表的时候要求可以在本页面上弹出一个新的层来进行员工的信息列出,同时此列表可以进行分页控制(无刷新分页)
    5. 整个的系统中不允许出现删除操作
  • 本系统中还有一个关键的问题,权限认证问题,权限认证如果真要操作,一定在控制层和业务层上进行限制.

  • 如果从MVC标准开发来讲,整个的开发之中任何一个JSP页面都不允许直接链接跳转到另一个JSP页面,必须使用Servlet跳转,必须使用MVC设计模式完成

  • 核心技术

  • MVC+JQuery+Bootstrap

1.2 数据库设计

  1. 用户表

在这里插入图片描述

  • 在整个的代码之中,密码一定要进行加盐处理,所以本次在进行测试数据准备的时候也准备好了salt.
  • salt:mldnjava,Base64:“bWxkbmphdmE=”,格式:密码 password({salt})
  • 在用户表中有一个超级管理员标记,一个系统中有且只有一个超级管理员标记,超级管理员是绝不允许删除的,而后所有的用户要想进行操作控制,那么就必须有角色
    • 超级管理员标记的项有两个内容:
    • 普通用户:sflag=0;
    • 超级管理员:sflag=1;
  1. 角色表
    • 一个用户拥有多个角色信息,而且一个角色会有对应有多个权限

在这里插入图片描述

  • 在"用户-角色"关系表之中,其主导作用的是用户表,在角色表中有一个最为重要的字段:flag.再将在整个程序中进行角色验证关键因素所在.
  1. 权限关系:一个角色对应有多个权限信息

在这里插入图片描述

  • 在权限表中拥有一个标记信息,这个信息于角色标记作用如出一辙,它里面保存也是程序之中需要进行判断的权限名称标记.

数据字典表

  1. 部门表

在这里插入图片描述

  • 在部门之中有一个最关键的部分称为最大人数,在进行雇员信息添加的时候,一定要保证当前的人数不超过最大人数,还有一个当前人数,表示部门已经存在的人数,表示当雇员增加的时候或离职的时候需要进行此类信息的更改.
  1. 职位等级表:level

在这里插入图片描述

  • 职位等级一定要和工资对等,也就是说现在增加了一个新雇员,但是设置的工资内容超过了等级范围,表示这是一个错误的数据,将不允许雇员的添加.

  • 在进行设计过程之中也为项目进行了简化,例如:要设计部门的经理信息,那么必须要求经历达到了某个等级之后才能够进行安排,而且一个部门可能有一个经理,还有两个副经理.

1.3雇员信息表

  • 用户登录之后具备有雇员权限的用户应该具备有雇员信息的crud处理(不包含删除),在进行雇员信息添加的时候需要添加以下内容,

在这里插入图片描述

  • 其中用户ID指的就是添加该雇员信息的管理员,那么也就是说人事用户.其中雇员的雇佣日期需要使用当前日期时间生成.
  • 而对于雇员的照片,需要保存两份,一个是原始图片,另一份是压缩图片,现实的时候显示压缩图片
  1. 雇员信息日志表
  • 每一个雇员的创建,修改,离职等操作都要求在固原日志信息表上进行列出.

在这里插入图片描述

  • 这个备注要求记录好雇员信息变更时的相关意见.所有的备注信息只允许出现查询和增加两个功能.

  • 这几张表中都是最基础的业务逻辑操作,那么关键是这些标记字段的设计

  • 完整数据表关系图,使用PowerDesigner数据库建模工具软件

在这里插入图片描述

1.4页面组成分析

  • 最理想的状态是只写后台业务代码,前台的开发都有美工完成,但是在中国软件开发公司中,一般都由程序员完成.
  • 整个界面的功能由如下几个部分组成
    1. 登录界面,这里面需要使用ajax进行页面处理
    2. 主页面,包含所有页面信息
    3. 雇员列表
    4. 部门列表
    5. 雇员操作界面
WEBROOT----------web项目的发布目录
js:-----------------------保存所有的\*.js文件
-----|page
------------|back
--------------------|emp
---------------------------|emp_add.js-------专门为emp_add.jap页面服务
----|login.js	----------------这个为跟路径下的login.js服务
login.jsp---------------扶着登录界面显示
|pages------------------保存所有的页面
----|index.jsp--------保存所有首页信息
----|back--------------保存所有需要进行过滤检测的程序
------------|member--------后台用户系统
-------------|dept------------部门模块的文件
-------------|emp-------------雇员模块信息
-------------------|emp_add.jsp

2. 用户登录模块开发

2.1模块开发要求

  1. 整个项目之设计了一个单用户的登录用户操作,所以所有的操作都必须要求用户登录之后才能够操作

  2. 用户登录之后要求去除所有的角色信息和权限信息,这些信息是用来控制前台链接使用的

    • 为了方便进行角色或权限数据的查找,那么建议讲这些权限或角色的信息保存在Map集合里面
  3. 用户登录时考虑到安全性需要使用验证码

    • 验证码所使用到的开发包:kaptcha
  4. 进入到首页之后需要进行页面链接的动态判断

  5. 使用的数据库

在这里插入图片描述

  • 用户要求输入用户名和密码进行登录
  • 登录成功之后需要取出用户的角色或者是权限
  • 进入到首页之后需要根据角色或者是权限动态判断那些链接需要生成,所有的链接地址徐奥由开发者自己设置好相应路径,其基本原则:所有的jsp页面必须提交到servlet之后才可以跳转到jsp页面
  • 考虑到浏览器的屏蔽问题,不建议使用弹出框进行提示信息,建议使用倒计时的方式完成

2.2基础登录实现

  1. 建立Member表的vo类:mao.shu.emp.vo.Member
package mao.shu.emp.vo;

import java.io.Serializable;

public class Member implements Serializable {
    private String mid;
    private String password;
    private String name;
    private int sflag;

    public String getMid() {
        return mid;
    }

    public void setMid(String mid) {
        this.mid = mid;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSflag() {
        return sflag;
    }

    public void setSflag(int sflag) {
        this.sflag = sflag;
    }
}

  • 对于vo类需要实现Serializable接口,可以序列化对象.
  1. 建立IMemberDAO接口,这个接口直接继承自IDA接口
package mao.shu.em.dao;

import mao.shu.em.vo.Member;
import mao.shu.util.IDAO;

public interface IMemberDAO extends IDAO<String, Member> {
}

  1. 建立MemberDAO子类,这个子类只需要复写findById()方法

public class MemberDAO extends AbstractDAO implements IMemberDAO {
  @Override
    public Member findById(String id) throws SQLException {
        String sql="SELECT mid,password,name,sflag FROM member WHERE mid=?";
        super.pstmt = super.conn.prepareStatement(sql);
        ResultSet rs = super.pstmt.executeQuery();
        Member vo = null;
        if(rs.next()){
            vo = new Member();
            vo.setMid(rs.getString(1));
            vo.setPassword(rs.getString(2));
            vo.setName(rs.getString(3));
            vo.setSflag(rs.getInt(4));
        }
        return vo;
    }
 }
  • 建立一个业务的操作,考虑到这是一个后台项目,为了日后能够有功能扩充,定义的接口都属于后台业务接口,所以建议将其保存在mao.sji.em.service.back包下,而建立的业务结构名称为:IMemberServiceBack
package mao.shu.em.service.back;

import mao.shu.em.vo.Member;

import java.util.Map;

public interface IMemberServiceBack {
    /**
     * 实现用户登录的方法,该方法主要完成一下功能<br>
     *     1. 实现登录检测,调用 IMemberDAP.findById()方法<br>
     *     2. 获取用户的所有角色信息<br>
     *     3. 获取所有的用户权限信息<br>
     * @param vo 包含一个试图登录的用户的基本信息,输入的用户名和密码
     * @return 该方法将所有的信息以Map集合形式返回,map集合中会拥有以下内容
     *     1. key = flag value= true | false; 标志着用户登录成功或失败的标记<br>
     *     2. key = sflag value = 0 | 1 判断当前用户是否为超级管理员
     *     3. key = name  value = 用户的真是姓名
     *     4. key = allRoles value = set集合 保存用户所有的角色数据
     *     5. key = allActions value =set集合 保存用户所有的权限信息
     * @throws Exception
     */
    public Map<String,Object> login(Member vo)throws Exception;
}

  1. 随后不要直接建立业务层子类,最好在业务层接口和子类之间设置一个抽象类:AbstractService
    • 但是这个类现阶段什么都不需要处理
package mao.shu.em.service.abs;

public class AbstractService {
}

  • 建立MemberServiceBackImpl子类
package mao.shu.em.service.back.impl;

import mao.shu.em.dao.IMemberDAO;
import mao.shu.em.service.back.IMemberServiceBack;
import mao.shu.em.vo.Member;
import mao.shu.util.factory.DAOFactory;

import java.util.HashMap;
import java.util.Map;

public class MemberServiceBackImpl implements IMemberServiceBack {

    @Override
    public Map<String, Object> login(Member vo) throws Exception {
        Map<String,Object> resultMap = new HashMap<String,Object>();
        IMemberDAO memberDAO = DAOFactory.getInstance(IMemberDAO.class);
        Member ckVO = memberDAO.findById(vo.getMid());
        if(ckVO.getPassword().equals(vo.getPassword())){
            resultMap.put("flag",true);//保存用户登录结果
            resultMap.put("name",ckVO.getName());//保存哟凝固的真实姓名
            resultMap.put("sflag",ckVO.getSflag());//保存用户的超级管理员标记

        }else{
            resultMap.put("flag",false);
        }
        return null;
    }
}

  1. 建立MemberLoginServlet,这里面要包含有两个功能,登录检测和注销功能
    • 修改Message.properties 文件增加相应的登陆成功提示信息
login.success.msg=用户登录成功!
login.failure.msg=用户登录失败(请确认用户名或密码正确)
  • 修改pages.properties文件,增加登录页映射操作
login.page=/login.jsp
index.page=/pages/index.jsp
  • 为了方便进行MD5加密处理,编写一个专门负责加密处理得类 :EncryptUtil
package mao.shu.util;

public class EncryptUtil {
    public static final String salt = "bWxkbmphdmE=";
    public static String getPassword(String password){
        return new MD5Code().getMD5ofStr(password + "({" + salt + "})");
    }

}

  • 在DispatcherServlet类中添加以下的方法
	public HttpSession getSession(){
		return this.request.getSession();
	}
	public void setSessionAttribute(String name,String value){
		this.request.getSession().setAttribute(name,value);
	}
	public HttpServletRequest getRequest(){
		return this.request;
	}
	public void setRequestAttribute(String name,String value){
		this.request.setAttribute(name,value);
	}
  • 完善MemberLoginServlet类中登录方法
 public String login(){
        //进行加密处理
        this.member.setPassword(EncryptUtil.getPassword(this.member.getPassword()));
        IMemberServiceBack memberServiceBack = ServiceFactory.getInstance(MemberServiceBackImpl.class);
        try {
            Map<String, Object> loginResult = memberServiceBack.login(this.member);
            boolean loginFlag = (boolean) loginResult.get("flag");
            if(loginFlag){
                super.setUrlAndMsg("index.page","login.success.msg");
                super.setSessionAttribute("name",loginResult.get("name"));
                super.setSessionAttribute("sflag",loginResult.get("sflag"));
            }else{
                super.setUrlAndMsg("login.page","login.failure.msg");

            }
        }catch (Exception e){
            e.printStackTrace();
        }
            return "forward.page";
    }
  • 测试登录效果

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3. 登录控制加强

  • 首先修改页面,将当前用户的真实姓名,进行显示

    • 修改两个页面:i
    • include_title_head.jsp,
    • include_menu_item.jsp
  • 修改"include_title_head.jsp"追加注销链接

<%
	String logOutUrl = "MemberLoginServlet/logout";
%>
  • 在MemberLoginServlet了之中对于注销的控制,只需要让session失效即可,之后应该跳转到登录页
    • 在Message.properties文件中追加注销信息
logout.msg=用户注销成功!
  • 完善MemberLoginServlet中的logOut()方法
   public String logout(){
        super.getSession().invalidate();//使session失效
        super.setUrlAndMsg("login.page","logout.msg");
        return "forward.page";
    }
  1. 由于整个后台的资源需要登录后才可以使用,name必须保证是已经等了故宫的用户才可以访问,因此建立增加过滤器.
  • 添加编码过滤器
package mao.shu.util.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/pages/*")
public class EncodingFilter implements Filter {


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding("UTF-8");
        servletResponse.setCharacterEncoding("UTF-8");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {

    }


}


  • 登录成功之后MemberLoginServlet类中一定要将mid添加到session中.
    • 修改MemberLoginServlet中的login()方法
    super.setSessionAttribute("mid",this.member.getMid());
  • 在Message.properties中添加一个未登录的信息
unlogin.msg=您还未登录,请登录后访问
  • 添加loginFilter过滤器
package mao.shu.em.filter;
import mao.shu.util.ResourceUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @author Xiemaoshu
 * @date : 2019年2月5日 21:05:49
 * 该类主要用于对/pages/下的所有页面,做一个用户是否登录过滤,,如果用户未登录,则跳转到login.jsp页面中
 *
 */
@WebFilter("/pages/*")
public class loginFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpSession session = request.getSession();
        if(session.getAttribute("mid")!=null){
            filterChain.doFilter(servletRequest,servletResponse);
        }else{
            String msg = ResourceUtils.get("Messages","unlogin.msg");
            String loginPage = ResourceUtils.get("Pages","login.page");
            String forwardPage = ResourceUtils.get("Pages","forward.page");
            servletRequest.setAttribute("url",loginPage);
            servletRequest.setAttribute("msg",msg);
            servletRequest.getRequestDispatcher(forwardPage).forward(servletRequest,servletResponse);
        }
    }

    @Override
    public void destroy() {

    }
}

  • 测试效果

在这里插入图片描述

  1. 服务器端检测
    • 正常来讲必须使用login.jsp表单进行提交之后才可以登录,如果有破坏意义的用户,那么就希望绕过表单,直接将参数交给MemberloginServlet/login处理
    • 所以在Validations.properties文件中添加规则操作
MemberLoginServlet.login.rules = member.mid:string|member.password|string
  • 随后还需要在Pages.properties中添加,一个执行MemberLoginServlet后的服务器端检测失败调换页面
#执行MemberLoginServlet的login()方法出现错误时,跳转的页面
MemberLoginServlet.login.error.page=/pages/index.jsp
  • 但有时候如果没有写 MemberLoginServlet.login.error.page 这个页面就可能会出现,错误,为了防止错误的放生,可以修改 DispatcherServlet 中的 getPageValue()方法,如果没有设置跳转页面,则统一跳转到 error.page页面中
/**
 * 取得Pages.properties文件中指定的key对应的value内容
 * 
 * @param pageKey
 *            要读取的资源文件的key信息
 * @return
 */
public String getPageValue(String pageKey) {
	try {
		return this.pageResource.getString(pageKey);
	}catch(Exception e){}
	return this.pageResource.getString("error.page");
}

在这里插入图片描述

  • 可以修改 error.jsp页面,显示出错误的提示信息
<div class="form-bottom" style="background: white;">
	<span
			class="h5">程序出错了,请返回首页,与管理员联系!
	改程序出现了如下的错误:
		<ul>
			<e:forEach items="${errors}" var="a">
				<li>${a.key}:${a.value}</li>
			</e:forEach>
		</ul>
	</span>
</div>

在这里插入图片描述

  • 既然这个项目代码最终要放到公网上,那么就必须考虑任何安全漏洞

角色与权限

  • 实现了基础的登录控制之后下面就需要针对角色和权限进行取出.在取出权限或角色的时候,所取出的是他的标记(flag)的内容.
  • 闯进Role 和Action 的vo类
package mao.shu.em.vo;

import java.io.Serializable;

public class Role implements Serializable {
    private Integer rid;
    private String title;
    private String flag;

    public Integer getRid() {
        return rid;
    }

    public void setRid(Integer rid) {
        this.rid = rid;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getFlag() {
        return flag;
    }

    public void setFlag(String flag) {
        this.flag = flag;
    }
}

package mao.shu.em.vo;

import java.io.Serializable;

public class Action implements Serializable {
    private Integer actid;
    private String title;
    private String flag;

    public Integer getActid() {
        return actid;
    }

    public void setActid(Integer actid) {
        this.actid = actid;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getFlag() {
        return flag;
    }

    public void setFlag(String flag) {
        this.flag = flag;
    }
}

  1. 定义IRoleDAO于IActionDAO
public interface IRoleDAO extends IDAO<Integer, Role> {
    /**
     * 该方法根据一个用户id查找该用户的所有角色信息
     * @return
     * @throws SQLException
     */
    public Set<String> findAllByMember(String mid)throws SQLException;
}
public interface IActionDAO extends IDAO<Integer, Action> {
    /**
     * 该方法根据一个用户id取出该用户的所哟权限信息
     * @return
     * @throws SQLException
     */
    public Set<String> findAllByMember(String mid)throws SQLException;
}

  1. 实现 RoleDAOImpl 和ActionDAOImpl
public class RoleDAOImpl extends AbstractDAO implements IRoleDAO {
    @Override
    public Set<String> findAllByMember(String mid) throws SQLException {
        String sql = "SELECT flag FROM role WHERE rid IN(" +
                "SELECT rid FROM member_role WHERE mid=?)";
        super.pstmt = super.conn.prepareStatement(sql);
        super.pstmt.setString(1,mid);
        ResultSet rs = super.pstmt.executeQuery();
        Set<String> rids = new HashSet<String>();
        while(rs.next()){
            rids.add(rs.getString(1));
        }
        return rids;
    }
 }
public class ActionDAOImpl extends AbstractDAO implements IActionDAO {

    @Override
    public Set<String> findAllByMember(String mid) throws SQLException {
        String sql = "select flag from action where actid in( " +
                        " select actid from role_action where rid in( " +
                             " select rid from member_role where mid=? " +
                         " ) " +
                      " ) ";
        super.pstmt = super.conn.prepareStatement(sql);
        super.pstmt.setString(1,mid);
        ResultSet rs = super.pstmt.executeQuery();
        Set<String> action_flags = new HashSet<String>();
        while(rs.next()){
            action_flags.add(rs.getString(1));
        }
        return action_flags;

    }
}    
  • 修改MemberServletBack,在用户登录成功之后为将用户的权限flag和角色flag的Set集合保存到登录方法返回的 Map集合中.

    @Override
    public Map<String, Object> login(Member vo) throws Exception {
        Map<String,Object> resultMap = new HashMap<String,Object>();
        IMemberDAO memberDAO = DAOFactory.getInstance(MemberDAO.class);
        Member ckVO = memberDAO.findById(vo.getMid());
        if(ckVO!= null) {
            if (ckVO.getPassword().equals(vo.getPassword())) {
                IRoleDAO roleDAO = DAOFactory.getInstance(RoleDAOImpl.class);
                IActionDAO actionDAO = DAOFactory.getInstance(ActionDAOImpl.class);
                resultMap.put("flag", true);//保存用户登录结果
                resultMap.put("name", ckVO.getName());//保存用户的真实姓名
                resultMap.put("sflag", ckVO.getSflag());//保存用户的超级管理员标记
                resultMap.put("allRoles",roleDAO.findAllByMember(vo.getMid()));
                resultMap.put("allActions",actionDAO.findAllByMember(vo.getMid()));

            } else {
                resultMap.put("flag", false);
            }
        }else{
            resultMap.put("flag", false);
        }
        return resultMap;
    }
  1. 这些数据在登录后的页面之中都需要使用到,所以应该将这些数据添加到添加到Session对象之中
    • 修改MemberLoginServlet
super.setSessionAttribute("allRoles",loginResult.get("allRoles"));
super.setSessionAttribute("allActions",loginResult.get("allActions"));
  1. 随后需要处理的就是菜单的显示处理,因为EL表达式无法使用set.contains()的方法判断Set集合中的数据,所以就必须使用jstl开发包的另一组标签:函数标签
    • 将fn.tld的标签文件拷贝到WEB-INF中(该标签文件在jstl的开发包之中)
    • 配置web.xml文件进行标签的引入声明

在这里插入图片描述

  • 修改web.xml文件
<taglib>
	<taglib-uri>http://www.mao.su/fn</taglib-uri>
	<taglib-location>/WEB-INF/fn.tld</taglib-location>
</taglib>
  • 在include_static_head.jsp页面中进行标签的定义:
<%@ taglib prefix="fn" uri="http://www.mao.su/fn" %>
  • 修改"include_menu_iterm.jsp页面进行判断角色和权限
<c:if test="${fn:contains(allRoles,'member' )}">
<c:if test="${fn:contains(allRoles,'dept' )}">
<c:if test="${fn:contains(allRoles,'emp' )}">
  • 现在不同的用户就会有不同的菜单出现,

在这里插入图片描述

在这里插入图片描述

验证码检测

  • 对于登录的验证码的检测一定要发那个在两个位置上完成
    1. 客户端检测
    2. 服务端检测处理
  1. 如果要进行客户端的验证码处理,那么需要编写一个专门的检测工具类,登录页面使用的验证码生成工具为 KaptchaServlet ,在web.xml文件中对保存到session属性名称中的配置如下
<init-param>	<!-- 设置验证码保存到session中的属性名称 -->
	<param-name>kaptcha.session.key</param-name>
	<param-value>rand</param-value>
</init-param>
  • 编写CodeCheckServlet.java程序类,改程序类为一个可重用的代码,所以可以定义在util包下
package mao.shu.util.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/CodeCheckServlet")
public class CodeCheckServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //得到用户表单提交的验证码
        String input_code = req.getParameter("code");
        //得到KaptchaServlet 生成的验证码
        String servlet_code = (String) req.getSession().getAttribute("rand");

        if(input_code != null) {
            resp.getWriter().print(input_code.equalsIgnoreCase(servlet_code));
        }else{
            resp.getWriter().print("false");
        }
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       this.doPost(req,resp);
    }


}

  • 在login.js文件中,提供有ajax异步处理的验证方法
			"code" : {
				required : true ,
				remote : {
					url : "CodeCheckServlet", // 后台处理程序
					type : "post", // 数据发送方式
					dataType : "html", // 接受数据格式
					data : { // 要传递的数据
						code : function() {
							return $("#code").val();
						}
					},
					dataFilter : function(data, type) {
						if (data.trim() == "true") {
							return true;
						} else { 
							return false;
						}
					}
				}

			}
		}
	});
  • 随后需要实现服务器端的验证处理,所以这个时候需要扩充验证工具类,现在假设验证码的检测标记为:rand
    • 修改Validations.properties文件,追加验证码检测处理
MemberLoginServlet.login.rules = member.mid:string|member.password:string|code|rand
  • 在Message.properties文件中添加一个验证码提示信息,
validation.rand.msg=验证码输入错误
  • 后端的验证由Validation.java类完成,因此要修改Validation.java文件,防止用户未填写登录表单,而直接访问index.jsp或者其他需要登录后才可以访问的页面
default : {//其它类型,表示验证的是验证码.
	if(!Validation.validateEmpty(val)){
		errors.put(temp[0],servlet.getMessageValue("validation.rand.msg"));
	}else{//如果此时有验证码
		//得到Servlet生成的验证码
		String rand = (String) servlet.getSession().getAttribute("rand");
		//和用户提交的验证码内容进行判断
		if(rand==null){
			errors.put(temp[0],servlet.getMessageValue("validation.rand.msg"));
		}else if(!rand.equalsIgnoreCase(val)){
			errors.put(temp[0],servlet.getMessageValue("validation.rand.msg"));
		}
	}
}
  • 这种编写方式最大的好处在于:主控制层MemberServletLogin.java就不需要在进行数据的验证了,所有的验证都交由Validation.java类完成,这样可以做到代码分离,写出简洁的代码.
  • 如果此时直接访问 http://localhost:8080/EMProject/MemberLoginServlet/login 会出现以下提示

在这里插入图片描述

  • 但是此时还会发现一个严重的错误,当用户在输入验证码表单的时候,如果用户输入完正确的验证码,但是还未提交,又点击了验证码图片切换了验证码,此时就会出现一下的错误

在这里插入图片描述

  • 这是由于用户之前的表单验证结果,还没有在前台页面及时刷新导致的,这使得前台的表单验证处于正确状态,但是用户提交之后,在后台验证出错.

  • 为了解决这个问题,可以使用以下的解决思路

    1. 当用户点击验证码图片切换验证码之后,应该清空验证码输入框的内容,并且恢复表单之前的样式.
  • 修改login.js文件,

	$("#imageCode").on("click",function(){
		$("#imageCode").attr("src","captcha.jpg?p=" + Math.random());
		vld.resetForm();//重置表单
		$("#code").val("");//清空验证码表单内容
		$("#codeDiv").attr("class","form-group")
	}) ;
  • 修改之后的效果

在这里插入图片描述

在这里插入图片描述

  • 很多的开发问题,一定要多思考,多使用才可以发现

雇员管理模块

开发任务

  • 雇员管理模块属于内部内部系统模块,他与登录不同,登录在进行操作的时候,每一个用户都可以进行操作,而雇员管理需要进行权限控制
  • 对于权限的控制需要在控制层和业务层两个位置完成
  • 控制层的检测可以通过session完成.
  • 而业务层需要进行数据库的查询检测
  1. 雇员入职
    • 首先一定要通过EmpServlet/addPre页面进行数据的读取:读取所有的部门数据以及所有的工资等级数据.
    • 部门追加管的时候都有人数限制,所以出现的部门都应该是有名额的部门
    • 员工等级的出现的主要目的是为了基本工资的合法性检测:
  • 前端使用ajax验证,而后端通过数据层的检测验证.
  1. 对于员工的列表操作,要考虑到分页的处理情况,以及模糊查询的处理问题.

    • 在进行员工编辑的时候牵扯到数据回填问题.
  2. 所有雇员的增加,修改都需要在一个雇员日志表中记录

    • 员工的相关信息说明要保存在雇员日志记录表里面
    • 每一次修改的时候这个对于雇员日志都属于新的内容,都要求重新保存

雇员增加—业务层

  • 雇员增加操作直接就是针对于emp表的insert处理,在整个的处理之中:需要注意以下几点
    • 雇员的工资必须符合于所处的职位级别
    • 雇员要分配的部门必须有空位
    • 部门人数一定要进行增加;
  1. 建立Emp.java的vo类
package mao.shu.em.vo;

import java.io.Serializable;
import java.util.*;

public class emp implements Serializable {
    private Integer empno;
    private Integer deptno;
    private String mid;
    private Integer lid;
    private String ename;
    private String job;
    private Double sal;
    private Double comm;
    private Date hiredate;
    private String photo;
    private Integer flag;

//getter和setter方法

}
  1. . 建立IEmpDAO接口
package mao.shu.em.dao;

import mao.shu.em.vo.Emp;
import mao.shu.util.IDAO;

public interface IEmpDAO extends IDAO<Integer, Emp> {
    
}

  1. 建立EmpDAOImpl子类,只需要实现一个doCreate()方法即可
public class EmpDAOImpl extends AbstractDAO implements IEmpDAO {
    @Override
    public boolean doCreate(Emp vo) throws SQLException {
        String sql = "INSERT INTO emp(deptno,mid,lid,ename,job,sal,comm,hiredate,photo,flag)VALUES(?,?,?,?,?,?,?,?,?,?)";
        super.pstmt = super.conn.prepareStatement(sql);
        super.pstmt.setInt(1,vo.getDeptno());
        super.pstmt.setString(2,vo.getMid());
        super.pstmt.setInt(3,vo.getLid());
        super.pstmt.setString(4,vo.getEname());
        super.pstmt.setString(5,vo.getJob());
        super.pstmt.setDouble(6,vo.getSal());
        super.pstmt.setDouble(7,vo.getComm());
        super.pstmt.setDate(8,new java.sql.Date(vo.getHiredate().getTime()));
        super.pstmt.setString(9,vo.getPhoto());
        super.pstmt.setInt(10,vo.getFlag());
        return super.pstmt.executeUpdate()> 0;
    }
}
  1. 建立Level.java的vo类
package mao.shu.em.vo;

import java.io.Serializable;

public class Level implements Serializable {
    private Integer lid;
    private Double losal;
    private Double hisal;
    private String title;
    private String flag;

	//getter 和 setter 方法
}
  1. 建立ILevelDAO接口,目的是为了根据指定的级别编号查询出工资范围,以方便进行数据验证使用
package mao.shu.em.dao;

import mao.shu.em.vo.Level;
import mao.shu.util.IDAO;

public interface ILevelDAO extends IDAO<Integer, Level> {
    
}

  • 建立LevleDAOImpl的子类.只需要复写findById()方法和findAll()方法即可
public class LevelDAOImpl extends AbstractDAO implements ILevelDAO {
   @Override
    public Level findById(Integer id) throws SQLException {
        String sql = "SELECT lid,losal,hisal,title,flag FROM level WHERE lid=?";
        super.pstmt = super.conn.prepareStatement(sql);
        super.pstmt.setInt(1,id);
        Level vo = null;
        ResultSet rs = super.pstmt.executeQuery();
        if(rs.next()){
            vo = new Level();
            vo.setLid(rs.getInt(1));
            vo.setLosal(rs.getDouble(2));
            vo.setHisal(rs.getDouble(3));
            vo.setTitle(rs.getString(4));
            vo.setFlag(rs.getString(5));
        }
        return vo;
    }
    @Override
    public List<Level> findAll() throws SQLException {
        String sql = "SELECT lid,losal,hisal,title,flag FROM level";
        super.pstmt = super.conn.prepareStatement(sql);
        List<Level> allLevels = new ArrayList<Level>();
        ResultSet resultSet = super.pstmt.executeQuery();
        while(resultSet.next()){
            Level vo = new Level();
            vo.setLid(resultSet.getInt(1));
            vo.setLosal(resultSet.getDouble(2));
            vo.setHisal(resultSet.getDouble(3));
            vo.setTitle(resultSet.getString(4));
            vo.setFlag(resultSet.getString(5));
        }
        return allLevels;
    }
}
  1. 建立IDeptDAO接口,需要确定人数
    • 建立Dept.java的vo类
package mao.shu.em.vo;

import java.io.Serializable;

public class Dept implements Serializable {
    private Integer deptno;
    private String dname;
    private Integer maxnum;
    private Integer currnum;

	//getter 和setter 方法
}
  • 建立IDeptDAO接口,但是在这个接口里面需要扩充两个方法
    • 列出所有有空余人数的部门数据,方便在进
    • 进行部门当前人数的更新处理操作
package mao.shu.em.dao;

import mao.shu.em.vo.Dept;
import mao.shu.util.IDAO;

import java.sql.SQLException;
import java.util.List;

public interface IDeptDAO extends IDAO<Integer, Dept> {
    /**
     * 查询出所有还未满员的部门
     * @return 所有未满员的部门数据
     * @throws SQLException
     */
    public List<Dept> findUnders()throws SQLException;


    /**
     * 对指定部门的当前人数进行更新
     * @param deptno 更新的部门编号
     * @param updateNum  更新的数量,该参数允许为负数,如果是负数,则更新为减少部门人数
     * @return 更新的结果,成功返回true,失败返回false
     * @throws SQLException 
     */
    public boolean updateCurrnum(Integer deptno,Integer updateNum)throws SQLException;
}

  • 实现编写DeptDAOImpl子类,复写以上的抽象方法,并且还要复写一个findById()方法,这个方法在进行雇员的添加的时候用户判断要添加的部门是否有空位
public class DeptDAOImpl extends AbstractDAO implements IDeptDAO {
    @Override
    public List<Dept> findUnders() throws SQLException {
        String sql = "SELECT deptno,dname,maxnum,currnum FROM dept WHERE CURRNUM < MAXNUM";
        super.pstmt = super.conn.prepareStatement(sql);
        List<Dept> under_Depts = new ArrayList<Dept>();
        ResultSet rs = super.pstmt.executeQuery();
        while(rs.next()){
            Dept vo = new Dept();
            vo.setDeptno(rs.getInt(1));
            vo.setDname(rs.getString(2));
            vo.setMaxnum(rs.getInt(3));
            vo.setCurrnum(rs.getInt(4));
            under_Depts.add(vo);
        }
        return under_Depts;
    }

    @Override
    public boolean updateCurrnum(Integer deptno,Integer updateNum) throws SQLException {
        String sql = "UPDATE dept SET currnum=currnum+"+updateNum+" WHERE deptno=?";
        super.pstmt = super.conn.prepareStatement(sql);
        super.pstmt.setInt(1,deptno);
        return super.pstmt.executeUpdate() > 0;
    }
   @Override
    public Dept findById(Integer id) throws SQLException {
       String sql = "SELECT deptno,dname,maxnum,currnum WHERE deptno=?";
       super.pstmt = super.conn.prepareStatement(sql);
       super.pstmt.setInt(1,id);
       ResultSet resultSet = super.pstmt.executeQuery();
        Dept vo = null;
       if(resultSet.next()){
           vo = new Dept();
           vo.setDeptno(resultSet.getInt(1));
           vo.setDname(resultSet.getString(2));
           vo.setMaxnum(resultSet.getInt(3));
           vo.setCurrnum(resultSet.getInt(4));
       }
       return vo;
    }
 }
  1. 建立IEmpServiceBack接口,实现雇员的增加处理
    • 在进行雇员追加的时候必须要求先列出所有的部门信息以及所有的职位等级信息.
package mao.shu.em.service.back;

import mao.shu.em.vo.Emp;

import java.util.Map;

public interface IEmpServiceBack {
    /**
     * 进行雇员增加时的准备信息提取操作,将增加雇员所需要的信息以一个Map集合的形式返回.
     * @return 返回的信息中包含如下的内容 </br>
     * 1. key=allUnderDepts     value = 包含所有未满员的部门信息,使用IDeptDAP.findUnders()方法完成</br>
     * 2. key = allLevels()     value = 所有工资等级的数据,使用ILevelDAO.findAll()方法完成
     * @throws Exception
     */
    public Map<String,Object> addPre()throws Exception;

    /**
     * 进行添加一名雇员信息的操作,该操作会进行如下几步<br/>
     * 1. 对当前操作的用户进行判断,判断当前用户是否具备有添加雇员的权限<br/>
     * 2. 判断要添加的部门是否有空余位置<br/>
     * 3. 判断添加的工资是否在所处的等级范围中<br/>
     * 4. 对新雇员的信息进行保存<br/>
     * 5. 保存成功之后,对所在的部门的当前人数进行+1操作<br/>
     * 6. 要进行相关日志的保存操作
     * @param vo 要添加的雇员信息
     * @return 成功返回true,失败返回false
     * @throws Exception
     */
    public boolean add(Emp vo)throws Exception;
}

  • 在AbstractService中进行权限判断处理,因为权限的判断在Emp的操作中许多方法都会用得到,所以应该将对权限的判断操作交给父类完成,
    • 在IActionDAO中扩充一个根据用户id,判断用户是否具备指定的权限数据,只有具备这个方法,才可以进行权限的验证

    /**
     * 该方法进行对一个指定用户,判断该用户是否具备有指定权限的方法
     * @param mid 用户的id
     * @param flag 权限的标记 以"emp:add"的形式传入
     * @return 如果该用户具备有制定权限返回true,否则返回false
     * @throws SQLException
     */
    public boolean exists(String mid,String flag)throws SQLException;
  • exists()方法的具体实现
    @Override
    public boolean exists(String mid,String flag) throws SQLException {
        String sql = "SELECT flag FROM action WHERE actid IN " +
                " (SELECT actid FROM role_action WHERE rid IN " +
                " (SELECT rid FROM member_role WHERE mid=?) " +
                " )AND flag=? ";
        super.pstmt = super.conn.prepareStatement(sql);
        super.pstmt.setString(1,mid);
        super.pstmt.setString(2,flag);
        ResultSet resultSet = super.pstmt.executeQuery();
        if(resultSet.next()){
            return true;
        }else{
            return false;
        }
    }
  • 在AbstractService中追加统一的验证处理
package mao.shu.em.service.abs;

import mao.shu.em.dao.IActionDAO;
import mao.shu.em.dao.ILevelDAO;
import mao.shu.em.dao.impl.LevelDAOImpl;
import mao.shu.util.factory.DAOFactory;

public abstract class AbstractService {
    /**
     * 该方法进行对指定的用户判断是否具有指定的权限,使用IActionDAO.exist()方法完成
     * @param mid 用户的id
     * @param actionFlag 权限的flag字段
     * @return 如果具有返回true,否则返回false
     * @throws Exception
     */
    public boolean auth(String mid,String actionFlag)throws Exception{
        IActionDAO actionDAO = DAOFactory.getInstance(IActionDAO.class);
        return actionDAO.exists(mid,actionFlag);
    }
}

  • 定义EmpServiceBackImpl类,该类继承自AbstractService 类,该类实现对雇员的增加操作
package mao.shu.em.service.back.impl;

import mao.shu.em.dao.IDeptDAO;
import mao.shu.em.dao.IEmpDAO;
import mao.shu.em.dao.ILevelDAO;
import mao.shu.em.dao.impl.DeptDAOImpl;
import mao.shu.em.dao.impl.EmpDAOImpl;
import mao.shu.em.dao.impl.LevelDAOImpl;
import mao.shu.em.service.abs.AbstractService;
import mao.shu.em.service.back.IEmpServiceBack;
import mao.shu.em.vo.Dept;
import mao.shu.em.vo.Emp;
import mao.shu.em.vo.Level;
import mao.shu.util.factory.DAOFactory;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class EmpServiceBackImpl extends AbstractService implements IEmpServiceBack {

    @Override
    public Map<String, Object> addPre() throws Exception {
        Map<String,Object> addPre_result = new HashMap<String,Object>();
        IDeptDAO deptDAO = DAOFactory.getInstance(DeptDAOImpl.class);
        ILevelDAO levelDAO = DAOFactory.getInstance(LevelDAOImpl.class);
        addPre_result.put("allUnderDepts",deptDAO.findUnders());
        addPre_result.put("allLevels",levelDAO.findAll());
        return addPre_result;
    }

    @Override
    public boolean add(Emp vo) throws Exception {
        //判断操作用户的权限是否具备添加用户的权限
        if(super.auth(vo.getMid(),"emp:add")){
            IDeptDAO deptDAO = DAOFactory.getInstance(DeptDAOImpl.class);
           Dept dept = deptDAO.findById(vo.getDeptno());
           //判断当前添加的部门人数是否已满
            if(dept.getCurrnum()<dept.getMaxnum()){
                ILevelDAO levelDAO = DAOFactory.getInstance(LevelDAOImpl.class);
                Level level =  levelDAO.findById(vo.getLid());
                //判断新增雇员的工资是否在对应的工资等级范围中
                if(vo.getSal()>=level.getLosal()&&vo.getSal()<=level.getHisal()){
                    vo.setFlag(1);
                    vo.setHiredate(new Date());
                    //执行添加雇员操作
                    IEmpDAO empDAO = DAOFactory.getInstance(EmpDAOImpl.class);
                    if(empDAO.doCreate(vo)){

                        //如果添加雇员成功,则将对应部门的人数加1
                        return deptDAO.updateCurrnum(vo.getDeptno(),1);
                    }
                }
            }

        }
        return false;
    }
}

  • 自此雇员添加操作的后台业务层完成

雇员入职—显示层

  • 业务层实现之后就需要进行控制层的编写了,需要定义EmpServletBack程序类.
    • 但是发现整个代码中都有权限控制,所以考虑到权限方便之处,建议在DispatcherServlet之下在建立一个EMServlet,作为一个公共的操作.
  • 因为在用户登录的时候MemberLoginServlet程序类已经将的登录用户的权限信息和角色信息都保存在了 session属性之中,因此,在EMServlet中可以直接利用session属性取出操作用户的权限信息
package mao.shu.em.servlet.abs;

import mao.shu.util.servlet.DispatcherServlet;

import java.util.Set;

public abstract class EMServlet extends DispatcherServlet {
    public boolean auth(String actionFlag)throws Exception{
        Set<String> allActions = (Set<String>)super.getSession().getAttribute("allActions");
        return allActions.contains(actionFlag);
    }

}

  • 定义EmpServletBack程序类,继承自EMServlet
    • 在Pages.properties文件中追加相关的跳转页面路径
emp.add.page=/pages/back/emp/emp_add.jsp
emp.add.servlet=/pages/back/emp/EmpServletBack/addPre
  • 在Message.properties文件中添加一个权限错误提示信息
auth.failure.msg=你还未具备此权限,无法进行此操作
  • 在DispatcherServlet中添加一个addError()方法,当用户操作雇员出现错误的时候,调用此方法,在"errors"的错误信息集合中添加操作错误信息
/**
	 * 向session属性中的 errors 错误集合中添加错误提示信息
	 * errors集合中添加错误信息
	 * @param key 错误的属性名称
	 * @param value Message.properties消息提示文件中的关键字
	 */
	public void setErrors(String key,String value){
		Map<String,String> error = (Map<String,String>)this.getSession().getAttribute("errors");
		if(error==null){
			error = new HashMap<String,String>();
		}
		error.put(key,value);
	}
  • 编写EmpServletBackImpl 类中的 addPre()和add()方法
 public String addPre()throws Exception{
        //判断当前操作用户是否具备有操作雇员的权限
        if(super.auth("emp:add")){
            IEmpServiceBack empServiceBack = ServiceFactory.getInstance(EmpServiceBackImpl.class);
            Map<String,Object> map = empServiceBack.addPre();
            //保存所有的工资等级和有空位置的部门
            super.setSessionAttribute("allUnderDepts",map.get("allUnderDepts"));
            super.setSessionAttribute("allLevels",map.get("allLevels"));
            return "emp.add.page";
        }else{
            super.setErrors("auth","auth.failure.msg");
            return "error.page";
        }

    }
    public String add(){
        try {
            //判断当前是否有添加雇员的权限
            if(super.auth("emp:add")){
                //判断当前是否有文件上传
                if(super.isUploadFile()){
                    this.emp.setPhoto(super.createSingleFileName());//创建文件名称
                }else{
                    this.emp.setPhoto("nophoto.png");//使用默认的图片
                }
                this.emp.setMid(super.getMid());//设置添加该雇员的操作用户
                IEmpServiceBack empServiceBack = ServiceFactory.getInstance(EmpServiceBackImpl.class);
                if(empServiceBack.add(this.emp)){
                    super.setUrlAndMsg("emp.add.servlet","vo.add.success.msg");
                    if(super.isUploadFile()){
                        super.saveUploadFile(this.emp.getPhoto());//保存原图片
                        super.saveScale(this.emp.getPhoto());//保存缩略图
                    }
                }else{
                    super.setUrlAndMsg("emp.add.servlet","vo.add.failure.msg");
                }
                return "forward.page";
            }else{
                super.setErrors("auth","auth.failure.msg");
                return "error.page";
            }
        } catch (Exception e) {

            e.printStackTrace();
            return "error.page";
        }

    }
  • 修改雇员增加的地址链接信息
<li class="${param.action=='emp:add' ? 'active' : ''}">
	<a href="pages/back/emp/EmpServletBack/add">
		<i class="fa fa-circle-o"></i> 雇员入职
	</a>
</li>
  • 修改emp_add.jsp的表单提交路径
<%
	String addEmpUrl = basePath + "pages/back/emp/EmpServletBack/add" ;
%>
  • 修改emp_add.jsp页面中的部门列表和级别列表数据,因为数据都从数据库取出了.
<select id="emp.deptno" name="emp.deptno" class="form-control">
	<option value="">====== 请选择所在部门 ======</option>
	<c:forEach items="${allUnderDepts}" var="dept">
	<option value="${dept.deptno}">${dept.dname}(剩余人数:${dept.maxnum-dept.currnum})</option>
	</c:forEach>
</select>
<select id="emp.lid" name="emp.lid" class="form-control">
	<option value="">====== 请选择职位等级 ======</option>
	<c:forEach items="${allLevels}" var="level">
	<option value="${level.lid}">${level.title}(工资范围:${level.losal}---${level.hisal})</option>
	</c:forEach>
</select>
  • 显示结果

在这里插入图片描述

在这里插入图片描述

  • 为了保存缩略图,使用了一个:ImageScale的工具类,每每次保存雇员的图片时也保存一张图片的缩略图.
  • 因此在DispatchServlet程序类中提供有保存缩略图的方法:saveScale()
public void saveScale(String fileName){
		String srcPath = null;
		String savePath = null;
		if(this.getUploadDir() == null || "".equals(this.getUploadDir())){
			savePath = super.getServletContext().getRealPath("/")+"sm-"+fileName;
			srcPath = super.getServletContext().getRealPath("/")+fileName;
		}else{
			savePath = super.getServletContext().getRealPath("/")+this.getUploadDir()+"sm-"+fileName;
			srcPath = super.getServletContext().getRealPath("/")+this.getUploadDir()+fileName;
		}
		ImageScale.scale(savePath,srcPath);
	}
  • 在EmpServletBack的程序类中定义了图片的保存路径
    public String getUploadDir() {
        return "/upload/emp/";
    }
  • 在EmpServletBack的程序类中定义了添加的数据类型
    public String getType() {
        return "雇员";
    }
  • 如果要上传图片,需要将emp_add.jsp页面中的表单添加"enctype=“multipart/form-data”,进行表单封转控制
<form action="<%=addEmpUrl%>" id="myform" method="post" class="form-horizontal" enctype="multipart/form-data">
  • 最终添加效果

在这里插入图片描述

在这里插入图片描述

  • 数据库中保存的数据

在这里插入图片描述

雇员入职—前端验证

  • 在进行雇员处理过程之中,对于工资等级和部门的人数的上限是需要进行前端验证的.
  1. 业务层里面追加相应的新的接口
    • 增加IDeptServiceBack服务接口,此操作主要提供一个更具编号查询部门完整信息的方法.
package mao.shu.em.service.back;

import mao.shu.em.vo.Dept;

public interface IDeptServiceBack {
    /**
     * 该方法根据一个部门编号查询处一个部门完整信息,使用的是IDeptDAO.findByID()方法实现
     * @param deptno 查询的部门编号
     * @return 返回一个完整的部门信息
     * @throws Exception
     */
    public Dept get(String deptno)throws Exception;
}

  • git()方法的具体实现

    @Override
    public Dept get(Integer deptno) throws Exception {
        IDeptDAO deptDAO = DAOFactory.getInstance(DeptDAOImpl.class);
        return deptDAO.findById(deptno);
    }
  • 建立一个ILevelServiceBack操作接口
package mao.shu.em.service.back;

import mao.shu.em.vo.Level;

public interface ILevelServiceBack {
    /**
     * 该方法根据一个等级id查询出一个完整的等级信息
     * @param lid 等级id
     * @return 一个完整的工资等级数据
     * @throws Exception
     */
    public Level get(Integer lid)throws Exception;
}

  • ILevelDAO.get(0方法的具体实现
public class LevelServiceImpl extends AbstractService implements ILevelServiceBack {
    @Override
    public Level get(Integer lid) throws Exception {
        ILevelDAO levelDAO = DAOFactory.getInstance(LevelDAOImpl.class);
        return levelDAO.findById(lid);
    }
}

  1. 在EmpServletBack程序类里面追加相应的Ajax处理支持
    • 追加一个部门验证的处理操作
/**
     * 该方法用于验证新增的雇员要添加的部门是否有剩余人数,该方法主要提供给页面进行ajax异步验证处理
     */
    public void checkDept(){
        Integer deptno = super.getIntParameter("emp.deptno");
        IDeptServiceBack deptServiceBack = ServiceFactory.getInstance(DeptServiceBackImpl.class);
        try {
            Dept dept = deptServiceBack.get(deptno);
            if(dept.getCurrnum()<dept.getMaxnum()){
                super.printData("true");
            }else{
                super.printData("false");
            }
        } catch (Exception e) {
            super.printData("false");
            e.printStackTrace();
        }
    }

  • 追加一个工资的异步判断处理
 /**
     * 该方法用户对用户填写的新增雇员的工资是否在指定的工资等级范围之内做一个判断,
     * 该判断用于页面验证的ajax的异步判断
     */
    public void chekSal(){
        Double sal = super.getDoubleParameter("emp.sal");
        Integer lid = super.getIntParameter("emp.lid");
        ILevelServiceBack levelServiceBack = ServiceFactory.getInstance(LevelServiceBackImpl.class);
        try {
            Level level = levelServiceBack.get(lid);
            if(level.getHisal()>= sal && level.getLosal()<= sal){
                super.printData("true");
            }else {
                super.printData("false");
            }
        } catch (Exception e) {
            super.printData("false");
            e.printStackTrace();
        }
    }
  1. 在emp_add.js文件中进行ajax异步验证
			"emp.deptno" : {
				required : true,
				number : true,
				remote : {
					url :  "pages/back/emp/EmpServletBack/checkDept" ,
					type : "post",
					dataType : "text",
					data : {
						deptno : function(){
							return $("#emp\\.deptno").val();
						}
					},
					dataFilter : function(data,type){
						return data.trim() == "true";
					}
				}
			} ,

			"emp.sal" : {
				required : true ,
				number : true,
				remote : {	// 需要进行远程交互验证
					url : "pages/back/emp/EmpServletBack/chekSal" ,
					type : "post" ,
					dataType : "text" ,
					data : {
						sal : function () {
							return $("#emp\\.sal").val() ;
						},
						lid : function () {
							return $("#emp\\.lid").val();
						}
					} ,
					dataFilter : function(data,type) {
						return data.trim()=="true";
					}
				}
			} , 
  • 在Validation.properties文件中添加验证规则
#图片上传验证规则
EmpServletBack.upload.file.rules = image/jpeg,image/bmp,image,image/gif,image/png

# 雇员添加验证规则
EmpServletBack.add.rules = emp.ename:string|emp.deptno:int|emp.job:string|emp.lid:int|emp.sal:double|emp.comm:double|

  • 在整个用户登录之后如果出现错误消息,那么就不应该出现登录界面了
    • 建议修改login.jsp页面,如果已经登录过了,那么就直接跳转到index.jsp页面即可
	String indexUrl = basePath + "login.jsp" ;
	if(session.getAttribute("mid")!= null){
		//方式一直接跳转
		/*pageContext.forward("/pages/index.jsp");*/
		
		//方式二,修改超链接路径,有用户决定是否跳转
		indexUrl = basePath+"pages/index.jsp";
	}
  • 对于验证的操作处理永远是开发中必须考虑的核心所在.

  • 异步验证效果

在这里插入图片描述

在这里插入图片描述

雇员日志处理

  • 在用户增加的时候实际上这里需要有一个雇员入职的考虑问题,之所i去设置这样的日志记录,主要针对于雇员的工资进行进一步的详情的记录.

  • 在elog表中有一个sflag的字段,这个字段主要是进行工资的涨幅的匹配,现在对于此字段的取值定义有三个

    1. sflag=0:表示该条日志对应的是雇员刚入职的信息状况
    2. sflag=1: 表示该条日志对应的是雇员工资增加的状况
    3. sflag=2:表示该条日志对应的是雇员工资降低的状况
  1. 定义Elog的雇员vo类
package mao.shu.em.vo;

public class Elog {
    private Integer elid;//elid为日志id,该字段为自动增长
    private Integer empno;
    private Integer deptno;
    private String mid;
    private Integer lid;
    private String job;
    private Double sal;
    private Double comm;
    private Integer sflag;
    private Integer falg;
    private String note;

	//getter和setter方法;
}
  • 定义IElogDAO接口,该接口暂时不需要扩充任何方法
package mao.shu.em.dao;

import mao.shu.em.vo.Elog;
import mao.shu.util.IDAO;

public interface IElogDAO extends IDAO<Integer, Elog> {
    
}

  • 完善ElogDAOImpl子类的doCreate()方法,操作方法
public class ElogDAOImpl extends AbstractDAO implements IElogDAO {
    @Override
    public boolean doCreate(Elog vo) throws SQLException {
        String sql = "INSERT INTO elog(empno,deptno,mid,lid,job,sal,comm,sflg,flag,note) VALUES(?,?,?,?,?,?,?,?,?,?)";
        super.pstmt = super.conn.prepareStatement(sql);
        super.pstmt.setInt(1,vo.getEmpno());
        super.pstmt.setInt(2,vo.getDeptno());
        super.pstmt.setString(3,vo.getMid());
        super.pstmt.setInt(4,vo.getLid());
        super.pstmt.setString(5,vo.getJob());
        super.pstmt.setDouble(6,vo.getSal());
        super.pstmt.setDouble(7,vo.getComm());
        super.pstmt.setInt(8,vo.getSflag());
        super.pstmt.setInt(9,vo.getFalg());
        super.pstmt.setString(10,vo.getNote());
        return super.pstmt.executeUpdate() > 0;
    }
}
  • 如果要增加雇员的日志信息,那么应该在新雇员被增加的时候就添加日志信息.而要添加日志信息,需要取得每条日志的id,再添加日志信息中有一个empno 属性,该属性描述的是添加雇员的id,但是雇员的id室友数据库中的自动生长列完成的
  • 所以应该添加一个获取最后一个自动增加的id方法,考虑到许多DAO操作类都可能会使用到该方法,所以建议将该方法定义在父类IDAO中.
  • 在IDAO接口路面扩充一个新的功能 ,取得当前增长后的编号.
	/**
	 * 取得自动增加列的最后一个添加的id信息,MySQL数据库使用的是 LASG_INSERT_ID() 函数取得
	 * @return
	 * @throws SQLException
	 */
	public Integer getLastId()throws SQLException;
  • 但是有些DAO操作类可能不需要复写这个方法,因此可以在AbstractDAO这个类中复写该方法即可,这样其他子类就不需要必须复写该方法了.
  • 在AbstractDAO类里面复写这个方法
	public Integer getLastId()throws SQLException{
		String sql  = "SELECT LAST_INSERT_ID()";
		this.pstmt = this.conn.prepareStatement(sql);
		ResultSet rs = this.pstmt.executeQuery();
		if(rs.next()){
			return rs.getInt(1);
		}else{
			return null;
		}
		
	}
  • 对于日志之中的数据记录的问题,是在表单处理的,所以需要在控制层里面实例化Elog对象,主要的目的是接收传过来的note参数内容,同时这个对象要传到业务层中
    • 修改IEmpServiceBack接口,对于入职的方法里面需要增加一个新的处理参数:Elog
    public boolean add(Emp vo, Elog elog)throws Exception;
  • 在EmpServiletBack类中的add()方法里面,进行ELog类对象的实例化操作
Elog elog = new Elog();
 elog.setNote(super.getStringParameter("note"));
 if(empServiceBack.add(this.emp,elog)){
...

}
  • 在业务层实现子类:EmpServiceBackImpl子类里面,还需要将Emp对象中的内容设置到elog对象之中
  • 在添加Note属性的时候,为了日志看起来更加的具有可读性,所以建议定义一个工具类,该工具类进行将当前时间转换为字符串.
  • 而在添加Note属性的时候,使用该工具类,添加日期时间信息在note属性中
  • 定义DateUtil.java程序类
package mao.shu.util;

import java.util.Date;
import java.text.SimpleDateFormat;

/**
 * 该类进行时间与字符串的转换,将时间转换为这样的格式: yyyy-MM-dd HH:mm:ss
 */
public class DateUtil {
    private String pattern = "yyyy-MM-dd HH:mm:ss";
    public String getFormatDateTime(Date date){
        return new SimpleDateFormat(pattern).format(date);
    }

    public String getFormatDateTime(){
        return new SimpleDateFormat(pattern).format(new Date());
    }
}

  • 完善EmpServiceBackImpl 中的add()方法
   public boolean add(Emp vo, Elog elog) throws Exception {
        //判断操作用户的权限是否具备添加用户的权限
        if(super.auth(vo.getMid(),"emp:add")){
            IDeptDAO deptDAO = DAOFactory.getInstance(DeptDAOImpl.class);
           Dept dept = deptDAO.findById(vo.getDeptno());
           //判断当前添加的部门人数是否已满
            if(dept.getCurrnum()<dept.getMaxnum()){
                ILevelDAO levelDAO = DAOFactory.getInstance(LevelDAOImpl.class);
                Level level =  levelDAO.findById(vo.getLid());
                //判断新增雇员的工资是否在对应的工资等级范围中
                if(vo.getSal()>=level.getLosal()&&vo.getSal()<=level.getHisal()){
                    vo.setFlag(1);
                    vo.setHiredate(new Date());
                    //执行添加雇员操作
                    IEmpDAO empDAO = DAOFactory.getInstance(EmpDAOImpl.class);
                    Integer empno = empDAO.getLastId();
                    if(empDAO.doCreate(vo)){
						Integer empno = emoDao.getLastId();
                        //如果添加雇员成功,则将对应部门的人数加1
                        if( deptDAO.updateCurrnum(vo.getDeptno(),1)){
                            elog.setEmpno(empno );
                            elog.setDeptno(vo.getDeptno());
                            elog.setMid(vo.getMid());
                            elog.setLid(vo.getLid());
                            elog.setJob(vo.getJob());
                            elog.setSal(vo.getSal());
                            elog.setComm(vo.getComm());
                            elog.setSflag(0);//0表示刚入职,1表示涨工资,2表示减工资
                            elog.setFalg(1);
                            elog.setNote("["+ DateUtil.getFormatDateTime() +"]"+elog.getNote());
                            IElogDAO elogDAO = DAOFactory.getInstance(ElogDAOImpl.class);
                            return elogDAO.doCreate(elog);
                        }
                    }
                }
            }

        }
        return false;
    }
  • 这个日志在每一次的增加,修改删除的过程之中,都存在的信息
  • 测试数据

在这里插入图片描述

  • 数据库添加的数据

在这里插入图片描述

雇员列表

  • 对于雇员的列表主要分为两种列表:离职列表,在职列表
  1. 在IEmpDAO中扩充四个分页方法
package mao.shu.em.dao;

import mao.shu.em.vo.Emp;
import mao.shu.util.IDAO;

import java.sql.SQLException;
import java.util.List;

public interface IEmpDAO extends IDAO<Integer, Emp> {
    /**
     * 根据emp表中的flag字段进行分页查询
     * @param flag
     * @param currentPage
     * @param lineSize
     * @param column
     * @param keyword
     * @return
     * @throws SQLException
     */
    public List<Emp> splitAllByFlag(Integer flag,Integer currentPage,Integer lineSize,String column,String keyword)throws SQLException;

    public List<Emp> splitAllByFlag(Integer flag,Integer currentPage,Integer lineSize)throws SQLException;

    /**
     * 更具flag字段同级雇员人数
     * @param flag
     * @param cloumn
     * @param keyword
     * @return
     * @throws SQLException
     */
    public Integer getAllCountByFlag(String flag,String cloumn,String keyword)throws SQLException;
    public Integer getAllCountByFlag(String flag)throws SQLException;
}

  • 在EmpDAOImpl子类中实现四个方法
   @Override
    public List<Emp> splitAllByFlag(Integer flag, Integer currentPage, Integer lineSize, String column, String keyword) throws SQLException {
       String sql = "SELECT empno,deptno,mid,lid,ename,job,sal,comm,hiredate,photo,flag FROM emp WHERE " +
                " "+column+" LIKE ? AND flag=? LIMIT ?,?";
       super.pstmt = super.conn.prepareStatement(sql);
       super.pstmt.setString(1,"%"+keyword+"%");
       super.pstmt.setInt(2,flag);
        ResultSet resultSet = super.pstmt.executeQuery();
        List<Emp> allEmps = new ArrayList<Emp>();
        while(resultSet.next()){
            Emp emp = new Emp();
            emp.setEmpno(resultSet.getInt(1));
            emp.setDeptno(resultSet.getInt(2));
            emp.setMid(resultSet.getString(3));
            emp.setLid(resultSet.getInt(4));
            emp.setEname(resultSet.getString(5));
            emp.setJob(resultSet.getString(6));
            emp.setSal(resultSet.getDouble(7));
            emp.setComm(resultSet.getDouble(8));
            emp.setHiredate(resultSet.getDate(9));
            emp.setPhoto(resultSet.getString(10));
            emp.setFlag(resultSet.getInt(11));
            allEmps.add(emp);
        }
        return allEmps;
    }

    @Override
    public List<Emp> splitAllByFlag(Integer flag, Integer currentPage, Integer lineSize) throws SQLException {
        String sql = "SELECT empno,deptno,mid,lid,ename,job,sal,comm,hiredate,photo,flag FROM emp WHERE " +
                " flag=? LIMIT ?,?";
        super.pstmt = super.conn.prepareStatement(sql);
        super.pstmt.setInt(1,flag);
        super.pstmt.setInt(2,(currentPage-1)*lineSize);
        super.pstmt.setInt(3,lineSize);
        ResultSet resultSet = super.pstmt.executeQuery();
        List<Emp> allEmps = new ArrayList<Emp>();
        while(resultSet.next()){
            Emp emp = new Emp();
            emp.setEmpno(resultSet.getInt(1));
            emp.setDeptno(resultSet.getInt(2));
            emp.setMid(resultSet.getString(3));
            emp.setLid(resultSet.getInt(4));
            emp.setEname(resultSet.getString(5));
            emp.setJob(resultSet.getString(6));
            emp.setSal(resultSet.getDouble(7));
            emp.setComm(resultSet.getDouble(8));
            emp.setHiredate(resultSet.getDate(9));
            emp.setPhoto(resultSet.getString(10));
            emp.setFlag(resultSet.getInt(11));
            allEmps.add(emp);
        }
        return allEmps;
    }

    @Override
    public Integer getAllCountByFlag(Integer flag, String cloumn, String keyword) throws SQLException {
        String sql = "SELECT COUNT(*) FROM emp WHERE flag=? AND "+cloumn+" LIKE ?";
        super.pstmt = super.conn.prepareStatement(sql);
        super.pstmt.setInt(1,flag);
        super.pstmt.setString(2,"%"+keyword+"%");
        ResultSet resultSet = super.pstmt.executeQuery();
        if(resultSet.next()){
            return resultSet.getInt(1);
        }else{
            return null;
        }
    }

    @Override
    public Integer getAllCountByFlag(Integer flag) throws SQLException {
        String sql = "SELECT COUNT(*) FROM emp WHERE flag=?";
        super.pstmt = super.conn.prepareStatement(sql);
        super.pstmt.setInt(1,flag);
        ResultSet resultSet = super.pstmt.executeQuery();
        if(resultSet.next()){
            return resultSet.getInt(1);
        }else{
            return null;
        }
    }
  • 在IEmpServiceBack接口中追加列表的处理方法,需要考虑到权限认证问题.
 /**
     * 该方法进行雇员emp表的分页操作,需要进行以下操作
     * 1. 进行权限验证,判断操作用户是否有 emp:list 权限
     * @param flag 用户在职情况,flag=1 表示列出所有在职的人员,flag=0 表示列出所有离职人员
     * @param currentPage 当前所在页
     * @param lineSize 每页显示个数
     * @param column 模糊查询列
     * @param keyword 模糊查询关键字 如果没有column和keyword 表示默认查询所有数据
     * @return 返回以下的内容
     *  1. allEmps = 所有雇员集合
     *  2. allCount = 所有雇员人数
     * @throws Exception
     */
    public Map<String,Object> list(Integer flag,Integer currentPage,Integer lineSize,String column,String keyword)throws Exception;

  • list()方法的具体实现
    @Override
    public Map<String, Object> listByFlag(String mid,Integer flag, Integer currentPage, Integer lineSize, String column, String keyword) throws Exception {
        if(super.auth(mid,"emp:list")) {
            IEmpDAO empdao = DAOFactory.getInstance(EmpDAOImpl.class);
            Map<String,Object> map = new HashMap<String,Object>();
            if (column == null || keyword == null || "".equals(column) || "".equals(keyword)) {
                map.put("allEmps",empdao.splitAllByFlag(flag,currentPage,lineSize));
                map.put("allCount",empdao.getAllCountByFlag(flag));
            } else {
                map.put("allEmps",empdao.splitAllByFlag(flag,currentPage,lineSize,column,keyword));
                map.put("allCount",empdao.getAllCountByFlag(flag,column,keyword));
            }
            return map;
        }
        return null;
    }
  • 在EmpServletBack中追加分页处理方法:

  • 在Pages.properties文件中添加文件跳转路径

emp.list.page=/pages/back/emp/emp_list.jsp
emp.list.servlet=pages/back/emp/EmpServletBack/list
  • 在使用Servlet完成分页操作的时候,往往需要分页的几个基本数值,为了更方便的代码利用,决定将取得分页参数的操作定义为一个工具类—SplitUtil.java

    • 该类负责完成接受分页所需的 lineSize,currentPage,keyword,column 四个基本分页参数
  • SplitUtil.java程序类

package mao.shu.util.split;

import javax.servlet.http.HttpServletRequest;

public class SplitPageUtils {
	private HttpServletRequest request ;
	public SplitPageUtils(HttpServletRequest request) {
		this.request = request ;
	}
	public Integer getCurrentPage() {
		int currentPage = 1 ;
		try {
			currentPage = Integer.parseInt(this.request.getParameter("cp")) ;
		} catch (Exception e) {}
		return currentPage ;
	}
	public Integer getLineSize() {
		int lineSize = 5 ; 
		try {
			lineSize = Integer.parseInt(this.request.getParameter("ls")) ;
		} catch (Exception e) {}
		return lineSize ;
	}
	public String getColumn() {
		String column = this.request.getParameter("col") ;
		return column ;
	}
	public String getKeyWord() {
		String keyWord = this.request.getParameter("kw") ;
		return keyWord ; 
	}
}

  • 完善ist()的列表方法
   public String list(){
        String urlkey = "emp.split.servlet";//保存执行分页servlet的地址
        //判断用户查询的是在职雇员还是离职雇员的标记
        Integer flag = super.getIntParameter("flag");
        //进行分页参数的获取
        SplitPageUtils splitPageUtils = new SplitPageUtils(super.request);
        Integer currentPage = splitPageUtils.getCurrentPage();
        Integer linesize = splitPageUtils.getLineSize();
        String keyword = splitPageUtils.getKeyWord();
        String column = splitPageUtils.getColumn();
        IEmpServiceBack empServiceBack = ServiceFactory.getInstance(EmpServiceBackImpl.class);
        try {
            Map<String,Object> splitResult = empServiceBack.listByFlag(super.getMid(),flag,currentPage,linesize,column,keyword);
            //将分页的数据传输到JSP中
           request.setAttribute("allEmps",splitResult.get("allEmps"));
           //分页组件传递分页所需的参数,实现切换页码
           super.setSplitPage(urlkey,(Integer) splitResult.get("allCount"),splitPageUtils);
           super.setSplitParam("flag",flag);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "emp.list.page";
    }
  • 考虑到要区分在职人员分页和离职人员分页,所以在进行分页操作的时候,也将"flag"值设置到request属性中,为的是能够区分在职和离职人员的分页显示

  • 在DispatcherServlet中添加一个方法:–setSplitParam()该方法用于设置其他分页所需参数


	/**
	 * 设置分页所需的其他参数,
	 * @param key
	 * @param value
	 */
	public void setSplitParam(String key,Object value){
		this.request.setAttribute("parameName",key);
		this.request.setAttribute("parameValue",value);
	}
  • 在进行前端页面数据显示的时候使用jstl进行迭代循环,而分页控制有组件完成
  1. 修改split_plugin_item.jsp页面中雇员列表的链接地址,他执行的是EmpServletBack下的list()方法,提交一个falg参数=1,表示查询的是在职人员列表
<li class="${param.action=='emp:list' ? 'active' : ''}">
	<a href="pages/back/emp/EmpServletBack/list?flag=1">
		<i class="fa fa-circle-o">
		</i> 雇员列表
	</a>
</li>
  • 修改分页组件插件中include_splitpage_bar.jsp页面中对"keyword"属性和"column"属性接收的修改,为了防止这两个属性出现"null"的情况,使用三目运算符解决,当出现"null"的情况
	column = (String) request.getAttribute("column") ;
	column = column==null?"":column;
	keyWord = (String) request.getAttribute("keyWord") ;
	keyWord = keyWord==null?"":keyWord;

在这里插入图片描述

  • 修改分页组件插件中include_splitpage_bar.jsp中的分页链接路径地址,
    • 其中添加的${parameName}=${parameValue} 为在EmpServletBack中添加的"flag"属性
<a href="<%=url%>?cp=1&ls=<%=lineSize%>&kw=<%=keyWord%>&col=<%=column%>&${parameName}=${parameValue}">
  • 修改分页插件include_splitPage_search.jsp页面
    • 在搜索表单提交的时候,将"flag"属性页传入到Servlet中,继续向下传递
    • 也可以使用隐藏标签的方式传输
<form action="<%=url%>?${parameName}=${parameValue}" method="post" class="form-horizontal">
  • 隐藏标签的方式
<input type="hidden" value="${paramValue}" name="${paramName}">
  • 完整分页效果

在这里插入图片描述

详情列表显示

  • 现在已经实现了基本的列表显示操作,在列表上还应该将对应的部门名称和级别显示出来.
  • 所以这个时候如果要实现这一操作,就必须对程序进行业务层的处理
  1. 在EmpServiceBackImpl子类里面进行部门和级别数据的列表
    @Override
    public Map<String, Object> listByFlag(String mid,Integer flag, Integer currentPage, Integer lineSize, String column, String keyword) throws Exception {
        if(super.auth(mid,"emp:list")) {
            IEmpDAO empdao = DAOFactory.getInstance(EmpDAOImpl.class);
            IDeptDAO deptDAO = DAOFactory.getInstance(DeptDAOImpl.class);
            ILevelDAO levelDAO= DAOFactory.getInstance(LevelDAOImpl.class);
            Map<String,Object> map = new HashMap<String,Object>();
            if (column == null || keyword == null || "".equals(column) || "".equals(keyword)) {
                map.put("allEmps",empdao.splitAllByFlag(flag,currentPage,lineSize));
                map.put("allCount",empdao.getAllCountByFlag(flag));
            } else {
                map.put("allEmps",empdao.splitAllByFlag(flag,currentPage,lineSize,column,keyword));
                map.put("allCount",empdao.getAllCountByFlag(flag,column,keyword));
            }

            //得出所有部门的信息和工资等级的信息
            List<Dept> deptsList = deptDAO.findAll();
            List<Level> levelList = levelDAO.findAll();

            //因为El表达式中无法识别int类型,所以使用String代替
            //保存部门编号和部门名称
            Map<String,String> deptno_dname = new HashMap<String,String>();
            //保存等级编号和等级名称
            Map<String,String> lid_title_flag = new HashMap<String,String>();
            Iterator<Dept> deptIterator = deptsList.iterator();
            while(deptIterator.hasNext()){
                Dept temp = deptIterator.next();
                deptno_dname.put(temp.getDeptno().toString(),temp.getDname());
            }
            
            Iterator<Level> levelIterator = levelList.iterator();
            while(levelIterator.hasNext()){
                Level temp = levelIterator.next();
                lid_title_flag.put(temp.getLid().toString(),temp.getTitle()+"-"+temp.getFlag());
            }
            
            //将部门和等级信息保存到 map集合中
            map.put("deptno_dname",deptno_dname);
            map.put("lid_title_flag",lid_title_flag);
            return map;
        }
        return null;
    }
  1. 在EmpServletBack类中,将部门信息和等级信息传入到页面之中
  request.setAttribute("deptno_dname",splitResult.get("deptno_dname"));
  request.setAttribute("lid_title_flag",splitResult.get("lid_title_flag"));
  1. 在emp_list.jsp页面之中使用EL表达式进行map的查询
<td class="text-center">${lid_title_flag[''+emp.lid]}</td>
<td class="text-center">${deptno_dname[''+emp.deptno]}</td>
  • 最终表现效果

在这里插入图片描述

雇员编辑

  • 雇员编辑最麻烦的问题在于表单的回填处理

  • 修改EmpDAOImpl中的findById()方法

    @Override
    public Emp findById(Integer id) throws SQLException {
        String sql = "SELECT empno,deptno,mid,lid,job,sal,comm,hiredate,photo,ename FROM emp WHERE empno=?";
        super.pstmt = super.conn.prepareStatement(sql);
        super.pstmt.setInt(1,id);
        ResultSet resultSet = super.pstmt.executeQuery();
        if(resultSet.next()){
            Emp vo = new Emp();
            vo.setEmpno(resultSet.getInt(1));
            vo.setDeptno(resultSet.getInt(2));
            vo.setMid(resultSet.getString(3));
            vo.setLid(resultSet.getInt(4));
            vo.setJob(resultSet.getString(5));
            vo.setSal(resultSet.getDouble(6));
            vo.setComm(resultSet.getDouble(7));
            vo.setHiredate(resultSet.getDate(8));
            vo.setPhoto(resultSet.getString(9));
            vo.setEname(resultSet.getString(10));
            return vo;
        }
        return null;
    }
  • 完善doUpdate()方法
    @Override
    public boolean doUpdate(Emp vo) throws SQLException {
        String sql = " UPDATE emp SET deptno=?,lid=?,ename=?,job=?,sal=?,comm=?,photo=? WHERE empno=? ";
        super.pstmt = super.conn.prepareStatement(sql);
        super.pstmt.setInt(1,vo.getDeptno());
        super.pstmt.setInt(2,vo.getLid());

       super.pstmt.setString(3,vo.getEname());
       super.pstmt.setString(4,vo.getJob());
       super.pstmt.setDouble(5,vo.getSal());
       super.pstmt.setDouble(6,vo.getComm());
       super.pstmt.setString(7,vo.getPhoto());
       super.pstmt.setInt(8,vo.getEmpno());
        return super.pstmt.executeUpdate() > 0;
    }
  1. 在IEmpServiceBack中追加两个新的处理方法
    • editPre:操作编辑用户所需要的数据,
    • edit
    /**
     * 该方法用于做雇员编辑操作的准备工作
     * @param mid 操作雇员的mid
     * @param empno 修改雇员的雇员编号
     * @return 返回一个mao集合,该集合中有雇员编辑页面中所需要的数据,返回的结果会有以下几种数值
     * 1. key= allDepts value=所有的部门集合
     * 2. key= allLevels value=所有的等级集合
     * 3. key = emp     value=新修改的雇员信息
     * @throws Exception
     */
    public Map<String,Object> editPre(String mid,Integer empno)throws Exception;


    /**
     * 该方法进行雇员数据修改的操作,修改的同时需要添加一条日志信息
     * @param editEmp 修改的雇员信息
     * @param elog 日志信息
     * @return 修改成功返回true, 失败返回false
     * @throws Exception
     */
    public boolean edit(Emp editEmp,Elog elog)throws Exception;

  1. 这两个方法的具体实现
    public String editPre()throws Exception{
        //从请求中取得当前的雇员编号
        Integer empno = super.getIntParameter("empno");
        //判断当前用户的权限是否有编辑用户的权限
        if(super.auth("emp:edit")){
            IEmpServiceBack empServiceBack = ServiceFactory.getInstance(EmpServiceBackImpl.class);
            Map<String,Object> map = empServiceBack.editPre(super.getMid(),empno);
            super.setSessionAttribute("allDepts",map.get("allDepts"));
            super.setSessionAttribute("allLevels",map.get("allLevels"));
            super.setSessionAttribute("emp",map.get("emp"));
            return "emp.edit.page";
        }else{
            super.setErrors("auth","auth.failure.msg");
            return "error.page";
        }
    }

 	public String edit(){

        //判断操作用户的权限是否有"emp:edit",修改用户的权限
        try {
            if(super.auth("emp:edit")){
                //判断用户是否有上传文件
                if(super.isUploadFile()){
                    //因为默认的图片使用的名称为"notphoto.png",如果雇员之前使用的是默认的图片,则应该新建一个新的图片名称
                    if("nophoto.png".equals(this.emp.getPhoto())){
                        this.emp.setPhoto(super.createSingleFileName());
                    }
                }

                //修改操作员的编号
                this.emp.setMid(super.getMid());
                //保存日志信息
                Elog elog = new Elog();
                //在日志中保存雇员简介
                elog.setNote(super.getStringParameter("note"));

                //得到empserviceBack 业务层的操作接口对象
                IEmpServiceBack empServiceBack = ServiceFactory.getInstance(EmpServiceBackImpl.class);

                //修改雇员数据和保存日志数据
                if(empServiceBack.edit(this.emp,elog)){
                    super.setUrlAndMsg("emp.list.servlet","vo.edit.success.msg");
                    //保存新的图片和缩略图
                    if(super.isUploadFile()){
                        super.saveUploadFile(this.emp.getPhoto());
                        super.saveScale(this.emp.getPhoto());
                    }
                }else{
                    super.setUrlAndMsg("emp.list.servlet","vo.edit.failure.msg");
                }
                return "forward.page";
            }else{
                super.setErrors("auth","auth.failure.msg");
                return "error.page";
            }
        } catch (Exception e) {
            super.setUrlAndMsg("emp.list.page","vo.edit.failure.msg");
            e.printStackTrace();
            return "error.page";
        }
    }

  1. 在EmpServletBack中更新处理
 public String edit(){
        //判断操作用户的权限是否有"emp:edit",修改用户的权限
        try {
            if(super.auth("emp:edit")){
                //判断用户是否有上传文件
                if(super.isUploadFile()){
                    //因为默认的图片使用的名称为"notphoto.png",如果雇员之前使用的是默认的图片,则应该新建一个新的图片名称
                    if("notphoto.png".equals(this.emp.getPhoto())){
                        this.emp.setPhoto(super.createSingleFileName());
                    }
                }

                //修改操作员的编号
                this.emp.setMid(super.getMid());
                //保存日志信息
                Elog elog = new Elog();
                //在日志中保存雇员简介
                elog.setNote(super.getStringParameter("note"));

                //得到empserviceBack 业务层的操作接口对象
                IEmpServiceBack empServiceBack = ServiceFactory.getInstance(EmpServiceBackImpl.class);

                //修改雇员数据和保存日志数据
                if(empServiceBack.edit(this.emp,elog)){
                    super.setUrlAndMsg("emp.list.servlet","vo.edit.success.msg");
                    //保存新的图片和缩略图
                    if(super.isUploadFile()){
                        super.saveUploadFile(this.emp.getPhoto());
                        super.saveScale(this.emp.getPhoto());
                    }
                }else{
                    super.setUrlAndMsg("emp.list.servlet","vo.edit.failure.msg");
                }
                return "forward.page";
            }else{
                super.setErrors("auth","auth.failure.msg");
                return "error.page";
            }
        } catch (Exception e) {
            super.setUrlAndMsg("emp.list.page","vo.edit.failure.msg");
            e.printStackTrace();
            return "error.page";
        }
    }

  • 修改emp_list.jsp页面中的编辑雇员的链接路径
String editEmpUrl = basePath + "pages/back/emp/EmpServletBack/editPre" ;
  1. 在emp_edit.jsp页面上进行数据回填处理,但是需要注意一个问题,原始的图片需要通过隐藏域传递
  • 需要将当前雇员所在的部门编号也是用隐藏域的方式存储,为了让js的Ajax异步验证的时候可以判断
<!-- 雇员姓名回填-->
<input type="text" name="emp.name" id="emp.name" class="form-control input-sm" placeholder="请输入雇员真实姓名" value="${emp.ename}">

<!-- 雇员部门回填-->
<c:forEach items="${allDepts}" var="dept">
<option value="${dept.deptno}" ${emp.deptno==dept.deptno?"selected":""}>${dept.dname}(空余名额:${dept.maxnum-dept.currnum})</option>
</c:forEach>

<!-- 雇员职位回填-->
<input type="text" name="emp.job" id="emp.job" class="form-control input-sm" placeholder="请输入雇员职位" value="${emp.job}">


<!-- 雇员工资等级回填-->
<c:forEach items="${allLevel}" var="level">
<option value="${level.lid}" ${emp.lid==level.lid?"selected":""}>${level.title}-${level.flag}(工资范围:${level.hisal}-${level.losal})</option>
</c:forEach>

<!-- 雇员基本工资回填-->

<input type="text" name="emp.sal" id="emp.sal" class="form-control input-sm" placeholder="请输入雇员基本工资,工资要与员工等级匹配" value="${emp.sal}">

<!-- 工资佣金回填-->
<input type="text" name="emp.comm" id="emp.comm" class="form-control input-sm" placeholder="如果为销售人员,设置每月佣金" value="${emp.comm}">



  • 随后在表单的隐藏域里面存放此内容
<!-- 三个隐藏数据-->
<input type="hidden" name="emp.empno" value="${emp.empno}" class="btn btn btn-primary">
<input type="hidden" name="emp.photo" value="${emp.photo}">
<input type="hidden" id="currdeptno" value="${emp.deptno}">
  1. 修改部门的检测处理,在检测的时候应该刨除当前部门的检测处理
    • 直接在控制层上进行一些逻辑处理
  • 修改emp_edit.js 文件,在进行部门的异步验证的时候,将当前用户的部门编号也发送到验证方法中
"emp.deptno" : {
	required : true,
	remote : {
		url : "pages/back/emp/EmpServletBack/checkDept",
		type : "post",
		dataType : "text",
		data : {
			deptno : function(){
				return $("#emp\\.deptno").val();
			},
			currDeptno : function(){
				return $("#currdeptno").val();
			}
		},
		dataFilter : function(data,type){
			return data.trim()=="true";
		}

	}
} ,
  • 修改EmpServletBack/checkDept 验证部门信息的方法
   /**
     * 该方法用于验证新增的雇员要添加的部门是否有剩余人数,该方法主要提供给页面进行ajax异步验证处理
     */
    public void checkDept(){
        Integer deptno = super.getIntParameter("deptno");
        Integer currDeptno = super.getIntParameter("currDeptno");
        //判断当前雇员编号和原雇员编号是否一样
        if(deptno == currDeptno){
            super.printData("true");
            return;
        }
        IDeptServiceBack deptServiceBack = ServiceFactory.getInstance(DeptServiceBackImpl.class);
        try {
            Dept dept = deptServiceBack.get(deptno);
           super.printData(dept.getCurrnum()<dept.getMaxnum());

        } catch (Exception e) {
            super.printData("false");
            e.printStackTrace();
        }
    }
  • 整体页面效果

在这里插入图片描述

在这里插入图片描述

雇员离职

  • 如果要进行雇员离职处理,只需要修改一个标志位即可,但是离职的时候需要考虑离职的日志处理
  • 在emp_list.jap页面中,离职按钮式所提交的参数内容形式为"ids=id|id…"
  1. 在IEmpDAO中添加doUpdateByFlag()方法,项目中没有真正的删除操作,只是将雇员的flag修改flag的状态,flag=1表示在职.flag=0表示离职

    /**
     * 进行多个雇员在职状态的更新操作
     * @param empnos 雇员的编号集合
     * @param flag 在职的状态,flag=1 表示在职,flag=2 表示离职
     * @return 如果所有的雇员状态更新成功返回true,否则返回false
     * @throws SQLException
     */
    public boolean doUpdateByFlag(Set<Integer> empnos,Integer flag)throws SQLException;
  1. 在EmpDAOImpl中实现该方法
    @Override
    public boolean doUpdateByFlag(Set<Integer> empnos, Integer flag) throws SQLException {
        String sql = "UPDATE emp set flag=? WHERE empno=?";
        super.pstmt = super.conn.prepareStatement(sql);
        Iterator<Integer> idsIte = empnos.iterator();
        while(idsIte.hasNext()) {
            super.pstmt.setInt(1, flag);
            super.pstmt.setInt(2, idsIte.next());
            super.pstmt.addBatch();
        }
        int[] result = super.pstmt.executeBatch();
        for (int i = 0; i < result.length; i++) {
            if(result[i]<1){
                return false;
            }
        }
        return true;
    }
  1. 在IEmpServiceBack接添加一个批量删除的操作.
    /**
     * 批量进行多个雇员的离职处理,将雇员的flag值改为 0,同时添加一个雇员离职的日志信息,和修改离职雇员的所在部门的当前人数
     * @param mid 操作离职处理的用户id
     * @param empnos 多个离职的雇员编号集合
     * @return 成功返回true,否则返回false
     * @throws Exception
     */
 public boolean removeEmp(String mid, Set<Integer> empnos)throws Exception;
  • 在EmpServiceBackImpl中实现该方法

    public boolean removeEmp(String mid, Set<Integer> empnos){
        try {
            if(super.auth(mid,"emp:remove")){
                IEmpDAO empDAO = DAOFactory.getInstance(EmpDAOImpl.class);
                if(empDAO.doUpdateByFlag(empnos,0)){
                    Iterator<Integer> empnosIte = empnos.iterator();
                    IElogDAO  elogDAO = DAOFactory.getInstance(ElogDAOImpl.class);
                    IDeptDAO deptDAO = DAOFactory.getInstance(DeptDAOImpl.class);
                    while(empnosIte.hasNext()){
                        Integer empno = empnosIte.next();
                        Emp emp = empDAO.findById(empno);//得到每个雇员信息
                        Integer deptno = emp.getDeptno();//得到每个雇员所在的部门编号
                        //保存日志信息
                        Elog elog = new Elog();
                        elog.setEmpno(empno);
                        elog.setNote(DateUtil.getFormatDateTime()+"[雇员离职]");
                        elog.setDeptno(deptno);
                        elog.setMid(mid);
                        elog.setFlag(0);//设为离职处理
                        elog.setLid(emp.getLid());
                        elog.setJob(emp.getJob());
                        elog.setSal(emp.getSal());
                        elog.setComm(emp.getComm());
                        //保存日志信息
                        if(elogDAO.doCreate(elog)){
                            //将雇员所在的部门当前人数-1
                            if(!deptDAO.updateCurrnum(deptno,-1)){
                                return false;
                            }
                        }
                    }
                    return true;
                }
            }else{

            }
        } catch (Exception e) {

            e.printStackTrace();
        }
        return false;
    }
  1. 在EmpServletBack中添加删除操作控制
  public String remove(){
        try {
            if(super.auth("emp:remove")){
                //从请求中获取到离职雇员的编号
               String ids = super.request.getParameter("ids");
               String[] temp = ids.split("-");//按照"-"拆分
               Set<Integer> empnos = new HashSet<Integer>();
                for (int i = 0; i < temp.length; i++) {
                    empnos.add(Integer.parseInt(temp[i]));
                }
                IEmpServiceBack empServiceBack = ServiceFactory.getInstance(EmpServiceBackImpl.class);
                 if(empServiceBack.removeEmp(super.getMid(),empnos)){
                     super.setUrlAndMsg("emp.list.page","emp.out.success.msg");
                 }else{
                     super.setUrlAndMsg("emp.list.page","emp.out.failure.msg");
                 }
                return "forward.page";
            }
        } catch (Exception e) {
            super.setErrors("auth","auth.failure.msg");
            e.printStackTrace();
        }
        return "error.page";
    }
  • 在Message.properties文件中添加离职信息
emp.out.success.msg=雇员离职成功
emp.out.failure.msg=雇员离职失败
  1. 在emp_list.js中添加离职处理路径
	$("#outBtn").on("click",function(){
		operateChecked("empno","pages/back/emp/EmpServletBack/remove") ;
	}) ;
  1. 在雇员离职之后需要针对于部门人数做一个修改:
    • 在IDeptDAO接口里面扩充一个新的修改方法,根据雇员编号修改,雇员所在的部门的当前人数
    • 也可以在业务层中,使用EmpDAO中的findById()的方法查询出雇员所在部门编号,在使用DeptDAO中已有的updateCurrentNum方法进行部门人数的更新
    /**
     * 根据指定雇员的编号对该雇员所在的部门的当前人数进行更新
     * @param empno 雇员的编号
     * @param deptno 更新的部门编号
     * @param updateNum  更新的数量,该参数允许为负数,如果是负数,则更新为减少部门人数
     * @return 更新的结果,成功返回true,失败返回false
     * @throws SQLException
     */
    public boolean updateCurrnumByEmpno(Integer empno,Integer deptno,Integer updateNum)throws SQLException;

  • 该方法的具体实现
    @Override
    public boolean updateCurrnumByEmpno(Integer empno, Integer deptno, Integer updateNum) throws SQLException {
        String sql = " UPDATE dept SET currnum=currnum+"+updateNum+" WHERE deptno=(" +
                " SELECT deptno FROM emp WHERE empno=? AND flag=1) ";
        super.pstmt = super.conn.prepareStatement(sql);
        super.pstmt.setInt(1,empno);
        return super.pstmt.executeUpdate() > 0;
    }
  • 实现效果

在这里插入图片描述

在这里插入图片描述

  • 数据库的数据变化

  • emp表,雇员的flag字段成功变为0
    在这里插入图片描述

  • elog表,日志表中记录离职人员

在这里插入图片描述

  • 添加离职雇员列表页面
  • 修改include_menu_item.jsp 页面添加离职雇员列表链接
<li class="${param.action=='emp:list' ? 'active' : ''}">
	<a href="pages/back/emp/EmpServletBack/list?flag=0">
<i class="fa fa-circle-o"></i> 离职雇员列表</a></li>
  • 编写emp_list_out.jsp页面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ include file="/pages/plugins/include_static_head.jsp"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">

<jsp:include page="/pages/plugins/include_javascript_head.jsp" />
</head>
<body class="hold-transition skin-blue sidebar-mini">
	<div class="wrapper">
		<!-- 导入头部标题栏内容 -->
		<jsp:include page="/pages/plugins/include_title_head.jsp" />
		<!-- 导入左边菜单项 -->
		<jsp:include page="/pages/plugins/include_menu_item.jsp">
			<jsp:param name="role" value="emp"/>
			<jsp:param name="action" value="emp:list"/>
		</jsp:include>
		<div class="content-wrapper">
			<!-- 此处编写需要显示的页面 -->
			<div class="row">
				<div class="col-xs-12">
					<div class="box">
						<!-- /.box-header -->
						<div class="box-body table-responsive no-padding">
							<div class="panel panel-info">
								<div class="panel-heading">
									<strong><span class="glyphicon glyphicon-user"></span>&nbsp;雇员信息列表</strong>
								</div>
								<div class="panel-body" style="height : 95%;">
									<jsp:include page="/pages/plugins/include_splitpage_search.jsp"/>

									<table class="table table-hover">
										<tr>
											<th width="7%" class="text-center">头像</th>
											<th width="8%" class="text-center">姓名</th>
											<th width="15%" class="text-center">级别</th>
											<th width="15%" class="text-center">职位</th>
											<th width="10%" class="text-center">部门</th>
											<th width="10%" class="text-center">基本工资</th>
											<th width="10%" class="text-center">佣金</th>
											<th width="15%" class="text-center">雇佣日期</th>
										</tr>

										<c:forEach items="${allEmps}" var="emp">
										<tr>
											<td class="text-center"><img src="<%=basePath%>upload/emp/sm-${emp.photo}" style="height:40px;width:40px" /></td>
											<td class="text-center">${emp.ename}</td>
											<td class="text-center">${lid_title_flag[''+emp.lid]}</td>
											<td class="text-center">${emp.job}</td>
											<td class="text-center">${deptno_dname[''+emp.deptno]}</td>
											<td class="text-center">¥${emp.sal}/月</td>
											<td class="text-center">¥${emp.comm}/月</td>
											<td class="text-center">${emp.hiredate}</td>
										</tr>
										</c:forEach>
									</table>
									<jsp:include page="/pages/plugins/include_splitpage_bar.jsp"/>
									<jsp:include page="/pages/plugins/include_alert.jsp"/>
								</div> 
							</div>
						</div>
						<!-- /.box-body -->
					</div>
					<!-- /.box -->
				</div>
			</div>
		</div>
		<!-- 导入公司尾部认证信息 -->
		<jsp:include page="/pages/plugins/include_title_foot.jsp" />
		<!-- 导入右边工具设置栏 -->
		<jsp:include page="/pages/plugins/include_menu_sidebar.jsp" />
		<div class="control-sidebar-bg"></div>
	</div>
	<jsp:include page="/pages/plugins/include_javascript_foot.jsp" />
</body>
</html>


  • 在Pages.properties文件中添加,离职列表页面的映射路径
emp.list.out.page=/pages/back/emp/emp_list_out.jsp
  • 修改EmpServletBackImpl程序中list()函数,当flag=0的时候,跳转到离职雇员页面中
 public String list(){
        try {
            if(!super.auth("emp:list")){
                super.setErrors("auth","auth.failure.msg");
                return "error.page";
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        String urlkey = "emp.list.servlet";//保存执行分页servlet的地址
        //判断用户查询的是在职雇员还是离职雇员的标记
        Integer flag = null;
        try {
            flag =  super.getIntParameter("flag");
            //如果没有flag参数,则默认为1
        }catch(Exception e){
            flag = 1;
        }

        //进行分页参数的获取
        SplitPageUtils splitPageUtils = new SplitPageUtils(super.request);
        Integer currentPage = splitPageUtils.getCurrentPage();
        Integer linesize = splitPageUtils.getLineSize();
        String keyword = splitPageUtils.getKeyWord();
        String column = splitPageUtils.getColumn();
        IEmpServiceBack empServiceBack = ServiceFactory.getInstance(EmpServiceBackImpl.class);
        try {
            Map<String,Object> splitResult = empServiceBack.listByFlag(super.getMid(),flag,currentPage,linesize,column,keyword);
            //将分页的数据传输到JSP中
           request.setAttribute("allEmps",splitResult.get("allEmps"));
           request.setAttribute("deptno_dname",splitResult.get("deptno_dname"));
           request.setAttribute("lid_title_flag",splitResult.get("lid_title_flag"));

           //分页组件传递分页所需的参数,实现切换页码
           super.setSplitPage(urlkey,(Integer) splitResult.get("allCount"),splitPageUtils);
           super.setSplitParam("flag",flag);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //显示在职人员
        if(flag==1) {
            return "emp.list.page";
        }else if(flag==0){//显示离职人员
            return "emp.list.out.page";
        }
        return "emp.list.page";
    }

  • 离职雇员列表,页面效果

在这里插入图片描述

雇员详情

  • 每一个雇员都有一个自己的详细日志记录,而且也有自己的完整信息.
  1. 查询一个雇员的完整数据,使用IEmpDAP.findById()方法
  2. 查询一个雇员的不嗯信息,可以直接使用IDeptDAO.findByid()方法
  3. 擦汗寻一个雇员的完整日志信息,需要阔从一个新的方法:IElogDAO.findAllByEMp()方法
  • 在IElogDAO中扩充一个新的方法:findAllByEmp()
    @Override
    public Set<Elog> findAllByEmp(Integer empno) throws SQLException {
        Set<Elog> allElogs = new HashSet<Elog>();
        String sql = "SELECT elid,empno,deptno,mid,lid,job,sal,comm,sflag,flag,note FROM elog WHERE empno=?";
        super.pstmt = super.conn.prepareStatement(sql);
        super.pstmt.setInt(1,empno);
        ResultSet resultSet = super.pstmt.executeQuery();
        while(resultSet.next()){
            Elog elog = new Elog();
            elog.setElid(resultSet.getInt(1));
            elog.setEmpno(resultSet.getInt(2));
            elog.setDeptno(resultSet.getInt(3));
            elog.setMid(resultSet.getString(4));
            elog.setLid(resultSet.getInt(5));
            elog.setJob(resultSet.getString(6));
            elog.setSal(resultSet.getDouble(7));
            elog.setComm(resultSet.getDouble(8));
            elog.setSflag(resultSet.getInt(9));
            elog.setFlag(resultSet.getInt(10));
            elog.setNote(resultSet.getString(11));
            allElogs.add(elog);
        }
        return allElogs;
    }
  • 在IEmpServiceBack接口中追加一个显示详细信息的方法:gitDetails()
   /**
     * 进行一个雇员的详细信息的取出操作,
     * @param mid 操作员的mid,需要进行判断该操作员是否有对应"emp:list"权限
     * @param empno 查看的雇员编号
     * @return 返回的map集合中包含一下内容
     * 1. key=emp value=该雇员的基本信息
     * 2. key=dept value= 该雇员所在的部门的信息
     * 3. key=level value= 该雇员的工资等级的详细信息
     * 4. key=allRoles value= 该雇员的所有操作日志信息
     * @throws Exception
     */
    public Map<String,Object> getDetails(String mid,Integer empno)throws Exception;
  • getDetails()方法的具体实现
public class EmpServiceBackImpl extends AbstractService implements IEmpServiceBack {
    @Override
    public Map<String, Object> getDetails(String mid, Integer empno) throws Exception {
        Map<String,Object> all = new HashMap<String,Object>();
        if(super.auth(mid,"emp:list")){
            IDeptDAO deptdao = DAOFactory.getInstance(DeptDAOImpl.class);
            ILevelDAO levelDAO = DAOFactory.getInstance(LevelDAOImpl.class);
            IEmpDAO empDAO = DAOFactory.getInstance(EmpDAOImpl.class);
            IElogDAO elogDAO = DAOFactory.getInstance(ElogDAOImpl.class);
            Emp emp = empDAO.findById(empno);
            if(emp!=null){
                Dept dept = deptdao.findById(emp.getDeptno());
                Level level = levelDAO.findById(emp.getLid());
                Set<Elog> allElogs = elogDAO.findAllByEmp(empno);
                all.put("dept",dept);
                all.put("level",level);
                all.put("allElogs",allElogs);
            }
            all.put("emp",emp);
        }
        return all;
    }
  • 在EmpServleBack中追加一个显示信息的方法:show()
    • 在Pages.properties文件中定义一个新的路径
emp.show.page=/pages/back/emp/emp_show.jsp

  • 定义show()方法
   public String show(){
        try {
            if(super.auth("emp:list")){
                //得到查询的雇员编号
                Integer empno = super.getIntParameter("empno");
                IEmpServiceBack empServiceBack = ServiceFactory.getInstance(EmpServiceBackImpl.class);
                Map<String, Object> map = empServiceBack.getDetails(super.getMid(), empno);
                super.setRequestAttribute("emp", map.get("emp"));
                super.setRequestAttribute("dept", map.get("dept"));
                super.setRequestAttribute("allElogs", map.get("allElogs"));
                return "emp_show.page";
            }
        } catch (Exception e) {
            super.setErrors("auth","auth.failure.msg");
            e.printStackTrace();
        }
        return "error.page";
    }
  • 定义emp_show.jsp页面显示雇员的详细信息
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ include file="/pages/plugins/include_static_head.jsp"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<jsp:include page="/pages/plugins/include_javascript_head.jsp" />
<script type="text/javascript" src="<%=basePath%>/js/pages/back/emp/emp_show.js"></script>
</head>
<body class="hold-transition skin-blue sidebar-mini">
	<div class="wrapper">
		<!-- 导入头部标题栏内容 -->
		<jsp:include page="/pages/plugins/include_title_head.jsp" />
		<!-- 导入左边菜单项 -->
		<jsp:include page="/pages/plugins/include_menu_item.jsp">
			<jsp:param name="role" value="emp"/>
			<jsp:param name="action" value="emp:add"/>
		</jsp:include>
		<div class="content-wrapper">
			<!-- 此处编写需要显示的页面 -->
			<div class="row">
				<div class="col-xs-12">
					<div class="box">
						<!-- /.box-header -->
						<div class="box-body table-responsive no-padding">
							<div class="panel panel-info">
								<div class="panel-heading">
									<strong><span class="glyphicon glyphicon-user"></span>&nbsp;雇员详细信息</strong>
								</div>
								<div class="panel-body" style="height : 95%;">
									<div class="row">
										<div class="col-md-2">
											<img src="upload/emp/${emp.photo}" class="img-responsive img-bordered-sm" alt="">
										</div>
										<div class="col-md-5">
											<div class="row">
												<div class="col-md-2 text-left">雇员编号</div>
												<div class="col-md-5 ">${emp.empno}</div>
											</div>
											<div class="row">
												<div class="col-md-2 text-left">雇员姓名</div>
												<div class="col-md-5 ">${emp.ename}</div>
											</div>
											<div class="row">
												<div class="col-md-2 text-left">部门编号</div>
												<div class="col-md-5 ">${emp.deptno}</div>
											</div>
											<div class="row">
												<div class="col-md-2 text-left">操作员</div>
												<div class="col-md-5 ">${emp.mid}</div>
											</div>
											<div class="row">
												<div class="col-md-2 text-left">工资等级</div>
												<div class="col-md-5 ">${level.title}</div>
											</div>
											<div class="row">
												<div class="col-md-2 text-left">职业</div>
												<div class="col-md-5 ">${emp.job}</div>
											</div>
											<div class="row">
												<div class="col-md-2 text-left">基本工资</div>
												<div class="col-md-5 ">${emp.sal}</div>
											</div>
											<div class="row">
												<div class="col-md-2 text-left">销售佣金</div>
												<div class="col-md-5 ">${emp.comm}</div>
											</div>
											<div class="row">
												<div class="col-md-2 text-left">入职日期</div>
												<div class="col-md-5 ">${emp.hiredate}</div>
											</div>
											<div class="row">
												<div class="col-md-2 text-left">在职状态</div>
												<c:if test="${emp.flag==0}">
													<div class="col-md-5  text-danger">离职</div>
												</c:if>
												<c:if test="${emp.flag==1}">
													<div class="col-md-5  text-success">在职</div>
												</c:if>

											</div>
										</div>

									</div>
									<h5 class="divider"></h5>
									<div class="row">
										<div class="panel panel-success">
											<div class="panel-heading">
												<strong><span class="glyphicon glyphicon-user"></span>&nbsp;雇员所在部门详情信息</strong>
											</div>
											<div class="panel-body">
												<div class="col-md-7">
													<div class="row">
														<div class="col-md-2 text-left">部门编号</div>
														<div class="col-md-5 ">${dept.deptno}</div>
													</div>
													<div class="row">
														<div class="col-md-2 text-left">部门名称</div>
														<div class="col-md-5 ">${dept.dname}</div>
													</div>
													<div class="row">
														<div class="col-md-2 text-left">部门最大人数</div>
														<div class="col-md-5 ">${dept.maxnum}</div>
													</div>
													<div class="row">
														<div class="col-md-2 text-left">部门当前人数</div>
														<div class="col-md-5 ">${dept.currnum}</div>
													</div>
												</div>
											</div>
										</div>
									</div>
									<div class="row">
										<div class="panel panel-warning">
											<div class="panel-heading">
												<strong><span class="glyphicon glyphicon-user"></span>&nbsp;雇员所处的工资等级详情</strong>
											</div>
											<div class="panel-body">
												<table class="table table-hover">
													<tr>
														<th>工资等级</th>
														<th>等级名称</th>
														<th>最低工资</th>
														<th>最高工资</th>

													</tr>
														<tr>
															<td>${level.flag}</td>
															<td>${level.title}</td>
															<td>${level.losal}</td>
															<td>${level.hisal}</td>
														</tr>
												</table>
											</div>
										</div>
									</div>
									<div class="row">
										<div class="panel panel-warning">
											<div class="panel-heading">
												<strong><span class="glyphicon glyphicon-user"></span>&nbsp;雇员所有日志详情信息</strong>
											</div>
											<div class="panel-body">
												<table class="table table-hover">
													<tr>
														<th>操作员编号</th>
														<th>职位变化</th>
														<th>所在部门变化</th>
														<th>工资调整</th>
														<th>佣金变化</th>
														<th>在职状态</th>
														<th>修改说明</th>
													</tr>
													<c:forEach items="${allElogs}" var="elog">
														<tr>
														<td>${elog.mid}</td>
														<td>${elog.job}</td>
														<td>${elog.deptno}</td>
														<td>
																${elog.sal}
																<c:if test="${elog.sflag == 0}">
																	<span class="glyphicon glyphicon-check"></span>
																</c:if>
																<c:if test="${elog.sflag == 1}">
																	<span class="text-success"><span class="glyphicon glyphicon-plus-sign"></span></span>
																</c:if>
																<c:if test="${elog.sflag == 2}">
																	<span class="text-danger"><span class="glyphicon glyphicon-minus-sign"></span></span>
																</c:if>
																<c:if test="${elog.sflag == 3}">
																	<span class="text-warning"><span class="glyphicon glyphicon-info-sign"></span></span>
																</c:if>
														</td>
														<td>${elog.comm}</td>
														<td>${elog.flag}</td>
														<td>${elog.note}</td>
														</tr>
													</c:forEach>
												</table>
											</div>
										</div>
									</div>

								</div>

							</div>


						</div>


						<!-- /.box-body -->
					</div>


					<!-- /.box -->
				</div>


			</div>

		</div>
		<!-- 导入公司尾部认证信息 -->
		<jsp:include page="/pages/plugins/include_title_foot.jsp" />
		<!-- 导入右边工具设置栏 -->
		<jsp:include page="/pages/plugins/include_menu_sidebar.jsp" />
		<div class="control-sidebar-bg"></div>
	</div>
	<jsp:include page="/pages/plugins/include_javascript_foot.jsp" />
</body>
</html>


  • 在雇员列表中的姓名上,添加超链接,超链接地址为: pages/back/emp/EmpServletBack/show
<%
	String showUrl = basePath + "pages/back/emp/EmpServletBack/show";
%>
<td class="text-center"><a href="<%=showUrl%>?empno=${emp.empno}">${emp.ename}</a></td>
  • 如果需要功能进一步加强,则可以进行每一项的日志记录,显示详细的日志记录

  • 页面显示效果

在这里插入图片描述

在这里插入图片描述

部门管理模块

部门管理开发任务简介

  • 实际上部门操作之中最为麻烦的设计就是无刷新分页显示.

    1. 部门的删除
    2. 部门人员列表
    3. 修改部门人数上限
  • 开发要求"

  • 只有管理员才能够进行部门操作,因为这些术语项目的整体数据,而项目中需要的一些分支数据(雇员),分支数据都交给管理员完成.

  • 在进行部门人数的上限调整的时候使用的是一个Ajax处理操作,所以整体的操作之中只需要有一个页面就够了.同时在进行部门雇员信息显示的时候由于一个部门的人员可能有很多:

在这里插入图片描述

  • 部门人数按钮触发的模态窗口

在这里插入图片描述

  • 在一个页面上要进行修改i机雇员信息列表都有一个关键的因素,那么这个因素就是部门编号,所有的部门编号都是用id的形式保存的.

部门列表

  • 部门列表只是一个简单的查询全部的操作.
  • 完善DeptDAOImpl中的findAll()方法
    @Override
    public List<Dept> findAll() throws SQLException {
        String sql = "SELECT deptno,dname,maxnum,currnum FROM dept";
        super.pstmt = super.conn.prepareStatement(sql);
        ResultSet resultSet = super.pstmt.executeQuery();
        List<Dept> list = new ArrayList<Dept>();
        while(resultSet.next()){
            Dept vo = new Dept();
            vo.setDeptno(resultSet.getInt(1));
            vo.setDname(resultSet.getString(2));
            vo.setMaxnum(resultSet.getInt(3));
            vo.setCurrnum(resultSet.getInt(4));
            list.add(vo);
        }
        return list;
    }
  • 在IDeptServiceBack接口里面定义一个查询全部的部门方法
    @Override
    public List<Dept> getALl() throws Exception {
        IDeptDAO deptDAO = DAOFactory.getInstance(DeptDAOImpl.class);
        return deptDAO.findAll();
    }
  • 建立DeptServletBack程序类实现部门的数据列表
  • 在Pages.properties文件中新增部门列表路径
dept.list.page=/pages/back/dept/dept_list.jsp
  • 建立DeptServletBack中的list()方法
    public String list()throws Exception{
        if(super.auth("dept:list")){
            IDeptServiceBack deptServiceBack = ServiceFactory.getInstance(DeptServiceBackImpl.class);
            List<Dept> allDepts = deptServiceBack.getALl(super.getMid());
            super.setRequestAttribute("allDepts",allDepts);
            return "dept.list.page";
        }else{
            super.setErrors("auth","auth.failure.msg");
            return "error.page";
        }
    }

  • 在dept_list.jsp页面中进行信息显示处理
<c:forEach items="${allDepts}" var="dept">
	<tr>
		<td><span id="dname-${dept.deptno}">${dept.dname}</span></td>
		<td>
			<input type="text" class="input-sm" name="maxnum-${dept.deptno}" id="maxnum-${dept.deptno}" value="${dept.maxnum}" style="width:20%">
			<button class="btn btn-info" id="editBtn-${dept.deptno}"><span class="fa fa-pencil-square"></span>&nbsp;调整人数上限</button>
		</td>
		<td><a id="showBtn-1" class="btn btn-warning" title="查看本部门雇员信息">1位雇员</a></td>
	</tr>
</c:forEach>
  1. 更改菜单include_menu_item.jsp页面中的部门列表链接地址
<a href="/pages/back/dept/DeptServlet/list"><i class="fa fa-circle-o"></i> 部门列表</a>
  • 页面效果

在这里插入图片描述

修改部门上限

  • 部门上线修改只需要控制一下几点

    1. 修改的部门人数不能够小于当前部门人数
    2. 前台和后台业务层上需要进行完整的控制
  • 在IdeptDAO中添加一个doUpdateMaxnum()方法

    /**
     *  进行制定部门的最大人数调整,部门最大人数修改成功返回true,否则返回false
     * @param deptno 修改的部门编号
     * @param maxnum 调整的部门最大人数
     * @return 
     * @throws SQLException
     */
    public boolean doUpdateMaxnum(Integer deptno,Integer maxnum)throws SQLException;
  • 实现该方法
   @Override
    public boolean doUpdateMaxnum(Integer deptno, Integer maxnum) throws SQLException {
        String sql = "UPDATE dept SET maxnum=? WHERE deptno=?";
        super.pstmt = super.conn.prepareStatement(sql);
        super.pstmt.setInt(1,maxnum);
        super.pstmt.setInt(2,deptno);
        return super.pstmt.executeUpdate() > 0;
    }

  • 在IDeptServiceBack接口中定义一个修改部门上限的方法:editMaxnum()
    /**
     * 修改制定部门的最大部门人数
     * @param mid 操作员的id,主要用于判断权限
     * @param dept 修改的部门信息,其中包含被修改的部门编号,和要调整的最大部门人数
     * @return
     * @throws Exception
     */
    
    public boolean editMaxnum(String mid,Dept dept)throws Exception;

  • editMaxnum()方法的具体实现
    @Override
    public boolean editMaxnum(String mid, Dept dept) throws Exception {
        if(super.auth(mid,"dept:edit")){
            IDeptDAO deptDAO = DAOFactory.getInstance(DeptDAOImpl.class);
            //查询处原部门信息
            Dept oldDept = deptDAO.findById(dept.getDeptno());
            //判断要修改的最大人数是否小于当前部门人数
            //如果修改的部门最大人数等于当前部门人数也是也可以通过的
            if(dept.getMaxnum() > oldDept.getCurrnum()){
                return deptDAO.doUpdateMaxnum(dept.getDeptno(),dept.getMaxnum());
            }else if (dept.getMaxnum() == oldDept.getCurrnum()){
                return true;
            }else{
                return false;
            }
        }
        return false;

    }
  1. 在DeptServletBack程序类之中修改上限的人数的方法,:editMaxnum()
    /**
     * 该方法用于页面中调整部门最大人数的ajax操作,直接将程序的执行结果打印到页面之中
     * 所以没有任何返回结果.
     */
    public void editMaxnum(){
        try {
            if(super.auth("dept:edit")){
                //获取页面请求中的部门编号和修改的部门最大人数
                Integer deptno = super.getIntParameter("deptno");
                Integer maxnum = super.getIntParameter("maxnum");
                IDeptServiceBack deptServiceBack = ServiceFactory.getInstance(DeptServiceBackImpl.class);
                Dept dept = new Dept();
                dept.setDeptno(deptno);
                dept.setMaxnum(maxnum);
                boolean resultFlag = deptServiceBack.editMaxnum(super.getMid(),dept);
                super.printData(resultFlag);
            }
        } catch (Exception e) {
            super.printData("false");
            e.printStackTrace();
        }
    }
  1. 重点在于针对于dept_list.jsp页面的控制上
    • 修改dept_list.js脚本
$(function(){
	$("[id*=editBtn-]").each(function(){
		var deptno = this.id.split("-")[1] ;
		$(this).on("click",function() {
			var maxnum = $("#maxnum-" + deptno).val();
			if (maxnum > 0){
				if (window.confirm("您确定要修改该部门的人数上限吗?")) {
					$.post("pages/back/dept/DeptServlet/editMaxnum", {
						deptno: deptno,
						maxnum: maxnum
					}, function (data) {
						operateAlert(data.trim() == "true", "部门人数上限修改成功!", "部门人数上限修改失败!");
					}, "text");

				}
			}else{
				operateAlert(false, "", "部门人数上限修改失败!调整的部门最大人数要大于部门当前人数");
			}
		}) ;
	});

});

在这里插入图片描述

  • 数据库中的数据

在这里插入图片描述

部门人员分页—后台业务

  • 在本次操作之中对于部门人员的信息列表采用了无刷新分页控制,主要的难点在于前端上,主要的控制将使用JSON作为数据传输.
  • 使用JSON还需要注意的是JSON使用的日期为java.sql.Date,如果java类中使用java.util.Date程序类描述日期,JSON将无法解析.
  1. 将json所需要的开发包拷贝到项目的lib目录之中,使用的是 json-lib-2.2.3-jdk13.jar 开发包
  2. 在IEmpDAO接口里面根据部门编号进行分页显示的操作,默认列出的都是在职的雇员,因此sql语句中会添加一个flag=1的条件
    • findAllByDept()
    • getAllCountByDept()
  @Override
    public List<Emp> splitAllByDept(Integer deptno, Integer currentPage, Integer linesize) throws SQLException {
        String sql = "SELECT empno,deptno,mid,lid,ename,job,sal,comm,hiredate,photo,flag FROM emp WHERE deptno=? AND flag=1 LIMIT ?,?";
        super.pstmt = super.conn.prepareStatement(sql);
        super.pstmt.setInt(1,deptno);
        super.pstmt.setInt(2,(currentPage-1)*linesize);
        super.pstmt.setInt(3,linesize);
        ResultSet resultSet = super.pstmt.executeQuery();
        List<Emp> allEmps = new ArrayList<Emp>();
        while(resultSet.next()){
            Emp emp = new Emp();
            emp.setEmpno(resultSet.getInt(1));
            emp.setDeptno(resultSet.getInt(2));
            emp.setMid(resultSet.getString(3));
            emp.setLid(resultSet.getInt(4));
            emp.setEname(resultSet.getString(5));
            emp.setJob(resultSet.getString(6));
            emp.setSal(resultSet.getDouble(7));
            emp.setComm(resultSet.getDouble(8));
            emp.setHiredate(new Date(resultSet.getDate(9).getTime()));
            emp.setPhoto(resultSet.getString(10));
            emp.setFlag(resultSet.getInt(11));
            allEmps.add(emp);
        }
        return allEmps;
    }

    @Override
    public Integer getALlCountByDept(Integer deptno) throws SQLException {
       String sql = "SELECT COUNT(*) FROM emp WHERE deptno=? AND flag=1";
       super.pstmt = super.conn.prepareStatement(sql);
       super.pstmt.setInt(1,deptno);
       ResultSet resultSet = super.pstmt.executeQuery();
       if(resultSet.next()){
           return resultSet.getInt(1);
       }
       return null;
    }
  1. 在IDeptServiceBack接口里面进行相应的方法定义,列出分页雇员
    • listEmpByDept()
  /**
     * 对部门中的雇员进行分页显示
     * @param mid 操作员的id 用于进行权限判断
     * @param deptno 部门编号
     * @param currentPage 要查询的当前所在页数
     * @param lineSize 每个页面显示的数据个数
     * @return 查询出的结果有两个内容
     * <ol>
     *  <PRE>
     *       <code>key = allEmps  value = 查询出的雇员数据</code>
     *  </PRE>
     *  <PRE>
     *      <code>key = allRecorders value = 查询出所有雇员数量</code>
     * </PRE>
     * </ol>
     * @throws Exception
     */
    public Map<String,Object> listEmpByDept(String mid,Integer deptno,Integer currentPage,Integer lineSize)throws Exception;

  • listEmpByDept()方法的具体实现

    @Override
    public Map<String, Object> listEmpByDept(String mid, Integer deptno, Integer currentPage, Integer lineSize) throws Exception {
        if(super.auth(mid,"dept:list")){
            Map<String,Object> resultMap = new HashMap<String,Object>();
            IEmpDAO empDAO = DAOFactory.getInstance(EmpDAOImpl.class);
            resultMap.put("allEmps",empDAO.splitAllByDept(deptno,currentPage,lineSize));
            resultMap.put("allRecorders",empDAO.getALlCountByDept(deptno));
        }
        return null;
    }
  1. 在DeptServletBack程序类中上进行数据的输出,这个数据使用的是JSON数据输出;
    • listEmp()
   /**
     * <p>该方法进行部门雇员的分页显示,使用的是ajax异步处理调用,所以没有任何返回值</p>
     * <p>将分页去除的数据直接以JSON数据格式打印到页面之中</p>
     * <p>
     *     打印的json数据中包含以下内容
     *     <ol>
     *         <li>
     *             flag:方法执行的结果
     *             allEmps:分页查询的雇员数据
     *             allRecorders:所有部门人员的数量
     *         </li>
     *     </ol>
     * </p>
     * @throws Exception
     */
    public void listEmp()throws Exception{
        JSONObject jsonObject = new JSONObject();
        if(super.auth("dept:list")){
            jsonObject.put("flag",true);
            //进行分页所需的基本数据处理,如果没有使用默认的数据
            SplitPageUtils splitPageUtils = new SplitPageUtils(super.request);
            Integer currentPage = splitPageUtils.getCurrentPage();
            Integer lineSize = splitPageUtils.getLineSize();
            Integer deptno  = super.getIntParameter("deptno");
            IDeptServiceBack deptServiceBack = ServiceFactory.getInstance(DeptServiceBackImpl.class);
            Map<String,Object> map = deptServiceBack.listEmpByDept(super.getMid(),deptno,currentPage,lineSize) ;
            jsonObject.put("allEmps",map.get("allEmps"));
            jsonObject.put("allRecorders",map.get("allRecorders"));
        }else{
            jsonObject.put("flag",false);
        }
        //向也页面之中打印JSON数据
        super.printData(jsonObject);
    }
  • 测试该方法是否能够使用,访问以下的地址

http://localhost:8080/EMProject/pages/back/dept/DeptServletBack/listEmp?deptno=1

  • 如果成功返回内容,则表示该方法可以使用

在这里插入图片描述

部门雇员分页—基础分页

  • 对于前端界面需要考虑到JSON数据进行处理情况
  • 修改dept_list.jsp页面,修改模态窗口中的<tbody>标签
<tbody id="empBody">
	

</tbody>
  • 在dept_list.jsp页面之中定义上一页和下一页按钮
    • previousBut
    • nextBut
<div class="row">
	<div class="col-md-4 col-md-offset-8">
		<ul class="pager">
			<li><span id="previousBut">上一页</span></li>
			<li><span id="nextBut">下一页</span></li>
		</ul>
	</div>
</div>
  • 在dept_list.js文件中编写一个loadDate()函数,进行ajax异步调用部门人员分页函数 DeptServletBack/listEmp
  1. 在函数外定义三个变量保存分页所需的数据
var cp = 1;//默认当前所在页为1
var lineSize = 5;//默认每页显示数据个数为5
var allRecorders = 0; //保存部门雇员总个数
var pageSize = 0;//保存分页总页数
  • 定义loadDate()函数
/**
 * 进行部门雇员分页ajax处理,调用DeptServletBack/list函数
 * <p>将分页查询出来的数据以json的数据格式返回
 * <p>将返回的数据,填写到 #empBody 元素中
 * @param deptno
 */
function loadData(deptno){
	$.post("pages/back/dept/DeptServletBack/listEmp",{cp:cp,lineSize:lineSize,deptno:deptno},function(data){
		if(data.flag==true){
			//取出数据总量
			allRecorders = data.allRecorders;
			//计算出分页总页数
			pageSize = (allRecorders +lineSize-1)/lineSize;
			//进行数据填写时,先清空表格中原有的内容,放置表格重复添加数据
			$("#empBody tr").remove();
			//进行数据填写
			for (var i = 0; i < data.allEmps.length; i++) {
				var empTrObj = "<tr> " +
					" <td class='text-center'><img src='upload/emp/"+data.allEmps[i].photo+"'style='width:30px;'></td> " +
					" <td class='text-center'>"+data.allEmps[i].ename+"</td>  " +
					" <td class='text-center'>"+data.allEMps[i].job+"</td> " +
					" <td class='text-center'>"+data.allEMps[i].lid+"</td> " +
					" <td class='text-center'>¥"+data.allEMps[i].sal+"/月</td> " +
					" <td class='text-center'>¥"+data.allEMps[i].comm+"/月</td> " +
					" <td class='text-center'>"+data.allEMps[i].hiredate+"</td> " +
					" </tr>"
				$("#empBody").append(empTrObj);
			}
		}
	},"json");

}
  • 为模态窗口绑定事件处理
$("[id*='showBtn-']").each(function(){	// 取得显示按钮
		var deptno = this.id.split("-")[1];	// 分离出id信息
		$(this).on("click",function(){
			$(deptTitleSpan).text($("#dname-" + deptno).text()) ;

			// 编写Ajax异步更新操作,读取所有的权限信息
			//每次打开模态窗口默认当前所在页为1
			cp = 1;
			//加载部门的雇员分页数据
			loadData(deptno);

			$("#empInfo").modal("toggle") ;//显示模态窗口
			//为上一页和下一页按钮绑定点击事件
			$("#previousBut").on("click",function(){
				if(cp > 1){
					//清除按钮样式
					$("#previousButLi").attr("class","");
					cp --;
					loadData(deptno);
				}else if(cp ==1 ){
					//禁用上一页按钮
					$("#previousButLi").attr("class","disabled");
				}else{
					//重新赋值,防止出现负数的情况
					cp=1;
				}
			});
			$("#nextBut").on("click",function(){
				if(cp < pageSize){
					$("#nextButLi").attr("class","");
					cp ++;
					loadData(deptno);
				}else if(cp == pageSize){
					$("#nextButLi").attr("class","disabled");
				}else{
					cp = pageSize;//防止出现cp > pageSize 的情况
				}
			});

		}) ;
	}) ;
  • 在关闭模态窗口的时候,应该要清楚上一页和下一页的按钮绑定的事件,否则会出现,同时执行多次的情况
	//设置模态窗口隐藏后,清楚上一页和下一页的绑定事件
	$("#empBody").on("hidden.bs.modal",function(){
		$("#previousBut").unbind("click");
		$("#nextBut").unbind("click");
	})
  • 初步效果

在这里插入图片描述

  • 初步效果中可以发现在雇佣日期的显示中,会发现显示的是:[Object Object],这是因为从ajax返回中的数据如果有date类型,则返回的结果是以下的格式

/Date(1436595149269)/

  • 所以如果要显示出可以阅读的时间,则需要将JSON的时间做一个格式化
  • 在mldn.js文件之中复写Date类的format()函数
// 对Date的扩展,将 Date 转化为指定格式的String
// 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符,
// 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
// 例子:
// (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423
// (new Date()).Format("yyyy-M-d h:m:s.S")      ==> 2006-7-2 8:9:4.18
Date.prototype.format = function(fmt) {
	var o = {
		"M+" : this.getMonth() + 1, //月份
		"d+" : this.getDate(), //日
		"h+" : this.getHours(), //小时
		"m+" : this.getMinutes(), //分
		"s+" : this.getSeconds(), //秒
		"q+" : Math.floor((this.getMonth() + 3) / 3), //季度
		"S" : this.getMilliseconds()
		//毫秒
	};
	if (/(y+)/.test(fmt))
		fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "")
			.substr(4 - RegExp.$1.length));
	for ( var k in o)
		if (new RegExp("(" + k + ")").test(fmt))
			fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k])
				: (("00" + o[k]).substr(("" + o[k]).length)));
	return fmt;
}
  • 修改dept_list.js文件中对日期取出的显示
" <td class='text-center'>"+new Date(data.allEmps[i].hiredate.time).format("yyyy-MM-dd")+"</td> " +

  • 修改后的页面效果

在这里插入图片描述

部门雇员分页—完善显示信息

需要知道雇员的级别
需要为雇员的状态

  • 在dept_list.jsp页面之中的模态窗口中的表格之中添加一行描述雇员在职状态的表头
<td style="width:10%;" class="text-center"><strong>在职状态</strong></td>
  • 修改dept_list.js文件,在该文件之中对每个取出的雇员数据进行判断,如果雇员的flag信息==1表示在职,如果flag==0表示离职
//进行数据填写
for (var i = 0; i < data.allEmps.length; i++) {
    var jobStatus = "";
    if(data.allEmps[i].flag==1){
        jobStatus ="<td class='text-center text-success'>在职</td>";
             }else{
                 jobStatus ="<td class='text-center text-danger'>离职</td>";

             }
	var empTrObj = "<tr> " +
		" <td class='text-center'><img src='upload/emp/"+data.allEmps[i].photo+"'style='width:30px;'></td> " +
		" <td class='text-center'>"+data.allEmps[i].ename+"</td>  				" +
		" <td class='text-center'>"+data.allEmps[i].job+"</td> " +
		" "+jobStatus+" " +
		" <td class='text-center'>"+data.allEmps[i].lid+"</td> " +
		" <td class='text-center'>¥"+data.allEmps[i].sal+"/月</td> " +
		" <td class='text-center'>¥"+data.allEmps[i].comm+"/月</td> " +
		" <td class='text-center'>"+new Date(data.allEmps[i].hiredate.time).format("yyyy-MM-dd")+"</td> " +
		" </tr>"
	$("#empBody").append(empTrObj);
}	

在这里插入图片描述

  • 如果要知道雇员的级别信息,需要修改业务层的方法,需要查询出所有的级别信息.
  • 修改IDeptServiceBackImpl中的listEmpByDept()方法,查询处所有的级别信息
   @Override
    public Map<String, Object> listEmpByDept(String mid, Integer deptno, Integer currentPage, Integer lineSize) throws Exception {
        if(super.auth(mid,"dept:list")){
            Map<String,Object> resultMap = new HashMap<String,Object>();
            IEmpDAO empDAO = DAOFactory.getInstance(EmpDAOImpl.class);
            ILevelDAO levelDAO = DAOFactory.getInstance(LevelDAOImpl.class);
            resultMap.put("allEmps",empDAO.splitAllByDept(deptno,currentPage,lineSize));
            resultMap.put("allRecorders",empDAO.getALlCountByDept(deptno));
            resultMap.put("allLevels",levelDAO.findAll());
            return resultMap;
        }
        return null;
    }
  • 而后级别信息需要通过servlet设置到JSON之中,之后要进行对级别的内容进行确认.
  • 修改DeptServletBack中的listEmp()方法
  public void listEmp()throws Exception{
        JSONObject jsonObject = new JSONObject();
        if(super.auth("dept:list")){
            jsonObject.put("flag",true);
            //进行分页所需的基本数据处理,如果没有使用默认的数据
            SplitPageUtils splitPageUtils = new SplitPageUtils(super.request);
            Integer currentPage = splitPageUtils.getCurrentPage();
            Integer lineSize = splitPageUtils.getLineSize();
            Integer deptno  = super.getIntParameter("deptno");
            IDeptServiceBack deptServiceBack = ServiceFactory.getInstance(DeptServiceBackImpl.class);
            Map<String,Object> map = deptServiceBack.listEmpByDept(super.getMid(),deptno,currentPage,lineSize) ;
            jsonObject.put("allEmps",map.get("allEmps"));
            jsonObject.put("allRecorders",map.get("allRecorders"));
            jsonObject.put("allLevels",map.get("allLevels"));
        }else{
            jsonObject.put("flag",false);
        }
        //向也页面之中打印JSON数据
        super.response.setContentType("JSON");
        super.printData(jsonObject);
    }
  • 修改dept_list.js文件,判断每个雇员数据中的lid,并显示出每个级别的title和flag
//进行数据填写
			for (var i = 0; i < data.allEmps.length; i++) {
				var jobStatus = "";
				if(data.allEmps[i].flag==1){
					jobStatus ="<td class='text-center text-success'>在职</td>";
				}else{
					jobStatus ="<td class='text-center text-danger'>离职</td>";

				}
				
				var levleTitleFlag = "";
				for (var j = 0; j < data.allLevels.length; j++) {
					if(data.allLevels[j].lid == data.allEmps[i].lid){
						levleTitleFlag = data.allLevels[j].title+"-"+data.allLevels[j].flag;
					}
				}
				var empTrObj = "<tr> " +
					" <td class='text-center'><img src='upload/emp/"+data.allEmps[i].photo+"'style='width:30px;'></td> " +
					" <td class='text-center'>"+data.allEmps[i].ename+"</td>  " +
					" <td class='text-center'>"+data.allEmps[i].job+"</td> " +
					" "+jobStatus+" "+
					" <td class='text-center text-info'>"+levleTitleFlag+"</td> " +
					" <td class='text-center'>¥"+data.allEmps[i].sal+"/月</td> " +
					" <td class='text-center'>¥"+data.allEmps[i].comm+"/月</td> " +
					" <td class='text-center'>"+new Date(data.allEmps[i].hiredate.time).format("yyyy-MM-dd")+"</td> " +
					" </tr>"
				$("#empBody").append(empTrObj);
			}

在这里插入图片描述

管理员用户模块

  • 在整个的项目之中管理员也就是用户,使用member表保存用户.可以进行管理员的添加以及管理员的列表处理,不允许管理员的删除操作

  • 本模块的编写主要是使用member表,role表,memberrole表

  • 为了安全起见,所有的管理员只能够由超级管理员创建.

  • 管理员添加jsp页面

[img]

  • 管理员再添加的时候必须有角色数据,哪怕只有一个角色

管理员列表

  • 管理员列表只是针对于Member数据表的一个完整显示,并且一个项目中的管理员不会很多,所以不需要有分页功能
  1. 建立IMemberDAo接口,实现findAll()方法

    @Override
    public List<Member> findAll() throws SQLException {
       String sql = "SELECT mid,name,sflag FROM member ";
       super.pstmt = super.conn.prepareStatement(sql);
       ResultSet resultSet = super.pstmt.executeQuery();
       List<Member> list = new ArrayList<Member>();
       while(resultSet.next()){
           Member vo = new Member();
           vo.setMid(resultSet.getString(1));
           vo.setName(resultSet.getString(2));
           vo.setSflag(resultSet.getInt(3));
           list.add(vo);
       }
       return list;
    }
  1. 建立一个IMemberServiceBack业务接口

    /**
     * 该方法进行所有管理员的列出操作
     * @param mid 登录用户的id,用于判断权限"member:list"
     * @return
     * @throws Exception
     */
    public Map<String,Object> list(String mid)throws Exception;
  • list()方法的具体实现
    @Override
    public Map<String, Object> list(String mid) throws Exception {
        if(super.auth(mid,"member:list")){
            IMemberDAO memberDAO = DAOFactory.getInstance(MemberDAOImpl.class);
            Map<String,Object> map = new HashMap<String,Object>();
            map.put("allMembers",memberDAO.findAll());
            return map;
        }
        return null;

    }
  1. 建立MemberServletBack程序类,实现list()方法
    public String list(){
        try {
            if(super.auth("member:list")){
                IMemberServiceBack memberServiceBack = ServiceFactory.getInstance(MemberServiceBackImpl.class);
                Map<String,Object> map = memberServiceBack.list(super.getMid());
                super.setRequestAttribute("allMembers",map.get("allMembers"));
            }else{
               super.setErrors("auth","auth.failure.msg");
               return "error.page";
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "error.page";

        }
        return "member.list.page";
    }
  1. 在member_list.jsp的页面中进行数据输出
<table class="table table-hover">
	<tr>
		<th width="60%">登录帐号</th>
		<th width="20%">姓名</th>
		<th width="20%">等级</th>
	</tr>
	<c:forEach items="${allMembers}" var="member">
	<tr>
		<td>${member.mid}</td>
		<td>${member.name}</td>
		<td>${member.sflag==1?"超级管理员":"普通管理员"}</td>
	</tr>
	</c:forEach>
</table>
  1. 修改include_menu.jsp页面中的用户列表链接
<li class="${param.action=='member:list' ? 'active' : ''}">
	<a href="pages/back/member/MemberServletBack/list">
		<i class="fa fa-circle-o"></i>管理员列表
	</a>
</li>
  • 在Pages.properties文件中添加部门列表的映射路径
member.list.page=/pages/back/member/member_list.jsp

  • 页面效果

在这里插入图片描述

管理员创建—后台业务

  • 对于管理员的数据必须有一个前提:mid绝对不能够重复.
  1. 再添加管理员的时候需要列出所有的角色数据,需要完善RoleDAOImpl中的findAll()方法
    @Override
    public List<Role> findAll() throws SQLException {
       String sql = "SELECT rid,title,flag FROM role;";
        super.pstmt = super.conn.prepareStatement(sql);
        ResultSet resultSet = super.pstmt.executeQuery();
        List<Role> list = new ArrayList<Role>();
        while(resultSet.next()){
            Role role = new Role();
            role.setRid(resultSet.getInt(1));
            role.setTitle(resultSet.getString(2));
            role.setFlag(resultSet.getString(3));
            list.add(role);
        }
        return list;
    }
  • 在IRoleDAO中追加一个批量添加用户角色信息的方法
    /**
     * 进行向member_role 表中添加多条数据,保存指定用户的角色信息
     * @param mid 添加的用户id
     * @param rids 保存的角色id集合
     * @return 所有数据保存成功返回他如何,否则返回false
     * @throws SQLException
     */
    public boolean doCreateByMember(String mid, Set<Integer> rids)throws SQLException;
  • doCreateByMember()方法的具体实现
   @Override
    public boolean doCreateByMember(String mid, Set<Integer> rids) throws SQLException {
        String sql = "INSERT INTO member_role(mid,rid)VALUES(?,?)";
        super.pstmt = super.conn.prepareStatement(sql);
        Iterator<Integer> ridIterator = rids.iterator();
        while(ridIterator.hasNext()){
            Integer rid = ridIterator.next();
            super.pstmt.setString(1,mid);
            super.pstmt.setInt(2,rid);
            super.pstmt.addBatch();
        }
        int[] resultArray = super.pstmt.executeBatch();
        for (int i : resultArray) {
            if (i < 1) {
                return false;
            }

        }
        return true;
    }
  • 需要保证追加的mid的信息不存在,这个可以借助IMemberDAO中的findById()方法
  • 追加管理员需要复写IMemberDAO中的doCreate()方法
    @Override
    public boolean doCreate(Member vo) throws SQLException {
        String sql = "INSERT INTO member(mid,password,name,sflag)VALUES(?,?,?,?)";
        super.pstmt = super.conn.prepareStatement(sql);
        super.pstmt.setString(1,vo.getMid());
        super.pstmt.setString(2,vo.getPassword());
        super.pstmt.setString(3,vo.getName());
        //新的管理员只能是普通管理员,只有超级管理员才能够将修改普通管理员
        super.pstmt.setInt(4,0);
        return super.pstmt.executeUpdate() > 0;
    }

  • 在IMemberServiceBack接口中定义三个方法:

    1. addPre():判断操作用户是否为超级管理员,查询出所有角色数据
    2. get():为mid数据的检测使用,主要为了ajax异步验证使用,防止管理员的mid重复
    3. add()实现管理员数据的添加处理
  • 由于这三个方法都需要做超级管理员的判断,因此可以将超级管理员的判断的操作方法,定义在业务层的公共父类:AbstractService类中

  • 在AbstractService类中定义一个 admin()方法

    /**
     * 判断一个管理员是否为超级管理员,依靠Member中的sflag字段判断
     * <p>如果sflag=1表示为超级管理员,如果sflag=0表示为普通管理员</p>
     * @param mid 管理员的id
     * @return
     * @throws Exception
     */
    public boolean admin(String mid)throws Exception{
        IMemberDAO memberDAO = DAOFactory.getInstance(MemberDAOImpl.class);
        Member member = memberDAO.findById(mid);
        if(member.getSflag().equals(1)){
            return true;
        }else{
            return false;
        }
    }
  • 此时这个方法中有一个缺点,那就是每次执行这个方法都要进行对数据库查询,那么如果有许多个地方同时调用该方法,则会导致过多的访问数据库,并且查询的还是同一个数据,就会浪费数据库的资源.因此在以后开发中这样的方法还可以改善

  • 完善IMemberServiceBack接口

   /**
     * 进行管理员添加前的准备工作
     * 1. 判断操作管理员是否为超级管理员,只有超级管理员才能够添加管理员
     * 2. 取出所有的角色数据
     * @param mid
     * @return
     * @throws Exception
     */
    public Map<String,Object> addPre(String mid)throws Exception;

    /**
     * 进行一个新管理员的添加操作,执行添加操作时需要进行如下操作
     * <li>判断操作管理员(mid)是否为超级管理员</li>
     * <li>如果符合以上的条件,使用IMemberDAO中的doCreate()方法添加一个新的管理员</li>
     * <li>添加新的管理员成功之后需要想member_role表中添加新的管理员角色数据,使用IRoleDAO中的doCreateByMember()方法</li>
     * @param vo
     * @param rids
     * @return
     * @throws Exception
     */
    public boolean add(String mid,Member vo, Set<Integer> rids)throws Exception;

    /**
     * <p>该方法提供给页面的ajax异步判断,用于判断新增加的管理员的mid是否已经存在</p>
     * @throws Exception
     */
    public Member checkMid(String loginMid,String addMid)throws Exception;
  • 在MemberServiceBack中实现这三个方法
   @Override
    public Map<String, Object> addPre(String mid) throws Exception {
        if (super.admin(mid)){
            IRoleDAO roleDAO = DAOFactory.getInstance(RoleDAOImpl.class);
            Map<String,Object> map = new HashMap<String,Object>();
            map.put("allRoles",roleDAO.findAll());
            return map;
        }
        return null;
    }

    @Override
    public boolean add(String mid, Member vo, Set<Integer> rids) throws Exception {
       if(super.admin(mid)){
           IMemberDAO memberDAO = DAOFactory.getInstance(MemberDAOImpl.class);
           IRoleDAO roleDAO = DAOFactory.getInstance(RoleDAOImpl.class);
         Member addMember = memberDAO.findById(vo.getMid());
           if(addMember == null){
               vo.setSflag(0);
               //将密码进行统一的加密处理
               vo.setPassword(EncryptUtil.getPassword(vo.getPassword()));
               if(memberDAO.doCreate(vo)){
                   return roleDAO.doCreateByMember(vo.getMid(),rids);
               }
           }
       }
       return false;
    }

    @Override
    public Member checkMid(String loginMid,String addMid) throws Exception {
        if(super.admin(loginMid)){
            IMemberDAO memberDAO = DAOFactory.getInstance(MemberDAOImpl.class);
            return memberDAO.findById(addMid);
        }
        return null;
    }

管理员创建—前台页面

  • 建立操作的Servlet程序
    1. 在EMServlet里面追加一个验证超级管理员的方法
  /**
     * 判断操作用户是否为超级用户
     * @return
     */
    public boolean admin(){
        String mid = this.getMid();
        IMemberDAO memberDAO = DAOFactory.getInstance(MemberDAOImpl.class);
        try {
            Member member = memberDAO.findById(mid);
                return member.getSflag().equals(1);

        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }
  • 在MemberServletBack程序类里面进行
    1. 在MemberServletBack程序类中添加 一个Member类成员属性,为DispatcherServlet类提供反射赋值操作
    2. 定义addPre()方法进行角色信息的列出
    private Member member = new Member();
    public Member getMember(){
        return this.member;
    }
   /**
     * 进行管理员添加时的准备操作,取出所有的角色信息
     * <p>需要注意的是在管理员登录时,已经将登录的管理员具有的所有角色都保存到了 request的attribute属性返回之中</p>
     * <p>所以此处的所有的角色设置到request的属性范围之中时,名称为"addPre_allRoles"</p>
     * @return 返回要跳转的页面,有DispatcherServlet程序类完成页面跳转
     */
    public String addPre(){
        if(super.admin()){
            IMemberServiceBack memberServiceBack = ServiceFactory.getInstance(MemberServiceBackImpl.class);
            try {
                Map<String,Object> map = memberServiceBack.addPre(super.getMid());
                super.setRequestAttribute("addPre_allRoles",map.get("allRoles"));
            } catch (Exception e) {
                e.printStackTrace();
                return "error.page";
            }
            return "member.add.page";
        }else{
            super.setErrors("auth","auth.failure.msg");
            return "error.page";
        }
    }
  • 定义用户名的检测处理
 public void checkMid(){
        if(super.admin()) {
            //得到ajax异步请求的mid参数
            String addMid = super.request.getParameter("mid");
            IMemberServiceBack memberServiceBack = ServiceFactory.getInstance(MemberServiceBackImpl.class);
            try {
                Member member = memberServiceBack.checkMid(super.getMid(),addMid);
                super.printData(member==null);

            } catch (Exception e) {
                e.printStackTrace();
                super.printData("false");
            }
        }else{
            super.printData("false");
        }
    }

  • 定数据的保存处理操作
 public String add(){
        if(super.admin()){
            //取得页面提交的所有角色id
            String[] rids = super.request.getParameterValues("rids");
            Set<Integer> ridsSet = new HashSet<Integer>();
            try {
                for (int i = 0; i < rids.length; i++) {
                    ridsSet.add(Integer.parseInt(rids[i]));
                }
            }catch(Exception e){}
            //进行密码加密处理
            this.member.setPassword(EncryptUtil.getPassword(this.member.getPassword()));
            IMemberServiceBack memberServiceBack = ServiceFactory.getInstance(MemberServiceBackImpl.class);

            try {
                if(memberServiceBack.add(super.getMid(),this.member,ridsSet)){
                    super.setUrlAndMsg("member.add.servlet","vo.add.success.msg");
                }else{
                    super.setUrlAndMsg("member.add.servlet","vo.add.failure.msg");
                }
            } catch (Exception e) {
            super.setUrlAndMsg("member.add.servlet","vo.add.failure.msg");
                e.printStackTrace();
            }
        }else{
            super.setUrlAndMsg("error.page","auth.failure.msg");
        }
        return "forward.page";
    }
  • 修改member_add.jsp页面
  1. 修改表单的提交路径
<%
	String addMemberUrl = basePath + "pages/back/member/MemberServletBack/add" ;
%>

  1. 修改member_add.js文件,对于member.mid需要进行异步验证
messages : {
"member.mid" : "该用户名已存在,无法使用,请跟换"
},
rules : {	// 针对于每一个表单实现的验证控制处理
			"member.mid" : {
				required : true ,
				remote : {	// 需要进行远程交互验证
					url : "pages/back/member/MemberServletBack/checkMid" ,
					type : "post" ,
					dataType : "text" ,
					data : {
						mid : function () {
							return $("#member\\.mid").val() ; 
						}
					} ,
					dataFilter : function(data,type) {
						return data.trim()=="true";
					}
				}
			} , 
			"member.name" : {
				required : true 
			} ,
			"member.password" : {
				required : true 
			} ,
			"rid" : {
				required : true 
			} 
		} 
  • 修改include_menu_items.jsp页面中的部门添加列表的链接地址
<li class="${param.action=='member:add' ? 'active' : ''}">
	<a href="pages/back/member/MemberServletBack/addPre">
		<i class="fa fa-circle-o"></i>增加管理员
	</a>
</li>
  • 页面效果

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 登录新增的管理员

在这里插入图片描述

  • 拥有对应的权限管理页面

在这里插入图片描述

  • 数据库中添加的数据
  1. member_role数据表

在这里插入图片描述

  1. member表

在这里插入图片描述

没有更多推荐了,返回首页