客户关系管理项目——用户登录模块设计

一 模块需求细化

登录的用户,默认情况有三个不同角色,分别为:系统管理员,前台客服,信息管理员。

用户登录后能够根据其角色来进行相关工作,进行完工作需要能够注销。

细化需求如下:

  • 用户登录之后按角色分配权限信息。

  • 登录日志表自动保存登录信息。

  • 需要把权限信息保存在Session中。

  • 需要登录检测,Session中保存用户id和最后登录时间等。

  • 在member表中也需要更新最后一次登录的日期时间。

  • 用户登录后可进行密码修改。

  • 需要用户登录后能够注销。

二 模块相关数据库实现细节

1 输入数据表

用户表:member

角色:role

角色-权限关系表:role-groups

权限组-权限关系表:groups-action

权限组表:group

权限表:action

2 输出数据表

用户登录日志表:logs

用户:member

三 用户登录界面设计

1 登录页面代码login.jsp

<%@ page language="java" import="java.util.*"  pageEncoding="UTF-8"%>
<%
     String path = request.getContextPath();
     String basePath = request.getScheme() + "://"
                + request.getServerName() + ":" +  request.getServerPort()
                + path + "/";
     String loginUrl = basePath + "LoginServletBack/login" ;
     System.out.println(loginUrl);
%>
<html>
<head>
<base href="<%=basePath%>">
<title>CRM管理系统</title>
<jsp:include page="/pages/plugins/import_file.jsp"/>
<script src="<%=basePath%>js/cloud.js"  type="text/javascript"></script>
<script src="<%=basePath%>js/login.js"  type="text/javascript"></script>
</head>
<body style="background-color:#df7611;  background-image:url(images/light.png);  background-repeat:no-repeat; background-position:center top;  overflow:hidden;">
     <div id="mainBody">
           <div id="cloud1" class="cloud"></div>
           <div id="cloud2" class="cloud"></div>
     </div>
     <div class="logintop">
           <span>欢迎登录CRM管理界面平台</span>
           <ul>
                <li><a href="#">回首页</a></li>
                <li><a href="#">帮助</a></li>
                <li><a href="#">关于</a></li>
           </ul>
     </div>
     <div class="loginbody">
           <span class="systemlogo"></span>
           <div class="loginbox">
                <form action="<%=loginUrl%>" method="post"  id="loginForm">
                <ul>
                     <li><input name="member.mid"  id="member.mid" type="text" class="loginuser"
                           placeholder="输入登录用户名"/></li>
                     <li><input name="member.password"  id="member.password" type="password" class="loginpwd"
                           placeholder="请输入登录密码"/></li>
                     <li><input type="submit" class="loginbtn"  value="登录"/></li>
                </ul>
                </form>
           </div>
     </div>
     <div class="loginbm"></div>
</body>
</html>

2 界面展示

3 输入用户名和密码,点击确认

会跳到loginUrl去处理,loginUrl的值为:http://localhost:8080/CRMProject/LoginServletBack/login

LoginServletBack/login的处理入口见控制层代码。

4 如果用户名和密码正确,控制层代码会将页面转向/pages/back/index.jsp

<%@ page language="java" import="java.util.*"  pageEncoding="UTF-8"%>
<%
     String path = request.getContextPath();
     String basePath = request.getScheme() + "://"
                + request.getServerName() + ":" +  request.getServerPort()
                + path + "/";
%>
<html>
<head>
<base href="<%=basePath%>">
<title>CRM管理系统</title>
<jsp:include page="/pages/plugins/import_file.jsp"/>
</head>
<frameset rows="88,*,31" cols="*" frameborder="no" border="0"
     framespacing="0">
     <frame src="<%=basePath%>pages/back/top.jsp"  name="topFrame" scrolling="No"
           noresize="noresize" id="topFrame" title="topFrame" />
     <frameset cols="187,*" frameborder="no" border="0"  framespacing="0">
           <frame src="<%=basePath%>pages/back/left.jsp"  name="leftFrame" scrolling="No"
                noresize="noresize" id="leftFrame"  title="leftFrame" />
           <frame  src="<%=basePath%>pages/back/DefaultServletBack/show"  name="rightFrame" id="rightFrame"
                title="rightFrame" />
     </frameset>
     <frame src="<%=basePath%>pages/back/footer.jsp"  name="bottomFrame" scrolling="No"
           noresize="noresize" id="bottomFrame"  title="bottomFrame" />
