黑馬客戶管理系統(SSM)

来源:https://www.cnblogs.com/maying1995/archive/2019/12/31/12126317.html
-Advertisement-
Play Games

黑馬客戶管理系統 1系統概述 1.1系統功能介紹 本系統後臺使用SSM框架編寫,前臺頁面使用當前主流的Bootstrap和jQuery框架完成頁面信息展示功能(關於Bootstrap的知識,有興趣的讀者可參考黑馬程式員編著的《響應式Web開發項目教程》)。系統中主要實現了兩大功能模塊:用戶登錄模塊和 ...


黑馬客戶管理系統

1系統概述

1.1系統功能介紹

本系統後臺使用SSM框架編寫,前臺頁面使用當前主流的Bootstrap和jQuery框架完成頁面信息展示功能(關於Bootstrap的知識,有興趣的讀者可參考黑馬程式員編著的《響應式Web開發項目教程》)。系統中主要實現了兩大功能模塊:用戶登錄模塊和客戶管理模塊,這兩個模塊的主要功能如圖18-1所示。

1.2系統架構設計

本系統根據功能的不同,項目結構可以劃分為以下幾個層次。

· 持久對象層(也稱持久層或持久化層):該層由若幹持久化類(實體類)組成。

· 數據訪問層(DAO層):該層由若幹DAO介面和MyBatis映射文件組成。介面的名稱統一以Dao結尾,且MyBatis的映射文件名稱要與介面的名稱相同。

· 業務邏輯層(Service層):該層由若幹Service介面和實現類組成。在本系統中,業務邏輯層的介面統一使用Service結尾,其實現類名稱統一在介面名後加Impl。該層主要用於實現系統的業務邏輯。

· Web表現層:該層主要包括Spring MVC中的Controller類和JSP頁面。Controller類主要負責攔截用戶請求,並調用業務邏輯層中相應組件的業務邏輯方法來處理用戶請求,然後將相應的結果返回給JSP頁面。為了讓讀者更清晰地瞭解各個層次之間的關係,下麵通過一張圖來描述各個層次的關係和作用,如圖18-2所示。

1.3文件組織結構

在正式講解項目的編寫之前,先來瞭解項目中所涉及的包文件、配置文件以及頁面文件等在項目中的組織結構,如圖18-3所示。

1.4系統開發及運行環境

BOOT客戶管理系統開發環境如下。

· 操作系統:Windows

· Web伺服器:Tomcat 8.0

· Java開發包:JDK8

· 開發工具:Eclipse Java EE IDE for Web Developers

· 資料庫:MySQL 5.5

· 瀏覽器:火狐或IE 8.0以上版本

2資料庫設計

本系統中主要涉及用戶登錄和客戶管理功能,因此在系統中會涉及系統用戶表和客戶信息表。除此之外,客戶信息中的客戶來源和所屬行業等內容是根據數據字典表中的信息查詢出的,所以還會涉及一個數據字典表。這3張表的表結構如表18-1、表18-2和表18-3所示。

3系統環境搭建

3.1準備所需JAR包

由於本系統使用的是SSM框架開發,因此需要準備這三大框架的JAR包。除此之外,項目中還涉及資料庫連接、JSTL標簽等,所以還要準備其他JAR包。整個系統所需要準備的JAR共計35個,具體如下所示。

1.Spring框架所需的JAR包(10個)

主要包括4個核心模塊JAR, AOP開發使用的JAR, JDBC和事務的JAR。

· aopalliance-1.0.jar

· aspectjweaver-1.8.10.jar

· spring-aop-4.3.6.RELEASE.jar

· spring-aspects-4.3.6.RELEASE.jar

· spring-beans-4.3.6.RELEASE.jar

· spring-context-4.3.6.RELEASE.jar

· spring-core-4.3.6.RELEASE.jar

· spring-expression-4.3.6.RELEASE.jar

· spring-jdbc-4.3.6.RELEASE.jar

· spring-tx-4.3.6.RELEASE.jar

2.Spring MVC框架所需要的JAR包(2個)

· spring-web-4.3.6.RELEASE.jar

· spring-webmvc-4.3.6.RELEASE.jar

3.MyBatis框架所需的JAR包(13個)主要包括核心包mybatis-3.4.2.jar,以及其解壓文件夾中lib目錄下的所有JAR。

· ant-1.9.6.jar

· ant-launcher-1.9.6.jar

· asm-5.1.jar· cglib-3.2.4.jar

· commons-logging-1.2.jar

· javassist-3.21.0-GA.jar

· log4j-1.2.17.jar

· log4j-api-2.3.jar

· log4j-core-2.3.jar

· mybatis-3.4.2.jar

· ognl-3.1.12.jar

· slf4j-api-1.7.22.jar

· slf4j-log4j12-1.7.22.jar

4.MyBatis與Spring整合的中間JAR(1個)

· mybatis-spring-1.3.1.jar

5.資料庫驅動JAR包(1個)

· mysql-connector-java-5.1.40-bin.jar

6.數據源dbcp所需JAR包(2個)

· commons-dbcp2-2.1.1.jar

· commons-pool2-2.4.2.jar

7.JSTL標簽庫JAR包(2個)

· taglibs-standard-impl-1.2.5.jar

· taglibs-standard-spec-1.2.5.jar

8.Jackson框架所需JAR包(3個)

· jackson-annotations-2.8.6.jar

· jackson-core-2.8.6.jar

· jackson-databind-2.8.6.jar

9.Java工具類JAR(1個)

· commons-lang3-3.4.jar

上面所需要準備的JAR包(除JSTL的2個JAR包和commons-lang3-3.4.jar外),都是本書前面章節所使用過的。讀者在學習本章時,可以直接下載項目源碼,並使用源碼中的JAR包。

小提示

本書中使用的JSTL版本是1.2.5,此版本中需要引入的JAR包為taglibs-standard-spec-1.2.5.jar(相當於之前的jstl.jar,屬於介面定義類)和taglibs-standard-impl-1.2.5.jar jar(相當於之前的standard.jar,屬於實現類)。這兩個JAR包可以通過網址“http://tomcat.apache.org/download-taglibs.cgi#Standard-1.2.5”下載得到。

3.2準備資料庫資源

通過MySQL 5.5 Command Line Client登錄資料庫後,創建一個名稱為boot_crm的資料庫,並選擇該資料庫。通過SQL命令將本書資源中所提供的boot_crm.sql文件導入到boot_crm資料庫中,即可導入本系統所使用的全部數據,其具體實現SQL命令如下。

(1)創建資料庫。

