目录
1 项目介绍
1.1 crm简介
1.2 业务流程
1.3 crm的技术架构
2 物理模型设计
2.1 crm表的结构
2.2 主键字段
2.2 外键字段
2.3 关于日期和时间的字段
3 搭建项目环境
3.1 添加maven依赖
3.2 添加配置文件
3.3 添加页面和静态资源
编辑
4 首页index
5 登录功能
5.1 登录页面
5.2 登录功能
5.2.1 添加mybatis逆向工程
5.2.2 编写service层和mapper层
5.2.3 实现controller层
5.2.4 实现页面层
5.2.5 优化登录功能
5.3 登录后显示名称
5.4 实现回车登录
5.5 记住密码功能
5.6 安全退出
5.7 登录验证功能
注意:本次项目是观看的是动力节点的视频:CRM-首页功能分析与设计_哔哩哔哩_bilibili
1 项目介绍
1.1 crm简介
CRM(Customer Relationship Management)客户关系管理是管理企业与客户之间关系的新型管理机制。终极目标是吸引新客户、保留老客户以及将已有客户转变为忠诚客户,以增加市场份额。它是一个完整的客户关系管理系统,包括市场、销售、服务3大环节,产品成熟,操作简单,功能强大。帮您从客户全生命周期的各个阶段获取价值。
1.2 业务流程
市场活动:市场部人员。
线索:销售部人员。(线索购买意愿非常强烈的时候,可以转换)
客户:线索中的公司信息
联系人:线索中的联系人信息
交易:已经促成的交易。促成的交易不一定就肯定成交,只有那些已经有交易意向的客户才创建交易记录。交易中有一些阶段,总共九个阶段,越是往下越是成交的可能性越大,当然也可能最后成交失败。
售后回访:客服人员。成交之后的。
统计报表:销售漏斗。主要体现交易数据各个阶段的数据变化。
1.3 crm的技术架构
共分为以下几层:
视图层(view):展示数据,跟用户交互。用到的技术有:html;css;jQuery;bootstrap;jsp等
控制层(Controller):控制业务处理流程(接收请求,接收参数,封装参数,根据不用的请求调用业务层处理业务;根据处理结果响应信息)。用到的技术:servlet;SpringMVC
业务层(Service):处理业务逻辑 。用到的技术:javaSE
持久层(Dao/Mapper):操作数据库。用到的技术:jdbc;mybatis
整合层:维护类资源,维护数据库资源。用到的技术:spring(IOC,AOP)(,ejb,corba)
2 物理模型设计
2.1 crm表的结构
根据业务需求我们需要创建以下数据库表:
tbl_user 用户表
tbl_dic_type 数据字典类型表
tbl_dic_value 数据字典值
tbl_activity 市场活动表
tbl_activity_remark 市场活动备注表
tbl_clue 线索表
tbl_clue_remark 线索备注表
tbl_clue_activity_relation 线索和市场活动的关联关系表
tbl_customer 客户表
tbl_customer_remark 客户备注表
tbl_contacts 联系人表
tbl_contacts_remark 联系人备注表
tbl_contacts_activity_relation 联系人和市场活动的关联关系表
tbl_tran 交易表
tbl_tran_remark 交易备注表
tbl_tran_history 交易历史表
tbl_task 任务表
2.2 主键字段
在数据库表中,如果有一组字段能够唯一确定一条记录,则可以把它们设计成表的主键字段。推荐使用一个字段做主键,而且推荐使用没有业务含义的字段做主键,比如:id等。
主键字段的类型和长度由主键值的生成方式来决定,主键值的生成方式:
(1)自增:借助数据库自身主键生成机制,数值型、长度由数据量来决定。它运行效率低但是开发效率高
(2)assighed:程序员手动值生成主键,唯一非空,使用算法,比如常见的方式:hi/low:数值型,长度由数据量决定;UUID:字符串,长度是32位。手动生成主键是我们常用的方式。
(3)共享主键:由另一张表的类型和长度决定,这种方式用的很少
(4)联合主键:由多个字段的类型和长度决定,这种方式几乎不用
2.2 外键字段
外键用来确定表和表之间的关系。
(1)一对多:一张表(A)中的一条记录可以对应另一张表(B)中的多条记录;另一张表(B)中的一条记录只能对应一张表(A)中的一条记录。
添加数据时,先添加父表记录,再添加子表记录;删除数据时,先删除子表记录,再删除父表记录;查询数据时,可能会进行关联查询:比如:查询所有姓张的学生的id,name和所在班级name
select s.id,s.name,c.name as className from tbl_student s join tbl_class c on s.class_id=c.id
where s.name like 'z%'。
我们再回顾一下关联查询的几种方式:
内连接:查询所有符合条件的数据,并且要求结果在两张表中都有相对应的记录
左外连接:查询左侧表中所有符合条件的数据,即使在右侧表中没有相对应的记录 右外连接:查询右侧条件的数表中所有符合据,即使在左侧表中没有相对应的记录
如果外键不能为空,优先使用内连接;如果外键可以为空:
--假如只需要查询那些在另一张表中有相对应的记录,使用内连接
--假如需要查询左侧表中所有符合条件的记录,使用左外连接.
(2)一对一:一张表(A)中的一条记录只能对应另一张表(B)中的一条记录;另一张表(B)中的一条记录也只能对应一张表(A)中的一条记录。
一对一这种方式用的不是很多,比如有个人类表和一个身份证表,这样就是一对一了。
a)共享主键:(不推荐)
添加数据:先添加先产生的表,再后产生的表记录
删除数据:先删除后产生的表记录,再删除先产生的表记录
查询数据:无需进行连接查询
b)唯一外键:一对一就是一种特殊的一对多。操作跟一对多完全一样。
(3)多对多:一张表(A)中的一条记录可以对应另一张表(B)中的多条记录;另一张表(B)中的一条记录也可以对应一张表(A)中的多条记录。多对多通常有三个表才能实现。比如下面三个表:
tbl_student tbl_course tbl_student_course_relation
id name id name student_id course_id
1001 zs 111 java 1001 111
1002 ls 222 mysql 1001 222 222 1002 111 111 1002 222
添加数据时,先添加父表记录(tbl_student,tbl_course),再添加子表(tbl_student_course_relation)记录;删除数据时,先删除子表记录(tbl_student_course_relation),再删除父表记录(tbl_student,tbl_course);查询数据时,可能会进行关联查询,如下://查询所有姓张的学生的id,name,和所选课程的name
select s.id,s.name,c.name as courseName from tbl_student s join tbl_student_course_relation scr on s.id=scr.student_id join tbl_course c on scr.course_id=c.id where s.name like 'z%'
2.3 关于日期和时间的字段
都按照字符串处理:char(10) yyyy-MM-dd char(19) yyyy-MM-dd HH:mm:ss
我们已经有以上的表的SQL语句,我们直接导入即可:
其中有个表的id为varchar(255) ,提示太长了,报错。我们改小一点,改成100就对了,暂时不知道为啥。
3 搭建项目环境
创建项目:crm-project
设置JDK.
创建工程:crm
补全目录结构:
最好先设置一下编码为utf-8
3.1 添加maven依赖
mysql驱动
<!-- MySQL数据库连接驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.43</version>
</dependency>
jdbc数据库连接池:druid
<!-- JDBC数据源连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.1</version>
</dependency>
mybatis框架依赖:
<!-- MyBatis框架依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
Spring相关配置依赖:
<!-- Spring框架依赖的JAR配置 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
SpringAOP的依赖:
<!-- Spring AOP支持-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
mybatis和Spring整合依赖:
<!-- MyBatis与Spring整合依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
添加项目对jsp的支持:
<!-- servlet及jstl标签库依赖的JAR配置 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-spec</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-impl</artifactId>
<version>1.2.1</version>
</dependency>
Jackson插件依赖:
<!-- 加载jackson插件依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.7.3</version>
</dependency>
POI依赖:
<!--poi依赖-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.15</version>
</dependency>
fileupload依赖:
<!-- 文件上传 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
log4j依赖:
<!-- Log4j2依赖的JAR配置 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.3</version>
</dependency>
3.2 添加配置文件
mybatis核心配置文件:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<package name="com.bjpowernode.crm.model"/>
</typeAliases>
<mappers>
<package name="com.bjpowernode.crm.mapper"/>
</mappers>
</configuration>
配置数据库连接和事务:applicationContext-datasource.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="url" value="jdbc:mysql://192.168.223.133:3306/crm_db?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
</bean>
<!-- 配置SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 必须注入属性dataSource -->
<property name="dataSource" ref="dataSource"/>
<!-- 如果mybatis没有特殊的配置(比如别名等),configLocation可以省去 ;否则,不能省略-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!-- mapper注解扫描器配置,扫描@MapperScan注解,自动生成代码对象 -->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.bjpowernode.crm.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务 -->
<aop:config>
<aop:pointcut expression="execution(* com.bjpowernode.crm..service.*.*(..))" id="allMethodPointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="allMethodPointcut"/>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="save*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="edit*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="do*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="*" propagation="REQUIRED" read-only="true"/>
</tx:attributes>
</tx:advice>
</beans>
SpringMVC配置:applicationContext-mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<!-- dispatcherServlet截获所有URL请求 -->
<mvc:default-servlet-handler />
<!-- spring mvc 扫描包下的controller -->
<context:component-scan base-package="com.bjpowernode.crm.web.controller"/>
<!-- 配置注解驱动 -->
<mvc:annotation-driven/>
<!-- 配置视图解析器 -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 配置文件上传解析器 id:必须是multipartResolver-->
<!--<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="#{1024*1024*80}"/>
<property name="defaultEncoding" value="utf-8"/>
</bean>-->
</beans>
Spring总配置文件:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 加载系统配置文件
<context:property-placeholder location="classpath:*.properties" />-->
<!-- 扫描注解 -->
<context:component-scan base-package="com.bjpowernode.crm.service" />
<!-- 导入数据相关配置 -->
<import resource="applicationContext-datasource.xml" />
</beans>
web.xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="dataservice" version="3.0">
<display-name>dataservice application</display-name>
<!-- spring监听器加载applicationContext.xml配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- spring字符过滤器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Spring mvc分发servlet -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!-- 欢迎页,默认进入index controller -->
<welcome-file-list>
<welcome-file>/</welcome-file>
</welcome-file-list>
</web-app>
设置maven对配置文件的编译选项:pom.xml
maven默认只帮我们编译java文件,配置以下内容maven可以编译我们配置的xml文件
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
需要注意的是以上配置文件还需要在修改,比如包名,可以把它当做一个模板,修改一点点就可以
3.3 添加页面和静态资源
图片之类的资源放在webapp下,页面 放在WEB-INF下。为什么呢? web应用根目录下的内容都是不安全的,外界可以通过url直接访问。所以,一般为了数据的安全,都会把页面放到WEB-INF下,因为WEB-INF目录下的资源是受保护的,外界不能直接访问。因为图片这种资源不需要保护,直接放到webapp下就行,而且如果图片也放在WEB-INF下的话,访问很麻烦,要先经过Controller层转发才行,没有那个必要。
接下来我们可以把项目部署到tomcat上:
4 首页index
理论上说我们要画流程图,但是目前我们先不画,能理解就好。
首先客户端发送请求:用户在浏览器输入url,跳转请求,我们需要一个controller接收这个请求然后跳转到首页(index),把这个网页的信息响应到浏览器,用户就可以看到了。我们写controller我们先创建包和类:
根据这个包我们需要修改之前的SpringMVC的配置:
我们写方法用于接收请求和响应网页信息:
@RequestMapping("/")
public String index(){
return "index";
}
我们启动服务器再次访问首页,发现它又去跳转登录页面。
5 登录功能
5.1 登录页面
通常来说一个资源目录对应一个controller,所以我们再创建一个controller类,而且我也不想把所有controller类放在一个包下。所以我们还可以创建一个包,这样方便我们查看也方便别人查看。那这里我们一个user路径就对应一个controller类
我们写方法:
@RequestMapping("/settings/qx/user/toLogin.do")
public String toLogin(){
return "settings/qx/user/login";
}
写登录页面:
我们发现这个前端登录页面中的../非常的多,写起来麻烦,它的意思是上一层目录。我们可以写一个通用的方式来找到路径:
<%
String basePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath()+"/";
%>
以上代码可以动态获取网页url,比如我们的url为:http://127.0.0.1:8080/crm/
再使用以下标签,它表示在本页面所有路径前面加上这个url:
<base href="<%=basePath%>">
我们再次访问:访问成功
5.2 登录功能
我们成功访问到登录界面后就开始输入账号和密码进行登录了,这里我们要注意几点:
- 用户名和密码不能为空
- 用户名或者密码错误,用户已过期,用户状态被锁定,ip受限 都不能登录成功
- 登录成功之后,所有业务页面显示当前用户的名称
- 实现10天记住密码
- 登录成功之后,跳转到业务主页面
- 登录失败,页面不跳转,提示信息
所以当我们点击“登录”的时候就要判断是否符合上面的内容,不符合是不能成功登录的。所以我们想一下登录后要进行的流程:
当用户点击“登录”或者回车后要跳转到一个controller判断是否符合条件,符合则跳转到新的页面,不符合要跳转到登录页面,总的来说就是要跳转到登录页面然后进行下一步。所以这里的controller还是用之前的那个(因为一个资源目录对应一个controller)。在controller中我们获取参数封装参数然后再向service层发送请求(因为我们要获取数据库内容来判断登录界面请求的内容是否合法),service层再向dao层发送请求执行SQL语句查询用户,结果返回给service层,再返回给controller层最后响应到前端。
5.2.1 添加mybatis逆向工程
在实现登录功能前,我们需要准备逆向工程,逆向工程是干什么的?
mybatis的逆向工程进一步简化程序员的工作量,它会根据我们的数据库表,帮我们生成对应实体类pojo类,还有mapper.xml和mapper.Java类。极大的减少了开发人员的工作量。
使用mybatis逆向工程:
(1)创建工程:crm-mybatis-generator
(2)添加插件:
<!--myBatis逆向工程插件-->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
(3)添加配置文件:包括数据库连接信息、代码保存的目录、表的信息。我们直接复制配置文件,然后修改关键信息就好。
生成的pojo类的位置
mapper映射文件
mapper接口
(4)运行mybatis的逆向工程,根据指定表生成java代码,保存到指定的目录中。
结果:
5.2.2 编写service层和mapper层
因为mybatis逆向工程只能生成部分信息,有些还需要我们自己写,比如这里我们需要查询用户名和密码信息:
mapper层
/**
* 根据用户名和密码查询用户信息
* @param map
* @return
*/
User selectUserByLoginActAndPwd(Map<String,Object> map);
<select id="selectUserByLoginActAndPwd" resultMap="BaseResultMap" parameterType="map">
select <include refid="Base_Column_List"/>
from tbl_user
where login_act=#{loginAct} and login_pwd=#{loginPwd}
</select>
service层:
public interface UserService {
User queryUserByLoginActAndPwd(Map<String,Object> map);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User queryUserByLoginActAndPwd(Map<String, Object> map) {
return userMapper.selectUserByLoginActAndPwd(map);
}
}
注意把service类交给spring容器管理,service层交给spring管理
5.2.3 实现controller层
@RequestMapping("/setting/qx/user/login.do")
@ResponseBody
public Object login(String loginAct, String loginPwd, String isRemPwd, HttpServletRequest request){
//封装参数
Map<String,Object> map = new HashMap<>();
map.put("loginAct",loginAct);
map.put("loginPwd",loginPwd);
//调用Service层方法,查询用户
User user = userService.queryUserByLoginActAndPwd(map);
//根据查询结果,生成响应信息
ReturnObject returnObject = new ReturnObject();
if (user == null){
//登录失败,用户名或密码错误
}else{
//进一步判断账户是否合法
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String nowStr = sdf.format(new Date());
if (nowStr.compareTo(user.getExpireTime())>0){
//登陆失败,账号已过期
//需要响应的信息
returnObject.setCode("0");
returnObject.setMessage("登陆失败,账号已过期");
}else if ("0".equals(user.getLockState())){
//登陆失败,状态被锁定
returnObject.setCode("0");
returnObject.setMessage("登陆失败,状态被锁定");
}else if (!user.getAllowIps().contains(request.getRemoteAddr())){
//登陆失败,ip受限
returnObject.setCode("0");
returnObject.setMessage("登陆失败,ip受限");
}else {
//登录成功
returnObject.setCode("1");
}
}
return returnObject;
}
在这一层我们接收信息然后封装到map集合,调用service层查询用户,拿得到的用户判断是否合法,其中是否合法我们要响应前端,我们写一个类封装响应的信息如下:
package com.itzw.crm.commons.domain;
public class ReturnObject {
private String code;//处理成功获取失败的标记:1---成功,0---失败
private String message;//提示信息
private Object retData;//其它数据
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getRetData() {
return retData;
}
public void setRetData(Object retData) {
this.retData = retData;
}
}
其中code为0表示不合法,1为合法,message表示不合法响应的信息。
5.2.4 实现页面层
前端我们进行表单验证,然后收集输入的信息发送给之前写的controller请求,然后controller请求会得到一个结果,我们再根据结果判断下一步怎么做,是跳转到新页面(成功)还是跳转到登录页面(失败)并给出提示信息
@Controller
public class WorkbenchIndexController {
@RequestMapping("/workbench/index.do")
public String index(){
//跳转到业务主页面
return "workbench/index";
}
}
可以访问到页面了
到这登录功能就基本完成了
5.2.5 优化登录功能
这里的值,是我们自己规定的,0表示失败,1表示成功,如果以后换表达方式呢,我们只能一个一个改,这里我们可以设置一个常量,今后修改只需要修改一处就好
修改如下:
public class Constants {
//保存ReturnObject类中的Code值
public static final String RETURN_OBJECT_CODE_SUCCESS="1";//成功
public static final String RETURN_OBJECT_CODE_FAIL="0";//失败
同样的下面格式化日期,可能我不想这样格式化,那又要全部修改一遍
我们可以写个工具类,后续修改更方便
package com.itzw.crm.commons.utils;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 对Date类型数据进行处理的工具类
*/
public class DateUtil {
/**
* 对指定的date对象进行格式化: yyyy-MM-dd HH:mm:ss
* @param date
* @return
*/
public static String formateDateTime(Date date){
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr=sdf.format(date);
return dateStr;
}
/**
* 对指定的date对象进行格式化: yyyy-MM-dd
* @param date
* @return
*/
public static String formateDate(Date date){
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
String dateStr=sdf.format(date);
return dateStr;
}
/**
* 对指定的date对象进行格式化: HH:mm:ss
* @param date
* @return
*/
public static String formateTime(Date date){
SimpleDateFormat sdf=new SimpleDateFormat("HH:mm:ss");
String dateStr=sdf.format(date);
return dateStr;
}
}
继续优化:在点击登录的时候,实际情况下可能验证很慢,和网络服务器等等都有关系,那么我们最好在验证过程中给用户提示“正在努力验证...”提升用户体验。
beforeSend:function () {//当ajax向后台发送请求之前,会自动执行本函数;
//该函数的返回值能够决定ajax是否真正向后台发送请求:
//如果该函数返回true,则ajax会真正向后台发送请求;否则,如果该函数返回false,则ajax放弃向后台发送请求。
$("#msg").text("正在努力验证....");
return true;
}
我们考虑需求,需要在点击验证之后并且在验证结束之前给出提示信息。在前端登录页面加上上面信息,它会在ajax发送请求之前自动执行本函数。
5.3 登录后显示名称
把控制层(controller)代码中处理好的数据传递到视图层(jsp),使用作用域传递:
- pageContext:用来在同一个页面的不同标签之间传递数据。
- request:在同一个请求过程中间传递数据。
- session: 同一个浏览器窗口的不同请求之间传递数据。
- application:所有用户共享的数据,并且长久频繁使用的数据。
所以在这里我们使用session域,因为当我们登录后,只要是在这个网页上,不管什么请求我们都要看到我们的登录名称在右上角挂着。
如上,这样我们就可以在前端接收,但是就像之前说的,这里的sessionUser是我们自己设置的,后期不方便修改,我们再写一个常量:
//保存当前用户的key
public static final String SESSION_USER="sessionUser";
在前端接收
5.4 实现回车登录
jquery事件函数的用法:
选择器.click(function(){//给指定的元素添加事件
//js代码
});
选择器.click();//在指定的元素上模拟发生一次事件
//给整个浏览器窗口添加键盘按下事件
$(window).keydown(function (e) {
//如果按的是回车键,则提交登录请求
if(e.keyCode==13){
//也就是执行“登录”按钮单击事件
$("#loginBtn").click();
}
});
给整个窗口都设置回车登录功能
5.5 记住密码功能
访问:login.jsp---->后台:.html:如果上次记住密码,自动填上账号和密码;否则,不填。
如何判断上次是否记住密码?`
上次登录成功,判断是否需要记住密码:如果需要记住密码,则往浏览器写cookie;否则,删除cookie。而且cookie的值必须是该用户的loginAct和loginPwd
下次登录时,判断该用户有没有cookie:如果有,则自动填写账号和密码;否则,不写。而且填写的是cookie的值。
//如果需要记住密码,则往外写cookie
if ("true".equals(isRemPwd)){
Cookie c1 = new Cookie("loginAct", loginAct);
c1.setMaxAge(10*24*60*60);
response.addCookie(c1);
Cookie c2 = new Cookie("loginPwd", loginPwd);
c1.setMaxAge(10*24*60*60);
response.addCookie(c2);
}else {
//把没有过期的cookie删除,也就是把存在时间设置为0
Cookie c1 = new Cookie("loginAct", loginAct);
c1.setMaxAge(0);
response.addCookie(c1);
Cookie c2 = new Cookie("loginPwd", loginPwd);
c1.setMaxAge(0);
response.addCookie(c2);
}
前端:要在登录界面的文本框中显示账号和密码,获取cookie
获取cookie有两种方式:
1、使用java代码获取cookie:
Cookie[] cs=request.getCookies();
for(Cookie c:cs){
if(c.getName().equals("loginAct")){
String loginAct=c.getValue();
}else if(c.getName().equals("loginPwd")){
String loginPwd=c.getValue();
}
}
2、使用EL表达式获取cookie:
${cookie.loginAct.value}
${cookie.loginPwd.value}
这里我们直接在前端使用EL表达式:
优化一下:如果选中记住密码,那么下一次访问界面不仅账户和密码已经输好,同时“十天内免登陆”的复选框也是被选中的。这里使用JSTL表达式。也就是说如果cookie中的账户和密码都是存在的,那么就选中复选框,如果有一个是空就不选中
5.6 安全退出
需求:
用户在任意的业务页面,点击"退出"按钮,弹出确认退出的模态窗口;用户在确认退出的模态窗口,点击"确定"按钮,完成安全退出的功能.
*安全退出,清空cookie,销毁session
*退出完成之后,跳转到首页
分析:
客户端用户点击退出按钮,提示是否退出,点击“确认”,发出退出请求给UserController,controller层开始清空cookie和销毁session,再跳转到首页。
@RequestMapping("/settings/qx/user/logout.do")
public String logout(HttpSession session,HttpServletResponse response){
//清空cookie
Cookie c1=new Cookie("loginAct","1");
c1.setMaxAge(0);
response.addCookie(c1);
Cookie c2=new Cookie("loginPwd","1");
c2.setMaxAge(0);
response.addCookie(c2);
//销毁session
session.invalidate();
//跳转到首页
return "redirect:/";
}
//给“确定”按钮添加单击事件
$("#logoutBtn").click(function () {
//发送同步请求
window.location.href="settings/qx/user/logout.do";
});
5.7 登录验证功能
因为目前,我们即使不登录,只要访问网页就可以访问成功,并且可以进行增删改查等操作。我们在进入网页的时候除了在登录的时候都要进行判断,判断是否登录过,如果登录过可以直接访问,如果没有登录跳转到登录界面:
登录验证有两种方式:
(1)过滤器:
a)implements Filter{
--init
--doFilter
--destroy
}
b)配置过滤器:web.xml
(2)拦截器:
a)提供拦截器类:implements HandlerInterceptor{
--pre
--post
--after
}
b)配置拦截器:springmvc.xml
这里我们使用拦截器,更方便
还记得我们之前把登录信息封装到User中,并且保存在session域中,所以我们只要访问session域查找是否存在登录信息即可
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
//如果用户没有登录则跳转到登录界面
HttpSession session = httpServletRequest.getSession();
User user = (User) session.getAttribute(Constants.SESSION_USER);
if (user == null){
//没有登录过
httpServletResponse.sendRedirect(httpServletRequest.getContextPath());
return false;//表示不继续进行下面的访问
}
return true;//表示可以继续访问
}
需要注意拦截器的三个方法的作用分别如下:
我们配置springmvc:
<mvc:interceptors>
<mvc:interceptor>
<!--拦截哪些内容-->
<mvc:mapping path="/settings/**"/>
<mvc:mapping path="/workbench/**"/>
<!--排除哪些内容,不拦截-->
<mvc:exclude-mapping path="/settings/qx/user/toLogin.do"/>
<mvc:exclude-mapping path="/settings/qx/user/login.do"/>
<!--使用那个拦截器-->
<bean class="com.itzw.crm.settings.web.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>