SSM框架项目实战-CRM(客户关系管理1)

news2025/1/20 18:20:19

目录​​​​​​​ 

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&amp;useUnicode=true&amp;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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/105932.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

超标量处理器设计——第九章_执行

参考《超标量处理器》姚永斌著 文章目录超标量处理器设计——第九章_执行9.1 概述9.2 FU类型9.2.1 ALU9.2.2 AGU9.2.3 BRU条件码分支正确性检查9.3 旁路网络9.3.1 简单设计的旁路网络9.3.2 复杂设计的旁路网络9.4 操作数的选择9.5 Cluster9.5.1 Cluster IQ9.5.2 Cluster Bypass…

Python 自动化测试(四):数据驱动

在实际的测试工作中&#xff0c;通常需要对多组不同的输入数据&#xff0c;进行同样的测试操作步骤&#xff0c;以验证我们的软件质量。这种测试&#xff0c;在功能测试中非常耗费人力物力&#xff0c;但是在自动化中&#xff0c;却比较好实现&#xff0c;只要实现了测试操作步…

前端基础_fillStyle和strokeStyle属性

fillStyle和strokeStyle属性 在前面的章节&#xff0c;在绘制图形时只用到默认的线条和填充样式。而在本节中将会探讨canvas全部的可选项&#xff0c;来绘制出更加吸引人的内容。如果想要给图形上色&#xff0c;有两个重要的属性可以做到&#xff1a;fillStyle和strokeStyle。…

社科院与杜兰大学金融管理硕士12门课程简介,其中有你心心念念的课程吗

当我们考量一个项目是否符合自身时&#xff0c;首先对课程设置是有要求的&#xff0c;课程设置是一个项目的灵魂所在&#xff0c;优质的课程与强大的师资更能体现项目的与众不同&#xff0c;下面一起去了解社科院与杜兰大学金融管理硕士项目12门必修课程概要&#xff0c;看看其…

【Java】java | maven | nexus私服 | maven私服 | docker安装nexus私服

一、说明 1、centos 7 2、docker 3、idea的maven项目 二、安装 1、拉取镜像 docker pull sonatype/nexus3:latest 2、创建映射目录并授权 mkdir /home/nexus/nexus-data && chown -R 200 /home/nexus/nexus-data 3、启动镜像 docker run -d -p 8081:8081 -p 8082:8082…

antd+vue——实现按钮始终固定在顶部,且根据权限的不同控制按钮组件的显示与隐藏——技能提升

最近在写后台管理系统&#xff0c;遇到一个小功能&#xff0c;就是一个按钮组件集合。 之前写过一篇文章&#xff0c;是关于按钮集合固定到页面顶部的文章。vue——实现页面滚动时&#xff0c;dom固定在顶部——基础积累 原理就是&#xff1a;监听页面的滚动&#xff0c;如果…

QT Qmake OpenGL osg笔记

文章目录概述QT修改样式qmake概述库引用和库路径指定QT创建动态库和使用小例子写动态库用动态库参考资料附录概述 需要先安装osg&#xff0c;然后再编译安装osgQOpenGL的插件。 其中&#xff0c;osgQOpenGL是OSG嵌入到qt中的一种实现方式&#xff0c;换言之&#xff0c;能够支…

AMBA、AHB、APB、AXI总线介绍及对比

link 一、AMBA概述AMBA (Advanced Microcontroller Bus Architecture) 高级微处理器总线架构定义了高性能嵌入式微控制器的通信标准&#xff0c;可以将RISC处理器&#xff08;精简指令集处理器&#xff09;集成在其他IP芯核和外设中&#xff0c;它是有效连接IP核的“数字胶”&a…

或许是市面上最强的 Mock 工具

背景 在开发过程中&#xff0c;由于后端与前端并行开发&#xff0c;或者前端需要等待后台开发&#xff0c;难以保证对接效率&#xff0c;同时即使用开发好的 API 对接&#xff0c;也有可能一个 API 不通就阻塞了整个软件的对接工作。同时对软件的敏感度也很高&#xff0c;一不…

冯·诺依曼:计算机硬件与软件Computer

计算机硬件与软件 计算机&#xff08;Computer&#xff09;&#xff1a;俗称电脑&#xff0c;是一种能接收和存储信息&#xff0c;并按照存储在其内部的程序对海量数据进行自动、高速地处理&#xff0c;然后把处理结果输出的现代化智能电子设备 冯诺依曼体系结构&#xff1a; …