create database boot_crm;

(2)選擇所創建的資料庫。

use boot_crm;

(3)導入資料庫文件,這裡假設該文件在F盤的根目錄下,其導入命令如下。

source F:\boot_crm.sql;

除此之外,還可以通過其他客戶端軟體導入sql文件,如SQLyog等。

創建crm資料庫,執行sql

3.3準備項目環境

1創建項目,引入JAR包

在Eclipse中,創建一個名稱為boot-crm的Web項目,將系統所準備的全部JAR包複製到項目的lib目錄中,併發布到類路徑下。

2編寫配置文件

(1)在項目目錄下創建一個源文件夾config,併在config文件夾下分別創建資料庫常量配置文件、Spring配置文件、MyBatis配置文件、log4j配置文件、資源配置文件以及Spring MVC配置文件。其中log4j配置文件log4j.properties、資料庫常量配置文件jdbc.properties與MyBatis配置文件mybatis-config.xml的配置與第17章講解整合時的配置代碼基本相同(註意修改資料庫名稱與包名),這裡將不再重覆講解。其他3個配置文件的代碼分別如文件18-1、文件18-2和文件18-3所示。

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>

    <!-- 別名 -->
    <typeAlises>
        <package name="com.itheima.core.pojo"/>
    </typeAlises>    
    
</configuration>
jdbc.properties

配置資料庫信息

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/crm?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root
log4j.properties

配置日誌信息

# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
applicationContext.xml

需要配置:

載入properties文件,數據源,SqlSessionFactory,Mapper掃描

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    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:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

    <!-- 配置 讀取properties文件 jdbc.properties -->
    <context:property-placeholder location="classpath:jdbc.properties" />

    <!-- 配置 數據源 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!-- 資料庫驅動 -->
        <property name="driverClassName" value="${jdbc.driver}" />
        <!-- 連接資料庫的url -->
        <property name="url" value="${jdbc.url}" />
        <!-- 連接資料庫的用戶名 -->
        <property name="username" value="${jdbc.username}" />
        <!-- 連接資料庫的密碼 -->
        <property name="password" value="${jdbc.password}" />
    </bean>
    
    <!-- 配置Mybatis的工廠 SqlSessionFactory -->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 設置MyBatis核心配置文件所在位置 -->
        <property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml" />
        <!-- 設置數據源 -->
        <property name="dataSource" ref="dataSource" />
    </bean>
    
        <!-- 事務管理器 -->
    <bean id="transactionManager"   class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 數據源 -->
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 通知 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- 傳播行為 -->
            <tx:method name="save*" propagation="REQUIRED" />
            <tx:method name="insert*" propagation="REQUIRED" />
            <tx:method name="add*" propagation="REQUIRED" />
            <tx:method name="create*" propagation="REQUIRED" />
            <tx:method name="delete*" propagation="REQUIRED" />
            <tx:method name="update*" propagation="REQUIRED" />
            <tx:method name="find*" propagation="SUPPORTS" read-only="true" />
            <tx:method name="select*" propagation="SUPPORTS" read-only="true" />
            <tx:method name="get*" propagation="SUPPORTS" read-only="true" />
            <tx:method name="query*" propagation="SUPPORTS" read-only="true" />
        </tx:attributes>
    </tx:advice>

    <!-- 切麵 -->
    <aop:config>
        <aop:advisor advice-ref="txAdvice"
            pointcut="execution(* cn.itcast.crm.service.*.*(..))" />
    </aop:config>
    
        <!-- 配置Mapper掃描 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 設置Mapper掃描包 -->
        <property name="basePackage" value="com.itheima.crm.mapper" />
    </bean>
    
    <!-- 配置Service掃描 -->
    <context:component-scan base-package="com.itheima.crm.service" />
    
</beans>

上述代碼與上一章整合時的配置文件代碼有所不同的是增加了事務傳播行為以及切麵的配置。在事務的傳播行為中,只有查詢方法的事務為只讀,添加、修改和刪除的操作必須納入事務管理。

resource.properties
#客戶來源
CUSTOMER_FROM_TYPE=002
#客戶行業
CUSTOMER_INDUSTRY_TYPE=001
#客戶級別
CUSTOMER_LEVEL_TYPE=006

上述配置代碼分別表示客戶來源、所屬行業和客戶級別,其值對應的是數據字典表中dict_type_code欄位的值。

springmvc-config.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:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    
    <!-- 配置Controller掃描 -->
    <context:component-scan base-package="com.itheima.crm.controller" />
    
    <!-- 載入屬性文件 controller需要的配置信息 -->
    <context:property-placeholder location="classpath:resource.properties" />

    <!-- 配置註解驅動:處理器映射器和適配器-->
    <mvc:annotation-driven />
    
    <!-- 對靜態資源放行,此配置中的文件,將不被前端控制器攔截-->
    <mvc:resources location="/css/" mapping="/css/**"/>
    <mvc:resources location="/js/" mapping="/js/**"/>
    <mvc:resources location="/fonts/" mapping="/fonts/**"/>
    
    <!-- 另外一種方式 解決靜態資源無法被springMVC處理的問題 -->
    <mvc:default-servlet-handler />


    <!-- 配置視圖解析器 -->
    <bean   class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 首碼 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- 尾碼 -->
        <property name="suffix" value=".jsp" />
    </bean>
    
</beans>

上述代碼除配置了需要掃描的包、註解驅動和視圖解析器外,還增加了載入屬性文件和訪問靜態資源的配置。

(2)在web.xml中,配置Spring的監聽器、編碼過濾器和SpringMVC的前端控制器等信息,如文件18-4所示。

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_2_5.xsd"
    id="WebApp_ID" version="2.5">
    <display-name>ssm</display-name>
    <!-- 系統預設頁面-->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    <!-- 配置spring -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/applicationContext.xml</param-value>
    </context-param>

    <!-- 配置監聽器載入spring -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- 配置編碼過濾器,解決post的亂碼問題 -->
    <filter>
        <filter-name>encoding</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>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 配置SpringMVC前端核心控制器 -->
    <servlet>
        <servlet-name>crm</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/springmvc.xml</param-value>
        </init-param>
        <!-- 配置springmvc什麼時候啟動,參數必須為整數 -->
        <!-- 如果為0或者大於0,則springMVC隨著容器啟動而啟動 -->
        <!-- 如果小於0,則在第一次請求進來的時候啟動 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>crm</servlet-name>
        <!-- 所有的請求都進入springMVC -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

3.引入頁面資源將項目運行所需要的CSS文件、字體、圖片、JS、自定義標簽文件和JSP文件按照圖18-3中的結構引入到項目中。