</frameset>
<noframes>
</html>

5 该页面展示如下:

上面代码关键是<%=basePath%>pages/back/DefaultServletBack/show,它是控制层实现的。我们去控制层分析该代码。

四 控制层模块设计实现

1 登录控制层代码实现

package cn.mldn.crm.servlet.back;

import java.util.Map;

import javax.servlet.annotation.WebServlet;

import cn.mldn.crm.factory.ServiceFactory;
import cn.mldn.crm.vo.Member;
import cn.mldn.util.MD5Code;
import cn.mldn.util.servlet.DispatcherServlet;

@SuppressWarnings("serial")
@WebServlet(urlPatterns = "/LoginServletBack/*")                // 当点击登录代码,会走到这里
public class LoginServletBack extends DispatcherServlet {
    private Member member = new Member();
    public String login() {
        // 把输入的密码加密成功后设置给password属性
        this.member.setPassword(new MD5Code().getMD5ofStr(this.member.getPassword()));
        try {
            // 调用业务层获得用户相关信息
            Map<String,Object> map = ServiceFactory.getIMemberServiceBackInstance().login(this.member) ;
            boolean loginFlag = (Boolean) map.get("flag") ;
            if (loginFlag) {
                super.getSession().setAttribute("mid", this.member.getMid());
                super.getSession().setAttribute("flag", this.member.getFlag());
                super.getSession().setAttribute("groups", this.member.getRole().getGroups());
                super.getSession().setAttribute("allActions", map.get("allActions"));
                super.getSession().setAttribute("unread", map.get("unread"));
                // 处理权限问题;
                super.setMsgAndUrl("member.login.success", "index.page");
            } else {
                super.setMsgAndUrl("member.login.failure", "member.login.page");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "forward.page" ;
    }
    ......

}

2 在上面这个函数中,如果密码输入正确,会跳到index.page去处理,并携带消息member.login.success

这里index.page为index.page=/pages/back/index.jsp

member.login.success为用户登录成功,欢迎光临!

3 相关属性文件定义

消息属性文件

vo.add.success={0}\u6570\u636E\u589E\u52A0\u6210\u529F\uFF01
vo.add.failure={0}\u6570\u636E\u589E\u52A0\u5931\u8D25\uFF01
vo.edit.success={0}\u6570\u636E\u4FEE\u6539\u6210\u529F\uFF01
vo.edit.failure={0}\u6570\u636E\u4FEE\u6539\u5931\u8D25\uFF01
vo.rm.success={0}\u6570\u636E\u5220\u9664\u6210\u529F\uFF01
vo.rm.failure={0}\u6570\u636E\u5220\u9664\u5931\u8D25\uFF01
member.login.success=\u7528\u6237\u767B\u5F55\u6210\u529F\uFF0C\u6B22\u8FCE\u5149\u4E34\uFF01
member.login.failure=\u767B\u5F55\u5931\u8D25\uFF0C\u9519\u8BEF\u7684\u7528\u6237\u540D\u6216\u5BC6\u7801\uFF01
member.logout.success=\u7528\u6237\u6CE8\u9500\u6210\u529F\uFF0C\u518D\u89C1\uFF01
member.password.edit.success=\u7528\u6237\u5BC6\u7801\u4FEE\u6539\u6210\u529F\uFF0C\u4E3A\u4E86\u4FDD\u8BC1\u5B89\u5168\u8BF7\u91CD\u65B0\u767B\u5F55\uFF01
member.password.edit.failure=\u7528\u6237\u5BC6\u7801\u4FEE\u6539\u5931\u8D25\uFF0C\u4E3A\u4E86\u4FDD\u8BC1\u5B89\u5168\u8BF7\u91CD\u65B0\u767B\u5F55\uFF01
member.edit.password.admin.success=\u7528\u6237\u5BC6\u7801\u91CD\u7F6E\u6210\u529F\uFF01
member.edit.password.admin.failure=\u7528\u6237\u5BC6\u7801\u91CD\u7F6E\u5931\u8D25\uFF01
task.over.success=\u4EFB\u52A1\u5173\u95ED\u6210\u529F\uFF01
task.over.failure=\u4EFB\u52A1\u5173\u95ED\u6210\u529F\uFF01
task.finish.success=\u4EFB\u52A1\u6210\u529F\u5B8C\u6210\uFF01
task.finish.failure=\u4EFB\u52A1\u66F4\u65B0\u51FA\u9519\uFF0C\u672A\u5B8C\u6210\uFF01

页面属性文件

error.page=/pages/errors.jsp
forward.page=/pages/forward.jsp
#back.emp.add.jsp=/pages/emp/emp_add.jsp
#back.emp.list.jsp=/pages/emp/emp_list.jsp
member.login.page=/login.jsp
member.logout.servlet=/LoginServletBack/logout
index.page=/pages/back/index.jsp
default.page=/pages/back/default.jsp
member.password.edit.page=/pages/back/member/member_edit_password.jsp
client.add.page=/pages/back/client/client_add.jsp
client.add.servlet=/pages/back/client/ClientServletBack/addPre
client.list.page=/pages/back/client/client_list.jsp
client.list.servlet=/pages/back/client/ClientServletBack/listSplit
client.edit.page=/pages/back/client/client_edit.jsp
mclient.list.page=/pages/back/mclient/client_list.jsp
mclient.show.page=/pages/back/mclient/client_show.jsp
mclient.list.servlet=/pages/back/mclient/ManagerClientServletBack/listSplit
mtask.list.page=/pages/back/mtask/task_list.jsp
mtask.list.servlet=/pages/back/mtask/ManagerTaskServletBack/listSplit
mtask.show.page=/pages/back/mtask/task_show.jsp
mtask.client.list.page=/pages/back/mtask/task_client_list.jsp
task.add.page=/pages/back/task/task_add.jsp
task.add.servlet=/pages/back/task/TaskServletBack/addPre
task.client.list.page=/pages/back/task/task_client_list.jsp
task.show.page=/pages/back/task/task_show.jsp
task.list.page=/pages/back/task/task_list.jsp
task.list.servlet=/pages/back/task/TaskServletBack/listSplit
task.edit.page=/pages/back/task/task_edit.jsp
news.add.page=/pages/back/news/news_add.jsp
news.add.servlet=/pages/back/news/NewsServletBack/addPre
news.list.page=/pages/back/news/news_list.jsp
news.list.servlet=/pages/back/news/NewsServletBack/list
news.show.page=/pages/back/news/news_show.jsp
news.edit.page=/pages/back/news/news_edit.jsp
action.list.page=/pages/back/action/action_list.jsp
groups.list.page=/pages/back/groups/groups_list.jsp
groups.show.page=/pages/back/groups/groups_show.jsp
role.add.page=/pages/back/role/role_add.jsp
role.add.servlet=/pages/back/role/RoleServletBack/addPre
role.list.page=/pages/back/role/role_list.jsp
role.list.servlet=/pages/back/role/RoleServletBack/list
role.show.page=/pages/back/role/role_show.jsp
role.edit.page=/pages/back/role/role_edit.jsp
member.add.page=/pages/back/member/member_add.jsp
member.add.servlet=/pages/back/member/MemberServletBack/addPre
member.list.page=/pages/back/member/member_list.jsp
member.list.servlet=/pages/back/member/MemberServletBack/listSplit
member.edit.page=/pages/back/member/member_edit.jsp
member.edit.password.admin.servlet=/pages/back/member/MemberServletBack/editPassPre
member.edit.password.admin.page=/pages/back/member/member_edit_password_admin.jsp

4 登录后内容区显示内容,通过DefaultServletBack的show函数实现。

@SuppressWarnings("serial")
@WebServlet("/pages/back/DefaultServletBack/*")
public class DefaultServletBack extends AbstractCRMServlet {
     public String show() {
           try {
                Map<String, Object> map = ServiceFactory
                           .getIDefaultServiceBackInstance().stat(super.getMid());
                super.request.setAttribute("allNewses",  map.get("allNewses"));
                super.request.setAttribute("allClients",  map.get("allClients"));
                super.request.setAttribute("allTasks",  map.get("allTasks"));
                super.request.setAttribute("clientCount",  map.get("clientCount"));
                super.request.setAttribute("unfinishCount",
                           map.get("unfinishCount"));
                super.request.setAttribute("wfinishCount",  map.get("wfinishCount"));
           } catch (Exception e) {
                e.printStackTrace();
           }
           return "default.page";
     }
   ......
}

五 业务层模块设计实现

1 业务层登录代码实现

public class MemberServiceBackImpl extends  AbstractCRMServiceBack implements
           IMemberServiceBack {
     @Override
     public Map<String, Object> login(Member vo) throws  Exception {
           Map<String, Object> result = new HashMap<String,  Object>();
           boolean flag = false;
           try {
                // 1、进行用户名和密码的验证,如果登录成功将返回flag、rid两个字段的数据
                if  (DAOFactory.getIMemberDAOInstance(this.dbc.getConnection())
                           .findLogin(vo)) {
                     // 2、更新member表中的最后一次登录日期时间
                     if  (DAOFactory.getIMemberDAOInstance(this.dbc.getConnection())
                                .doUpdateLastdate(vo.getMid()))  {
                           result.put(
                                     "unread",
                                     DAOFactory.getIMemberNewsDAOInstance(
                                                this.dbc.getConnection())
                                                .getAllCountUnread(vo.getMid()));
                           // 3、在登录日志表中进行一条数据的保存
                           Logs logs = new Logs();
                           logs.getMember().setMid(vo.getMid());
                           if (DAOFactory
                                     .getILogsDAOInstance(this.dbc.getConnection())
                                     .doCreate(logs)) {
                                // 4、根据用户的角色编号,查询出用户对应的权限组信息
                                List<Groups> allGroups =  DAOFactory
                                           .getIGroupsDAOInstance(this.dbc.getConnection())
                                           .findAllByRole(vo.getRole().getRid());
                                Set<Integer> gids = new  HashSet<Integer>();
                                // 5、根据每一个权限组的编号,查询出对应的所有权限信息
                                Iterator<Groups> iter =  allGroups.iterator();
                                // 6、根据每一个权限组的编号查询出所有的权限信息
                                while (iter.hasNext()) {
                                     Groups gup = iter.next();
                                     gids.add(gup.getGid());
                                     gup.setAction(DAOFactory.getIActionDAOInstance(
                                                this.dbc.getConnection()).findAllByGroups(
                                                gup.getGid()));
                                }
                                // 7、将权限组的信息保存在角色里面
                                vo.getRole().setGroups(allGroups);
                                flag = true;
                                result.put(
                                           "allActions",
                                           DAOFactory.getIActionDAOInstance(
                                                     this.dbc.getConnection())
                                                     .findAllByGroups(gids));
                           }
                     }
                }
           } catch (Exception e) {
                throw e;
           } finally {
                this.dbc.close();
           }
           result.put("flag", flag);
           return result;
     }
    ......
}

六 数据层模块设计实现

1 登录逻辑数据层实现在MemberDAOImpl寻找,还有一些实现需要在其他地方寻找。

package cn.mldn.crm.dao.impl;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import cn.mldn.crm.dao.IMemberDAO;
import cn.mldn.crm.dao.abs.AbstractDAOImpl;
import cn.mldn.crm.vo.Member;

public class MemberDAOImpl extends AbstractDAOImpl implements IMemberDAO {

    public MemberDAOImpl(Connection conn) {
        super(conn);
    }

    @Override
    public boolean doCreate(Member vo) throws Exception {
        String sql = "INSERT INTO member(mid,rid,password,tel,photo,locked,flag) VALUES (?,?,?,?,?,?,?)" ;
        super.pstmt = super.conn.prepareStatement(sql) ;
        super.pstmt.setString(1, vo.getMid());
        super.pstmt.setInt(2, vo.getRole().getRid());
        super.pstmt.setString(3, vo.getPassword());
        super.pstmt.setString(4,vo.getTel());
        super.pstmt.setString(5, vo.getPhoto());
        super.pstmt.setInt(6, vo.getLocked());
        super.pstmt.setInt(7, vo.getFlag());
        return super.pstmt.executeUpdate() > 0 ;
    }

    @Override
    public boolean doUpdate(Member vo) throws Exception {
        String sql = "UPDATE member SET rid=?,tel=?,locked=?,photo=? WHERE mid=?" ;
        super.pstmt = super.conn.prepareStatement(sql) ;
        super.pstmt.setInt(1, vo.getRole().getRid());
        super.pstmt.setString(2, vo.getTel());
        super.pstmt.setInt(3, vo.getLocked());
        super.pstmt.setString(4, vo.getPhoto());
        super.pstmt.setString(5, vo.getMid());
        return super.pstmt.executeUpdate() > 0 ;
    }

    @Override
    public boolean doRemove(Set<String> ids) throws Exception {
        StringBuffer buf = new StringBuffer();
        buf.append("DELETE FROM ").append("member").append(" WHERE ")
                .append("mid").append(" IN ( ");
        Iterator<String> iter = ids.iterator();
        while (iter.hasNext()) {
            buf.append("'").append(iter.next()).append("'").append(",");
        }
        buf.delete(buf.length() - 1, buf.length()).append(")");
        buf.append(" AND flag=0") ;    // 只能够删除普通用户
        this.pstmt = this.conn.prepareStatement(buf.toString());
        return this.pstmt.executeUpdate() == ids.size();
    }

    @Override
    public List<Member> findAll() throws Exception {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Member findById(String id) throws Exception {
        Member vo = null ;
        String sql = "SELECT mid,rid,tel,lastdate,photo,flag,locked FROM member WHERE mid=?" ;
        super.pstmt = super.conn.prepareStatement(sql) ;
        super.pstmt.setString(1, id);
        ResultSet rs = super.pstmt.executeQuery() ;
        if (rs.next()) {
            vo = new Member() ;
            vo.setMid(rs.getString(1));
            vo.getRole().setRid(rs.getInt(2));
            vo.setTel(rs.getString(3));
            vo.setLastdate(rs.getDate(4));
            vo.setPhoto(rs.getString(5));
            vo.setFlag(rs.getInt(6));
            vo.setLocked(rs.getInt(7));
        }
        return vo;
    }

    @Override
    public List<Member> findAllSplit(String column, String keyWord,
            Integer currentPage, Integer lineSize) throws Exception {
        List<Member> all = new ArrayList<Member>() ;
        String sql = "SELECT mid,rid,tel,lastdate,photo,flag,locked FROM member WHERE " + column + " LIKE ? LIMIT ?,?" ;
        super.pstmt = super.conn.prepareStatement(sql) ;
        super.pstmt.setString(1, "%" + keyWord + "%");
        super.pstmt.setInt(2, (currentPage - 1) * lineSize);
        super.pstmt.setInt(3, lineSize);
        ResultSet rs = super.pstmt.executeQuery() ;
        while (rs.next()) {
            Member vo = new Member() ;
            vo.setMid(rs.getString(1));
            vo.getRole().setRid(rs.getInt(2));
            vo.setTel(rs.getString(3));
            vo.setLastdate(rs.getDate(4));
            vo.setPhoto(rs.getString(5));
            vo.setFlag(rs.getInt(6));
            vo.setLocked(rs.getInt(7));
            all.add(vo) ;
        }
        return all;
    }

    @Override
    public Integer getAllCount(String column, String keyWord) throws Exception {
        return super.handleCount("member", column, keyWord);
    }

    // 查表获得用户的flag和rid,并写入到vo返回,供业务层使用
    @Override
    public boolean findLogin(Member vo) throws Exception {
        String sql = "SELECT flag,rid FROM member WHERE mid=? AND password=? AND locked=0" ;
        super.pstmt = super.conn.prepareStatement(sql) ;
        super.pstmt.setString(1, vo.getMid());
        super.pstmt.setString(2, vo.getPassword());
        ResultSet rs = super.pstmt.executeQuery() ;
        if (rs.next()) {
            vo.setFlag(rs.getInt(1));
            vo.getRole().setRid(rs.getInt(2));
            return true ;
        }
        return false;
    }

    @Override
    public boolean doUpdateLastdate(String id) throws Exception {
        String sql = "UPDATE member SET lastdate=? WHERE mid=?" ;
        super.pstmt = super.conn.prepareStatement(sql) ;
        System.out.println(new Date().getTime());
        super.pstmt.setTimestamp(1, new Timestamp(new Date().getTime()));
        super.pstmt.setString(2, id);
        return super.pstmt.executeUpdate() > 0;
    }

    @Override
    public boolean doUpdatePassword(String mid, String password)
            throws Exception {
        String sql = "UPDATE member SET password=? WHERE mid=?" ;
        super.pstmt = super.conn.prepareStatement(sql) ;
        super.pstmt.setString(1, password);
        super.pstmt.setString(2, mid);
        return super.pstmt.executeUpdate() > 0 ;
    }

}

七 参考

https://www.cnblogs.com/ylliap/p/6381478.html


版权声明:本文为chengqiuming原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。