cubeIDE开发, STM32移植GuiLite图形库开发要点

一、GuiLite GuiLite图形库&#xff0c;仅4千行C代码&#xff0c;0依赖&#xff0c;单一头文件库&#xff08;GuiLite.h&#xff09;的跨平台开源GUI库&#xff0c;支持支持的操作系统有iOS/macOS/WatchOS&#xff0c;Android&#xff0c;Linux&#xff08;ARM/x86-64&a…

Filter过滤器 | 过滤器的使用以及实现原理、责任链设计模式改造oa项目

目录 一&#xff1a;过滤器的使用以及实现原理 &#xff08;1&#xff09;当前的oa项目还存在什么缺陷&#xff1f; &#xff08;2&#xff09;Filter是什么&#xff0c;有什么用&#xff0c;执行原理是什么&#xff1f; &#xff08;3&#xff09;过滤器怎么写&#xff1f…

蓝桥杯:作物杂交 (DFS)

目录 题目描述 输入描述 输出描述 输入输出样例 输入 输出 样例说明 思路&#xff08;DFS&#xff09;&#xff1a; AC代码&#xff08;Java&#xff09;&#xff1a; 题目链接 题目描述 作物杂交是作物栽培中重要的一步。已知有 N种作物 (编号 1 至 N )&#xff0c;第…

[第十二届蓝桥杯/java/算法]B——空间

&#x1f9d1;‍&#x1f393;个人介绍&#xff1a;大二软件生&#xff0c;现学JAVA、Linux、MySQL、算法 &#x1f4bb;博客主页&#xff1a;渡过晚枫渡过晚枫 &#x1f453;系列专栏&#xff1a;[编程神域 C语言]&#xff0c;[java/初学者]&#xff0c;[蓝桥杯] &#x1f4d6…

python爬虫技术如何挣钱?教你爬虫月入三万!

前言 爬虫技术挣钱方法1:接外包爬虫项目 这是网络爬虫最通常的的挣钱方式&#xff0c;通过外包网站&#xff0c;熟人关系接一个个中小规模的爬虫项目&#xff0c;一般向甲方提供数据抓取&#xff0c;数据结构化&#xff0c;数据清洗等服务。 各位新入行的猿人看官大多都会先尝…

SpringBoot中的Aop用法

目录什么是AopAop概述相关注解相关概念实例1PointCut 表达式详解executionwithinthistargetargs:annotation:args:逻辑运算符实例2环绕通知什么是Aop 主要介绍springboot中aop的使用&#xff0c;用过Spring框架的都知道&#xff0c;aop是spring框架的两大核心功能之一&#xf…

「实操」适配 NebulaGraph 新版本与压测实践

本文来自邦盛科技-知识图谱团队-繁凡&#xff0c;本文以 NebulaGraph v3.1.0 为例。 前言 NebulaGraph v3.1 版本已经发布有一段时间了&#xff0c;但是我们的项目之前是基于 v2.6.1 版本开发的&#xff0c;由于一直在做功能相关的工作&#xff0c;所以一直没有对图库进行升级…

一个简单的步骤让你的 Python 代码更干净

说起来容易做起来难&#xff0c;我们都知道代码可读性非常重要&#xff0c;但是写的时候总是随心所欲&#xff0c;不考虑类型提示、import 排序、PEP8 规范。今天分享一个小技巧&#xff0c;通过一个简单的步骤就可以让你的 Python 代码更干净。 这就是 pre-commit&#xff1a…

pyQt5和pySide2 环境配置以及部分问题解决方案

首先介绍需要安装的内容&#xff1a; 1、Qt 2、python3.7.5&#xff08;查找资料过程中有注意到说是3.8及3.8以上版本Qt可能不兼容&#xff09;【记得勾选添加到环境变量中选项】 3、pip&#xff08;主要用来下载使用&#xff09; 4、pySide2、PyQt5、pyqt5-tools 5、如果有使…

vue 前端国际化方案

方案介绍&#xff1a; 1.使用i18n&#xff08;使用步骤可百度&#xff0c;要注意版本问题&#xff09; 2.整合ele-ui的国际化方案&#xff08;百度&#xff0c;几行添加一下即可&#xff09; 3.数据信息的配置&#xff08;重头戏&#xff0c;以下详细介绍&#xff09;excel转j…