至此,開發系統前的環境準備工作就已經完成。此時如果將項目發佈到Tomcat伺服器並訪問項目首頁地址http://localhost:8080/boot-crm/index.jsp,如圖18-4所示。

從圖18-4可以看出,訪問系統首頁時,頁面所展示的是系統登錄頁面。在下一節中,我們將對系統的登錄功能編寫進行詳細講解。

4用戶登錄模塊

4.1用戶登錄

BOOT客戶管理系統用戶登錄功能的實現流程如圖18-5所示。

從圖18-5可以看出,用戶登錄過程中首先要驗證用戶名和密碼是否正確,如果正確,可以成功登錄系統,系統會自動跳轉到主頁;如果錯誤,則在登錄頁面給出錯誤提示信息。

下麵就依照圖18-5中的流程,來實現系統登錄功能,具體步驟如下。

1創建持久化類

在src目錄下,創建一個com.itheima.crm. pojo包,在包中創建用戶持久化類User,併在User類中定義用戶相關屬性以及相應的getter/setter方法,如文件18-5所示。

文件18-5 User.java

package com.itheima.core.pojo;
import java.io.Serializable;
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    private Integer user_id; // 用戶id
    private String user_code; // 用戶賬戶
    private String user_name; // 用戶名稱
    private String use_password; // 用戶密碼
    // getter/setter方法
}

2實現DAO

(1)創建用戶DAO層介面。在src目錄下,創建一個com.itheima.crm.dao包,在包中創建一個用戶介面UserDao,併在介面中編寫通過賬號和密碼查詢用戶的方法,如文件18-6所示。

文件18-6 UserDao.java

public interface UserDao {
    
    /**
     * 通過賬戶和密碼查詢用戶
     * @param usercode
     * @param password
     * @return
     */
    public User findUser(@Param("usercode") String usercode,@Param("password")String password);
}

在上述方法代碼的參數中,@Param("usercode")表示為參數usercode命名,命名後,在映射文件的SQL中,使用#{usercode}就可以獲取usercode的參數值。

(2)創建映射文件。在com.itheima.core.dao包中,創建一個MyBatis映射文件UserDao.xml,併在映射文件中編寫查詢用戶信息的執行語句,如文件18-7所示。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.core.dao.UserDao">
    <!-- 查詢用戶 -->
    <select id="findUser" parameterType="String" resultType="User">
        select * 
        from sys_user
        where user_code = #{usercode}
        and user_password = #{password}
        and user_state = '1'
    </select>
</mapper>

上述代碼通過映射查詢語句來查詢系統用戶表中的可用用戶。

3實現Service

(1)創建用戶Service層介面。在src目錄下,創建一個com.itheima.core.service包,在包中創建UserService介面,併在該介面中編寫一個通過賬號和密碼查詢用戶的方法,如文件18-8所示。

文件18-8 UserService.java

public interface UserService {
    /**
     * 通過賬號和密碼查詢用戶
     * @param usercode
     * @param password
     * @return
     */
    public User findUser(String usercode,String password);
}

(2)創建用戶Service層介面的實現類。在src目錄下,創建一個com.itheima.core.service. impl包,併在包中創建UserService介面的實現類UserServiceImpl,在類中編輯並實現介面中的方法,如文件18-9所示。

文件18-9 UserServiceImpl.java

@Service
@Transactional
public class UserServiceImpl implements UserService {
    // 註入Userdao
    @Autowired
    private UserDao userdao;    
    @Override
    public User findUser(String usercode, String password) {
        User user=this.userdao.findUser(usercode, password);
        return user;
    }
}

在上述代碼的findUser()方法中,調用了UserDao對象中的findUser()方法來查詢用戶信息,並將查詢到的信息返回。

4實現Controller

在src目錄下,創建一個com.itheima.core.web.controller包,在包中創建用戶控制器類UserController,編輯後的代碼如文件18-10所示。

文件18-10 UserController.java

public class UserController {

    @Autowired
    private UserService userService;
    
    /**
     * 用戶登錄
     * @param usercode
     * @param password
     * @param model
     * @param session
     * @return
     */
    @RequestMapping(value="/login",method=RequestMethod.POST)
    public String login(String usercode,String password,Model model,HttpSession session) {
        
        User user= userService.findUser(usercode,password);
        
        if(user!=null) {
            //將用戶對象添加到Session
            session.setAttribute("USER_SESSION", user);
            // 跳轉到主頁面
            return "customer";
        }
        model.addAttribute("msg", "賬號或密碼錯誤,請重新登錄!");
        // 返回到登錄頁面
        return "login";             
    }
}

在文件18-10中,首先通過@Autowired註解將UserService對象註入到了本類中,然後創建了一個用於用戶登錄的login()方法。由於在用戶登錄時,表單都會以POST方式提交,所以將@RequestMapping註解的method屬性值設置為RequestMethod.POST。在login()方法中,首先通過頁面中傳遞過來的賬號和密碼查詢用戶,然後通過if語句判斷是否存在該用戶。如果存在,就將用戶信息存儲到Session中,並跳轉到系統主頁面;如果不存在,則提示錯誤信息,並返回到登錄頁面。

5實現頁面功能

(1)系統預設首頁index.jsp主要實現了一個轉發功能,在訪問時會轉發到登錄頁面,其實現代碼如文件18-11所示。

文件18-11 index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <!-- 轉發到登錄頁面 -->
    <jsp:forward page="/WEB-INF/jsp/login.jsp"></jsp:forward>
</body>
</html>

(2)登錄頁面中,主要包含一個登錄表單,其頁面實現代碼如文件18-12所示。

文件18-12 login.jsp

在文件18-12中,核心代碼是用戶登錄操作的form表單,該表單在提交時會通過check()方法檢查賬戶或密碼是否為空,如果為空,則通過標簽提示用戶“賬號或密碼不能為空!”;如果賬號和密碼都已填寫,則將表單提交到以“/login.action”結尾的請求中。

6啟動項目,測試登錄

將項目發佈到Tomcat伺服器並啟動,成功訪問登錄頁面後,即可輸入賬號和密碼登錄系統。在執行登錄操作之前,先查看一下資料庫中sys_user表中的數據,如圖18-6所示。

從圖18-6可以看出,表sys_user中包含4個賬號(user_code)以m開頭的用戶信息。此時在登錄頁面中輸入賬號“m0001”和密碼“123”,單擊“登錄”按鈕後,瀏覽器的顯示結果如圖18-7所示。

從圖18-7可以看出,系統已經成功進入客戶管理頁面,這說明系統登錄成功。此時由於項目中並沒有實現客戶查詢功能,所以客戶列表中沒有任何數據。

4.2 實現登錄驗證

雖然在18.4.1節中已經實現了用戶登錄功能,但是此功能還並不完善。假設在其他控制器類中也包含一個訪問客戶管理頁面的方法,那麼用戶完全可以繞過登錄步驟,而直接通過訪問該方法的方式進入客戶管理頁面。為了驗證上述內容,我們可以在用戶控制器類UserController中編寫一個跳轉到客戶管理頁面的方法,其代碼如下所示。

    /**
     * 模擬其他類中跳轉到客戶管理頁面的方法
     * @return
     */
    @RequestMapping(value="/toCustomer.action")
    public String toCustomer() {
        return "customer";
    }

此時,如果通過瀏覽器訪問地址http://localhost:8080/boot-crm/toCustomer.action,瀏覽器就會直接顯示客戶管理頁面

顯然,讓未登錄的用戶直接訪問到客戶管理頁面,是十分不安全的。為了避免此種情況的發生,並提升系統的安全性,我們可以創建一個登錄攔截器來攔截所有請求。只有已登錄用戶的請求才能夠通過,而對於未登錄用戶的請求,系統會將請求轉發到登錄頁面,並提示用戶登錄,其執行流程如圖18-9所示。

實現用戶登錄驗證的具體過程如下。

1創建登錄攔截器類

在src目錄下,創建一個com.itheima.core.interceptor包,併在包中創建登錄攔截器類LoginInterceptor,來實現用戶登錄的攔截功能,編輯後如文件18-13所示。

文件18-13 LoginInterceptor.java

public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception {
        
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 獲取請求的URL
        String url=request.getRequestURI();
        // URL:除了登錄請求外,其他的URL都進行攔截控制
        if(url.indexOf("/login.action")>=0) {
            return true;
        }
        // 獲取session
        HttpSession session = request.getSession();
        User user = (User)session.getAttribute("USER_SESSION");
        // 判斷Session中是否有用戶數據,如果有,則返回true,繼續向下執行
        if(user!=null) {
            return true;
        }
        // 不符合條件的給出提示信息,並轉發到登錄頁面
        request.setAttribute("msg", "您還沒有登錄,請先登錄!");
        request.getRequestDispatcher("/WEB_INF/jsp/login.jsp").forward(request, response);
        return false;
    }
}

在文件18-13的preHandle()方法中,首先獲取了用戶URL請求,然後通過請求來判斷是否為用戶登錄操作,只有對用戶登錄的請求才不進行攔截。接下來獲取了Session對象,並獲取Session中的用戶信息。如果Session中的用戶信息不為空,則表示用戶已經登錄,攔截器將放行;如果Session中的用戶信息為空,則表示用戶未登錄,系統會轉發到登錄頁面,並提示用戶登錄。

2配置攔截器

在springmvc-config.xml文件中,配置登錄攔截器信息,其配置代碼如下。

<!-- 配置攔截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.itheima.core.interceptor.LoginInterceptor"/>       
    </mvc:interceptor>  
</mvc:interceptors>

上述配置代碼會將所有的用戶請求都交由登錄攔截器來處理。至此,登錄攔截器的實現工作就已經完成。

發佈項目並啟動Tomcat伺服器後,再次通過瀏覽器訪問地址http://localhost:8080/boot-crm/toCustomer.action時,瀏覽器的顯示結果如圖18-10所示。

從圖18-10可以看出,未登錄的用戶在執行訪問客戶管理頁面方法後,並沒有成功跳轉到客戶管理頁面,而是轉發到了系統登錄頁面,同時在頁面的登錄視窗中也給出了提示信息。這也就說明用戶登錄驗證功能已成功實現。

4.3退出登錄

用戶登錄模塊中還包含一個功能——退出登錄。成功登錄後的用戶會跳轉到客戶管理頁面,並且在頁面中會顯示已登錄的用戶名稱,如圖18-11所示。

從圖18-11可以看出,頁面的右上角中已經顯示了登錄用戶“小韓”,並且彈出列表框最下方為“退出登錄”。那麼要如何實現“退出登錄”功能呢?

在customer.jsp頁面中,圖18-11中彈出列表框的實現代碼如下。

            <ul class="dropdown-menu dropdown-user">
                <li><a href="#"><i class="fa fa-user fa-fw"></i>
                               用戶:${USER_SESSION.user_name}
                    </a>
                </li>
                <li><a href="#"><i class="fa fa-gear fa-fw"></i> 系統設置</a></li>
                <li class="divider"></li>
                <li>
                    <a href="${pageContext.request.contextPath }/logout.action">
                    <i class="fa fa-sign-out fa-fw"></i>退出登錄
                    </a>
                </li>
            </ul>

從上述代碼中可以看出,顯示的登錄用戶名稱是通過EL表達式從Session中獲取的,而單擊“退出登錄”鏈接時,會提交一個以“/logout.action”結尾的請求。為了完成退出登錄功能,我們需要在用戶控制器類中編寫一個退出登錄的方法。在方法執行時,需要清除Session中的用戶信息,並且在退出登錄後,系統要返回到登錄頁面。因此,需要在用戶控制器類UserController中編寫退出登錄和返回到登錄頁面的方法,這兩個方法的實現代碼如下。

    /**
     * 退出登錄
     * @param session
     * @return
     */
    @RequestMapping(value="/logout.action")
    public String logout(HttpSession session) {
        // 清除Session
        session.invalidate();
        // 重定向到登錄頁面的跳轉方法
        return "redirect:login.action";
    }
    
    /**
     * 向用戶登錄頁面跳轉
     * @return
     */
@RequestMapping(value="/logout.action",method= RequestMethod.GET)
    public String tologin() {
        return "login";
    }

至此,“退出登錄”的功能代碼就已經編寫完成。重啟項目並登錄系統後,單擊圖18-11中的“退出登錄”即可退出系統。

5客戶管理模塊

客戶管理模塊是本系統的核心模塊,該模塊中實現了對客戶的查詢、添加、修改和刪除功能。在接下來的幾個小節中,將對這幾個功能的實現進行詳細講解。

5.1 查詢客戶

在實際應用中,無論是企業級項目,還是互聯網項目,使用最多的一定是查詢操作。不管是在列表中展示所有數據的操作,還是對單個數據的修改或者刪除操作,都需要先查詢並展示出資料庫中的數據。

查詢操作通常可以分為按條件查詢和查詢所有,但在實際使用時,我們可以將這兩種查詢編寫在一個方法中使用,即當有條件時,就按照條件查詢;當沒有條件時,就查詢所有。同時,由於資料庫中的數據可能有很多,如果讓這些數據在一個頁面中全部顯示出來,勢必會使頁面數據的可讀性變得很差,所以我們還需要考慮將這些數據進行分頁查詢顯示。

綜合上述分析以及客戶頁面的顯示功能,BOOT客戶管理系統的查詢功能需要實現的功能如圖18-12所示。

從圖18-12可以看出,客戶管理模塊中的查詢可分為按照條件查詢和分頁查詢,這兩種查詢操作所查詢出的數據都會顯示在客戶信息列表中。如果未選擇任何條件,那麼客戶信息列表將分頁查詢顯示出所有數據。我們要如何實現客戶的條件查詢和分頁查詢呢?下麵將對客戶管理中的查詢功能實現進行詳細講解,具體步驟如下。

分析:

  1. 前臺發起請求,需要接收請求過來的查詢條件數據,可以使用pojo接收數據,編寫QueryVo,裡面包含查詢條件屬性和分頁數據。
  2. 前臺需要分頁顯示,根據準備好的分頁實現,應該返回分頁類Page,而創建Page分頁類需要數據總條數,所以也需要查詢數據總條數的邏輯。

根據分析,DAO需要編寫兩個方法:

  1. 需要根據條件分頁查詢客戶信息
  2. 需要根據條件查詢數據總條數

1創建持久化類

在com.itheima.core.pojo包中,創建客戶持久化類、數據字典持久化類、查詢條件包裝類,編輯後如文件18-14和文件18-15所示。

文件18-14 Customer.java

public class Customer implements Serializable{
    private static final long serialVersionUID = 1L;    
    private Long cust_id;// 客戶編號
    private String cust_name;// 客戶名稱
    private Long cust_user_id;// 負責人id
    private Long cust_create_id;// 創建人id
    private String cust_source;// 客戶信息來源
    private String cust_industry;// 客戶所屬行業
    private String cust_level;// 客戶級別
    private String cust_linkman;// 聯繫人
    private String cust_phone;// 固定電話
    private String cust_mobile;// 行動電話 
    private String cust_zipcode;// 郵政編碼
    private String cust_address;// 聯繫地址
    private Date cust_createtime;// 創建時間
    //getter setter方法
}

在文件18-14中,聲明瞭與客戶數據表對應的屬性並定義了各個屬性的getter/setter方法。

文件18-15 BaseDict.java

public class BaseDict implements Serializable {
    
    private static final long serialVersionUID = 1L;
    private String dict_id;// 數據字典id
    private String dict_type_code;// 數據字典類別代碼
    private String dict_type_name;// 數據字典類別名稱
    private String dict_item_name; // 數據字典項目名稱
    private String dict_item_code;// 數據字典項目代碼
    private Integer dict_sort;// 排序欄位
    private String dict_enable;//  是否可用
    private String dict_memo;// 備註
     //getter setter方法
}

在文件18-15中,聲明瞭與數據字典表對應的屬性並定義了各個屬性的getter/setter方法。

QueryVo.java

public class QueryVo {   
    private String custName;// 客戶名稱
    private String custSource; // 客戶來源 
    private String custIndustry;// 所屬行業 
    private String custLevel;// 客戶級別   
    private Integer page = 1;// 當前頁碼數  預設查詢第1頁  
    private Integer start;// 起始行  資料庫從哪一條數據開始查 
    private Integer rows = 10; // 所取行數  每頁顯示數據條數 
    //getter setter方法
}

受請求參數的QueryVo,裡面包含查詢條件屬性和分頁數據。需要註意的是,屬性中的star和rows用於執行分頁操作,其中start表示分頁操作中的起始行,而rows則表示分頁中所選取的行數。

2實現DAO層

(1)創建客戶DAO層介面和映射文件。

在com.itheima.core.dao包中,創建一個CustomerDao介面,併在介面中編寫查詢客戶列表和客戶總數的方法,然後創建一個與介面同名的映射文件,如文件18-16和文件18-17所示。

文件18-16 CustomerDao.java

public interface CustomerDao {
    /**
     * 根據queryVo分頁查詢數據
     * 
     * @param queryVo
     * @return
     */
    List<Customer> queryCustomerByQueryVo(QueryVo queryVo);

    /**
     * 根據queryVo查詢數據條數
     * 
     * @param queryVo
     * @return
     */
    Integer queryCountByQueryVo(QueryVo queryVo);
}

文件18-17 CustomerDao.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.core.dao.CustomerDao">
    <!-- SQL片段-->
    <sql id="customerQueryVo">
        <where>
            <if test="custName != null and custName != ''">
                AND a.cust_name LIKE '%${custName}%'
            </if>
            <if test="custSource != null and custSource != ''">
                AND a.cust_source = #{custSource}
            </if>
            <if test="custIndustry != null and custIndustry != ''">
                AND a.cust_industry = #{custIndustry}
            </if>
            <if test="custLevel != null and custLevel != ''">
                AND a.cust_level = #{custLevel}
            </if>
        </where>
    </sql>

    <!-- 根據queryVo分頁查詢數據 -->
    <select id="queryCustomerByQueryVo" parameterType="com.itheima.core.pojo.QueryVo"
        resultType="com.itheima.core.pojo.Customer">
      SELECT
        a.cust_id,
        a.cust_name,
        a.cust_user_id,
        a.cust_create_id,
        b.dict_item_name cust_source,
        c.dict_item_name cust_industry,
        d.dict_item_name cust_level,
        a.cust_linkman,
        a.cust_phone,
        a.cust_mobile,
        a.cust_zipcode,
        a.cust_address,
        a.cust_createtime
      FROM
        customer a
        LEFT JOIN base_dict b ON a.cust_source = b.dict_id
        LEFT JOIN base_dict c ON a.cust_industry = c.dict_id
        LEFT JOIN base_dict d ON a.cust_level = d.dict_id
        <include refid="customerQueryVo" />
        <!-- 執行分頁查詢-->
        <if test="start != null">
            LIMIT #{start}, #{rows}
        </if>
    </select>

    <!-- 根據queryVo查詢數據條數 -->
    <select id="queryCountByQueryVo" parameterType="com.itheima.core.pojo.QueryVo"
        resultType="integer">
        SELECT count(1) FROM customer a
        <include refid="customerQueryVo" />
    </select>
</mapper>

在文件18-17中,首先編寫了一個SQL片段來作為映射查詢客戶信息的條件,然後編寫了查詢所有客戶的映射查詢方法。在方法的SQL中,分別通過左外連接的方式從數據字典表base_dict中的類別代碼欄位查詢出了相應的類別信息,同時通過limit來實現數據的分頁查詢。最後編寫了一個查詢客戶總數的映射查詢語句用於分頁使用。

(2)創建數據字典DAO層介面和映射文件。

在com.itheima.core.dao包中,創建一個BaseDictDao介面,併在介面中編寫根據類別代碼查詢數據字典的方法,然後創建一個與介面同名的映射文件,如文件18-18和文件18-19所示

文件18-18 BaseDictDao.java

public interface BaseDictDao {
    
    /**
     * 根據類別代碼查詢數據
     * @param dictTypecode
     * @return
     */
    public List<BaseDict> queryBaseDictByDictTypeCode(String dictTypecode);
    
}

文件18-19 BaseDictDao.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.core.dao.BaseDictDao">
    <!-- 根據類別代碼查詢數據 -->
    <select id="queryBaseDictByDictTypeCode" parameterType="String"
        resultType="com.itheima.core.pojo.BaseDict">
        SELECT * FROM base_dict WHERE dict_type_code =
        #{dict_type_code}
    </select>   
</mapper>

3實現Service層

(1)引入分頁標簽類。在src目錄下,創建一個com.itheima.common.utils包,在包中引入分頁時使用的標簽類文件Page.java和NavigationTag.java,這兩個文件可直接從源代碼中獲取,其具體實現代碼如文件18-20和文件18-21所示。

文件18-20 Page.java

public class Page<T> {
    
    private int total;// 總條數
    private int page; // 當前頁
    private int size; // 每頁數
    private List<T> rows; // 結果集
    // getter setter 方法
  }

文件18-21 NavigationTag.java

/**
 * 顯示格式:首頁 上一頁 1 2 3 4 5下一頁 尾頁
 */
public class NavigationTag extends TagSupport {
    static final long serialVersionUID = 2372405317744358833L;
    /**
     * request 中用於保存Page<E> 對象的變數名,預設為“page”
     */
    private String bean = "page";
    /**
     * 分頁跳轉的url地址,此屬性必須
     */
    private String url = null;
    /**
     * 顯示頁碼數量
     */
    private int number = 5;

    @Override
    public int doStartTag() throws JspException {
        JspWriter writer = pageContext.getOut();
        HttpServletRequest request = 
                (HttpServletRequest) pageContext.getRequest();
        Page page = (Page) request.getAttribute(bean);
        if (page == null)
            return SKIP_BODY;
        url = resolveUrl(url, pageContext);
        try {
            // 計算總頁數
            int pageCount = page.getTotal() / page.getSize();
            if (page.getTotal() % page.getSize() > 0) {
                pageCount++;
            }
            writer.print("<nav><ul class=\"pagination\">");
            //首頁鏈接路徑
            String homeUrl = append(url, "page", 1);
            //末頁鏈接路徑
            String backUrl = append(url, "page", pageCount);
            // 顯示“上一頁”按鈕
            if (page.getPage() > 1) {
                String preUrl = append(url, "page", page.getPage() - 1);
                preUrl = append(preUrl, "rows", page.getSize());
        writer.print("<li><a href=\"" + homeUrl + "\">" + "首頁</a></li>");
        writer.print("<li><a href=\"" + preUrl + "\">" + "上一頁</a></li>");
            } else {
writer.print("<li class=\"disabled\"><a href=\"#\">" + "首頁 </a></li>");
writer.print("<li class=\"disabled\"><a href=\"#\">" + "上一頁 </a></li>");
            }
            // 顯示當前頁碼的前2頁碼和後兩頁碼
            // 若1 則 1 2 3 4 5, 若2 則 1 2 3 4 5, 若3 則1 2 3 4 5,
            // 若4 則 2 3 4 5 6 ,若10 則 8 9 10 11 12
            int indexPage =1;
            if(page.getPage() - 2 <=0){
                indexPage=1;
            }else if(pageCount-page.getPage() <=2){
                indexPage=pageCount-4;
            }else{
                indexPage= page.getPage() - 2;
            }
    for (int i= 1;i <= number && indexPage <= pageCount;indexPage++,i++){
                if (indexPage == page.getPage()) {
            writer.print("<li class=\"active\"><a href=\"#\">" + indexPage
                +"<spanclass=\"sr-only\"></span></a></li>");
                    continue;
                }
                String pageUrl = append(url, "page", indexPage);
                pageUrl = append(pageUrl, "rows", page.getSize());
writer.print("<li><a href=\"" + pageUrl + "\">" + indexPage + "</a></li>");
            }
            // 顯示“下一頁”按鈕
            if (page.getPage() < pageCount) {
                String nextUrl = append(url, "page", page.getPage() + 1);
                nextUrl = append(nextUrl, "rows", page.getSize());
        writer.print("<li><a href=\"" + nextUrl + "\">" + "下一頁</a></li>");
        writer.print("<li><a href=\"" + backUrl + "\">" + "尾頁</a></li>");
            } else {
writer.print("<li class=\"disabled\"><a href=\"#\">" + "下一頁</a></li>");
writer.print("<li class=\"disabled\"><a href=\"#\">" + "尾頁</a></li>");
            }
            writer.print("</nav>");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return SKIP_BODY;
    }

    private String append(String url, String key, int value) {
        return append(url, key, String.valueOf(value));
    }
    /**
     * 為url 參加參數對兒
     */
    private String append(String url, String key, String value) {
        if (url == null || url.trim().length() == 0) {
            return "";
        }
        if (url.indexOf("?") == -1) {
            url = url + "?" + key + "=" + value;
        } else {
            if (url.endsWith("?")) {
                url = url + key + "=" + value;
            } else {
                url = url + "&amp;" + key + "=" + value;
            }
        }
        return url;
    }
    /**
     * 為url 添加翻頁請求參數
     */
    private String resolveUrl(String url, 
        javax.servlet.jsp.PageContext pageContext) throws JspException {
        Map params = pageContext.getRequest().getParameterMap();
        for (Object key : params.keySet()) {
            if ("page".equals(key) || "rows".equals(key)){
                continue;
            }
            Object value = params.get(key);
            if (value == null){
                continue;
            }
            if (value.getClass().isArray()) {
                url = append(url, key.toString(), ((String[]) value)[0]);
            } else if (value instanceof String) {
                url = append(url, key.toString(), value.toString());
            }
        }
        return url;
    }
    public String getBean() {
        return bean;
    }
    public void setBean(String bean) {
        this.bean = bean;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public void setNumber(int number) {
        this.number = number;
    }
}    

(2)創建數據字典及客戶的Service層介面。在com.itheima.core.service包中創建一個名稱為BaseDictService和CustomerService的介面,編輯後如文件18-22和文件18-23所示。

文件18-22 BaseDictService.java

public interface BaseDictService {
    /**
     * 根據類別代碼查詢
     * 
     * @param dictTypeCode
     * @return
     */
    List<BaseDict> queryBaseDictByDictTypeCode(String dictTypeCode);
}

文件18-23 CustomerService.java

public interface CustomerService {

    /**
     * 根據條件分頁查詢客戶
     * 
     * @param queryVo
     * @return
     */
    Page<Customer> queryCustomerByQueryVo(QueryVo queryVo);
    
}

3)創建數據字典及客戶Service層介面的實現類。在com.itheima.core.service.impl包中分別創建數據字典和客戶Service層介面的實現類BaseDictServiceImpl和CustomerServiceImpl,編輯後的代碼如文件18-24和文件18-25所示。

文件18-24 BaseDictServiceImpl.java

@Service    
public class BaseDictServiceImpl implements BaseDictService{
    
    @Autowired
    private BaseDictDao baseDictDao;

    @Override
    public List<BaseDict> queryBaseDictByDictTypeCode(String dictTypeCode) {
        
        return baseDictDao.queryBaseDictByDictTypeCode(dictTypeCode);
    }

}

文件18-23 CustomerServiceImpl.java

@Service
public class CustomerServiceImpl implements CustomerService {
    
    @Autowired
    private CustomerDao customerDao;
    
    @Override
    public Page<Customer> queryCustomerByQueryVo(QueryVo queryVo) {
        
        // 判斷參數對象
        if(null != queryVo) {
            if(StringUtils.isNotBlank(queryVo.getCustName())) {
                queryVo.setCustName(queryVo.getCustName());
            }
            if(StringUtils.isNotBlank(queryVo.getCustSource())) {
                queryVo.setCustSource(queryVo.getCustSource());
            }
            if(StringUtils.isNotBlank(queryVo.getCustIndustry())) {
                queryVo.setCustIndustry(queryVo.getCustIndustry());
            }
            if(StringUtils.isNotBlank(queryVo.getCustLevel())) {
                queryVo.setCustLevel(queryVo.getCustLevel());
            }
            if(StringUtils.isNotBlank(queryVo.getCustName())) {
                queryVo.setCustName(queryVo.getCustName());
            }
            if(StringUtils.isNotBlank(queryVo.getCustName())) {
                queryVo.setCustName(queryVo.getCustName());
            }
            
        }

        // 設置查詢條件,從哪一條數據開始查  page和rows有初始值
        queryVo.setStart((queryVo.getPage() - 1) * queryVo.getRows());

        // 查詢數據結果集
        List<Customer> list = this.customerDao.queryCustomerByQueryVo(queryVo);
        
        // 查詢到的數據總條數
        int total = this.customerDao.queryCountByQueryVo(queryVo);

        // 封裝返回的page對象
        Page<Customer> page = new Page<Customer>();
        page.setPage(queryVo.getPage());// 當前頁 和參數一樣
        page.setRows(list);// 結果集
        page.setSize(queryVo.getRows());// 每頁數 和參數一樣
        page.setTotal(total);// 總條數
        return page;        
    }

在文件18-25的實現方法中,首先判斷參數是否為空,然後判斷條件查詢中的客戶名稱、信息來源、所屬行業和客戶級別是否為空,只有不為空時,才添加到參數對象中。接下來獲取了頁面傳遞過來的當前頁page和每頁數信息rows,由此得到起始行start。然後查詢所有的客戶信息以及客戶總數。最後將查詢出的所有信息封裝到Page對象中並返回。

4實現Controller

在com.itheima.core.controller包中,創建客戶控制器類CustomerController,編輯後如文件18-26所示。

文件18-26 CustomerController.java

@Controller
public class CustomerController {
        
    // 客戶來源
    @Value("${CUSTOMER_FROM_TYPE}")
    private String CUSTOMER_FROM_TYPE;
    // 客戶行業
    @Value("${CUSTOMER_INDUSTRY_TYPE}")
    private String CUSTOMER_INDUSTRY_TYPE;
    // 客戶級別
    @Value("${CUSTOMER_LEVEL_TYPE}")
    private String CUSTOMER_LEVEL_TYPE;

    
    @Autowired
    private BaseDictService baseDictService;
    
    @Autowired
    private CustomerService customerService;
    
    /**
     * 顯示用戶列表
     * @return
     */
    @RequestMapping(value="/customer/list")
    public String list(Model model,QueryVo queryVo) {

//      已在tomcat的server.xml中修改了uri的編碼為UTF-8,此處代碼可不寫         <Connector URIEncoding="UTF-8" />   
//      try {
//          // 解決get請求亂碼問題
//          if (StringUtils.isNotBlank(queryVo.getCustName())) {
//              queryVo.setCustName(new String(queryVo.getCustName().getBytes("ISO-8859-1"), "UTF-8"));
//          }
//      } catch (Exception e) {
//          e.printStackTrace();
//      }
        
        // 客戶來源
        List<BaseDict> fromType = baseDictService.queryBaseDictByDictTypeCode(CUSTOMER_FROM_TYPE);
        // 所屬行業
        List<BaseDict> industryType =baseDictService.queryBaseDictByDictTypeCode(CUSTOMER_INDUSTRY_TYPE);
        // 客戶級別
        List<BaseDict> levelType = baseDictService.queryBaseDictByDictTypeCode(CUSTOMER_LEVEL_TYPE);
        
        // 把前端頁面需要顯示的數據放到模型中
        model.addAttribute("fromType", fromType);
        model.addAttribute("industryType", industryType);
        model.addAttribute("levelType", levelType);
        
        // 條件、分頁查詢數據
        Page<Customer> page = this.customerService.queryCustomerByQueryVo(queryVo);
        // 把分頁查詢的結果放到模型中
        model.addAttribute("page", page);

        // 數據回顯
        model.addAttribute("custName", queryVo.getCustName());
        model.addAttribute("custSource", queryVo.getCustSource());
        model.addAttribute("custIndustry", queryVo.getCustIndustry());
        model.addAttribute("custLevel", queryVo.getCustLevel());


        return "customer";
    }

在客戶控制器類中,首先聲明瞭customerService和baseDictService屬性,並通過@Autowired註解將這兩個對象註入到本類中;然後分別定義了客戶來源、所屬行業和客戶級別屬性,並通過@Value註解將resource.properties文件中的屬性值賦給這3個屬性;最後編寫了查詢客戶列表的方法來執行查詢操作,其中第1個參數page的預設值為1,表示從第1條開始,第2個參數的預設值為10,表示每頁顯示10條數據。

5實現頁面顯示

(1)在18.3.3小節準備項目環境時,已經說明瞭需要引入自定義標簽文件。在本項目中,自定義標簽文件主要用於實現分頁功能,其標簽名稱為commons.tld,標簽中的實現代碼如文件18-27所示。

文件18-27 commons.tld

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE taglib
   PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
   "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
    <!-- 指定標簽庫的版本號 -->
    <tlib-version>2.0</tlib-version>
    <!-- 指定JSP的版本號 -->
    <jsp-version>1.2</jsp-version>
    <!-- 指定標簽庫的名稱 -->
    <short-name>common</short-name>
    <!-- 指定標簽庫的URI -->
    <uri>http://itheima.com/common/</uri>
    <!-- 指定標簽庫的顯示名稱 -->
    <display-name>Common Tag</display-name>
    <!-- 指定標簽庫的描述 -->
    <description>Common Tag library</description>
    <!-- 註冊一個自定義標簽 -->
    <tag>
        <!-- 指定註冊的自定義標簽名稱 -->
        <name>page</name>
        <!-- 指定自定義標簽的標簽處理器類 -->
        <tag-class>com.itheima.common.utils.NavigationTag</tag-class>
        <!-- 指定標簽體類型 -->
        <body-content>JSP</body-content>
        <!-- 描述 -->
        <description>create navigation for paging</description>
        <!-- 指定標簽中的屬性 -->
        <attribute>
            <!-- 指定屬性名稱 -->
            <name>url</name>
            <!-- 該屬性為true時表示其指定是屬性為必須屬性 -->
            <required>true</required>
            <!-- 該屬性用於指定能不能使用表達式來動態指定數據,為true時表示可以 -->
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>bean</name> 
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>number</name> 
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
</taglib>

在文件18-27中,第13行代碼就是我們在使用自定義標簽時引入的URI,第23行代碼指定了自定義標簽的處理器類,其他內容參見代碼註釋信息。小提示在實際開發時,分頁功能通常都會使用通用的工具類,或分頁組件來實現,而這些工具類和組件一般不需要開發人員自己編寫,只需學會使用即可。所以本書中的分頁工具類和上面的分頁標簽文件讀者只需直接引入,並且掌握如何使用,而不需要自己編寫。

(2)在customer.jsp中,編寫條件查詢和顯示客戶列表以及分頁查詢的代碼,具體如文件18-28所示。

文件18-28 customer.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page trimDirectiveWhitespaces="true"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="itheima" uri="http://itcast.cn/common/"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() 
                       + ":" + request.getServerPort() + path + "/";
%>
<!DOCTYPE HTML>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>客戶管理-BootCRM</title>
    <!-- 引入css樣式文件 -->
    <!-- Bootstrap Core CSS -->
    <link href="<%=basePath%>css/bootstrap.min.css" rel="stylesheet" />
    <!-- MetisMenu CSS -->
    <link href="<%=basePath%>css/metisMenu.min.css" rel="stylesheet" />
    <!-- DataTables CSS -->
    <link href="<%=basePath%>css/dataTables.bootstrap.css" rel="stylesheet" />
    <!-- Custom CSS -->
    <link href="<%=basePath%>css/sb-admin-2.css" rel="stylesheet" />
    <!-- Custom Fonts -->
    <link href="<%=basePath%>css/font-awesome.min.css" rel="stylesheet" type="text/css" />
    <link href="<%=basePath%>css/boot-crm.css" rel="stylesheet" type="text/css" />
</head>
<body>
...
    <!-- 客戶列表查詢部分  start-->
    <div id="page-wrapper">
        <div class="row">
            <div class="col-lg-12">


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 程式入口 SpringApplication.run(BeautyApplication.class, args); 執行此方法來載入整個SpringBoot的環境。 1. 從哪兒開始? SpringApplication.java /** * Run the Spring application, ...
  • 責編 | 劉靜 天氣降溫,感情卻升溫了? 上午剛到公司,就收到小Q發來的靈魂拷問: ​ ​ “Q仔!要不然下午請個假!我帶你去精神科看看!?”我實在忍不了,脫口而出。 話音未落,前排的運營小花回頭看向小Q,莞爾一笑,百媚橫生。 ​ 這個悶騷小伙子什麼時候勾搭上運營一枝花了?我正要追問,小Q看穿了我的 ...
  • Talk is cheap, show me the code! 以上這段mybatis的入門案例代碼,相信每一個瞭解mybatis的朋友都能看得懂,知碼醬同學今天也細細品了品! 1. 項目的路徑問題 : 在實際的項目中,並不推薦眾所周知的相對路徑和絕對路徑。 相對路徑: web應用是需要部署到服務 ...
  • 一、生成表格1.創建模型類(在 models.py文件中創建一個person類並且繼承models.Models類) 2.生成表格(在項目目錄下)(1)生成遷移文件:在pycharm下方的命令行Terminal中寫入python manage.py makemigrations,回車鍵後顯示遷移文件 ...
  • Mapper代理 "上一節" 中直接利用session+id來執行sql的方式存在一些問題 session執行sql時都需要提供要執行sql的id,而這個id是字元串類型,意味著id是否正確在編譯期間是無法獲知的,必須等到運行時才能發現錯誤, sql需要的參數和返回值類都不明確,這也增加了出錯的概率 ...
  • 傳值還是傳引用 調用函數時, 傳入的參數的 傳值 還是 傳引用 , 幾乎是每種編程語言都會關註的問題. 最近在使用 golang 的時候, 由於 傳值 和 傳引用 的方式沒有弄清楚, 導致了 BUG. 經過深入的嘗試, 終於弄明白了 golang 的 傳值 的 傳引用 , 嘗試過程記錄如下, 供大家 ...
  • 1. Maven綜述與拓展概念準備 Maven起源:生產環境下開發不再是一個項目一個工程,而是每一個模塊創建一個工程, 而多個模塊整合在一起就需要使用到像 Maven 這樣的構建工具。 Maven定義:一個自動化構建工具 Maven簡介:Maven是Apache軟體基金會組織維護的一款自動化構建工具 ...
  • 效果 修改步驟 Settings -> Editor -> Code Style -> Java ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...