黑馬客戶管理系統(SSM)


黑馬客戶管理系統

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>

 	<!-- 別名 -->
    <typeAliases>
		<package name="com.itheima.core.pojo"/>
	</typeAliases>    
	
</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.core.dao" />
	</bean>
    
	<!-- 配置Service掃描 -->
	<context:component-scan base-package="com.itheima.core.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.core. 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.core.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

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<title>登錄頁面</title>
<meta http-equiv=Content-Type content="text/html; charset=utf-8">
<link href="${pageContext.request.contextPath}/css/style.css"
	   type=text/css rel=stylesheet>
<link href="${pageContext.request.contextPath}/css/boot-crm.css"
	   type=text/css rel=stylesheet>
<script src=
       "${pageContext.request.contextPath}/js/jquery-1.11.3.min.js">
</script>
<meta content="MSHTML 6.00.2600.0" name=GENERATOR>
<script>
// 判斷是登錄賬號和密碼是否為空
function check(){
    var usercode = $("#usercode").val();
    var password = $("#password").val();
    if(usercode=="" || password==""){
    	$("#message").text("賬號或密碼不能為空!");
        return false;
    }  
    return true;
}
</script>
</head>
<body leftMargin=0 topMargin=0 marginwidth="0" marginheight="0"
	background="${pageContext.request.contextPath}/images/rightbg.jpg">
<div ALIGN="center">
<table border="0" width="1140px" cellspacing="0" cellpadding="0"
                                                           id="table1">
	<tr>
		<td height="93"></td>
		<td></td>
	</tr>
	<tr>
   <td background="${pageContext.request.contextPath}/images/rights.jpg"
		width="740" height="412">
   </td>
   <td class="login_msg" width="400" align="center">
	 <!-- margin:0px auto; 控制當前標簽居中 -->
	 <fieldset style="width: auto; margin: 0px auto;">
		  <legend>
		     <font style="font-size:15px" face="宋體">
		          歡迎使用BOOT客戶管理系統
		     </font>
		  </legend> 
		<font color="red">
			 <%-- 提示信息--%>
			 <span id="message">${msg}</span>
		</font>
		<%-- 提交后的位置:/WEB-INF/jsp/customer.jsp--%>
		<form action="${pageContext.request.contextPath }/login.action" 
			                       method="post" onsubmit="return check()">
                      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /><br />
          賬&nbsp;號:<input id="usercode" type="text" name="usercode" />
          <br /><br />
          密&nbsp;碼:<input id="password" type="password" name="password" />
          <br /><br />
          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
          <center><input type="submit" value="登錄" /></center>
		 </form>
	 </fieldset>
	</td>
	</tr>
</table>
</div>
</body>
</html>

在文件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都進行攔截控制
                /*用到了string的indexof方法 如果字符串參數作為一個子字符串在此對象中出現,則返回第一個這種子字符串                                                            
                    的第一個字符的索引;如果它不作為一個子字符串出現,則返回 -1。*/
		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,里面包含查詢條件屬性和分頁數據。需要注意的是,屬性中的page、star和rows用於執行分頁操作,其中page表示當前的頁碼數,start表示分頁操作中的起始行(由page、rows計算得出),而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="QueryVo"
		resultType="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="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="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">
				<h1 class="page-header">客戶管理</h1>
			</div>
			<!-- /.col-lg-12 -->
		</div>
		<!-- /.row -->
		<div class="panel panel-default">
			<div class="panel-body">
				<form class="form-inline" method="get" 
				      action="${pageContext.request.contextPath }/customer/list.action">
					<div class="form-group">
						<label for="customerName">客戶名稱</label> 
						<input type="text" class="form-control" id="customerName" 
						                   value="${custName }" name="custName" />
					</div>
					<div class="form-group">
						<label for="customerFrom">客戶來源</label> 
						<select	class="form-control" id="customerFrom" name="custSource">
							<option value="">--請選擇--</option>
							<c:forEach items="${fromType}" var="item">
								<option value="${item.dict_id}"
								       <c:if test="${item.dict_id == custSource}">selected</c:if>>
								    ${item.dict_item_name }
								</option>
							</c:forEach>
						</select>
					</div>
					<div class="form-group">
						<label for="custIndustry">所屬行業</label> 
						<select	class="form-control" id="custIndustry"  name="custIndustry">
							<option value="">--請選擇--</option>
							<c:forEach items="${industryType}" var="item">
								<option value="${item.dict_id}"
								        <c:if test="${item.dict_id == custIndustry}"> selected</c:if>>
								    ${item.dict_item_name }
								</option>
							</c:forEach>
						</select>
					</div>
					<div class="form-group">
						<label for="custLevel">客戶級別</label>
						<select	class="form-control" id="custLevel" name="custLevel">
							<option value="">--請選擇--</option>
							<c:forEach items="${levelType}" var="item">
								<option value="${item.dict_id}"
								        <c:if test="${item.dict_id == custLevel}"> selected</c:if>>
								    ${item.dict_item_name }
								</option>
							</c:forEach>
						</select>
					</div>
					<button type="submit" class="btn btn-primary">查詢</button>
				</form>
			</div>
		</div>
		<a href="#" class="btn btn-primary" data-toggle="modal" 
		           data-target="#newCustomerDialog" onclick="clearCustomer()">新建</a>
		<div class="row">
			<div class="col-lg-12">
				<div class="panel panel-default">
					<div class="panel-heading">客戶信息列表</div>
					<!-- /.panel-heading -->
					<table class="table table-bordered table-striped">
						<thead>
							<tr>
								<th>編號</th>
								<th>客戶名稱</th>
								<th>客戶來源</th>
								<th>客戶所屬行業</th>
								<th>客戶級別</th>
								<th>固定電話</th>
								<th>手機</th>
								<th>操作</th>
							</tr>
						</thead>
						<tbody>
							<c:forEach items="${page.rows}" var="row">
								<tr>
									<td>${row.cust_id}</td>
									<td>${row.cust_name}</td>
									<td>${row.cust_source}</td>
									<td>${row.cust_industry}</td>
									<td>${row.cust_level}</td>
									<td>${row.cust_phone}</td>
								    <td>${row.cust_mobile}</td>
									<td>
										<a href="#" class="btn btn-primary btn-xs" data-toggle="modal" data-target="#customerEditDialog" onclick= "editCustomer(${row.cust_id})">修改</a>
										<a href="#" class="btn btn-danger btn-xs" onclick="deleteCustomer(${row.cust_id})">刪除</a>
									</td>
								</tr>
							</c:forEach>
						</tbody>
					</table>
					<div class="col-md-12 text-right">
						<itheima:page url="${pageContext.request.contextPath }/customer/list.action" />
					</div>
					<!-- /.panel-body -->
				</div>
				<!-- /.panel -->
			</div>
			<!-- /.col-lg-12 -->
		</div>
	</div>
	<!-- 客戶列表查詢部分  end-->
</div>
...

在上述頁面代碼中,第4行和第5行代碼引入了JSTL標簽和自定義的分頁標簽;第17~62行代碼是一個條件查詢的form表單,單擊“查詢”按鈕后,會提交到一個“list.action”請求中;第72~105行代碼是顯示客戶信息列表的表格,查詢出的客戶信息會在此表格中顯示;第107~108行代碼是自定義的分頁標簽,該標簽會根據客戶數以及設定的頁數數據顯示內容。

6測試條件查詢和分頁

發布項目並啟動Tomcat服務器后,進入客戶管理頁面,然后單擊“查詢”按鈕即可查詢出所有客戶信息,並且這些信息都已分頁顯示,如圖18-13所示。

細心的讀者一定會發現,在進入客戶管理頁面時,客戶信息列表是沒有任何顯示的,只有單擊“查詢”按鈕后,才會顯示出數據。那么有什么辦法能夠讓其進入該頁面時就默認顯示數據呢?

要實現登錄后展示客戶信息列表的操作很簡單,只需將用戶控制器類(UserController)中用戶登錄方法(login())內跳轉到主頁面的語句修改為重定向到主頁的跳轉方法即可,修改后的語句如下。

	@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";
			return "redirect:customer/list.action";
		}
		model.addAttribute("msg", "賬號或密碼錯誤,請重新登錄!");
		// 返回到登錄頁面
		return "login";
				
	}

這樣,登錄成功后,客戶管理頁面將會直接顯示出分頁后的客戶列表信息。

5.2 添加客戶

在本系統中,添加客戶的操作是通過頁面彈出窗口實現的,當單擊“新建”按鈕時,將彈出“新建客戶信息”窗口,如圖18-16所示。

填寫完圖18-16中的所有客戶信息后,單擊“創建客戶”按鈕,將執行添加客戶的操作。那么此操作具體是如何實現的呢?下面將對系統中的添加客戶的功能實現進行詳細講解,具體步驟如下。1.實現頁面功能代碼在頁面中,“新建”按鈕鏈接的實現代碼如下。

1實現頁面功能代碼

在頁面中,“新建”按鈕鏈接的實現代碼如下。

<a href="#" class="btn btn-primary" data-toggle="modal" data-target="#newCustomerDialog" onclick="clearCustomer()">新建</a>

在上述代碼中,data-toggle="modal" 和data-target="#newCustomerDialog"是Bootstrap的模態框代碼,當單擊“新建”按鈕后,會彈出id為newCustomerDialog的窗口,同時通過onclick屬性執行clearCustomer()方法來清除窗口中的所有數據。在customer.jsp中,新建客戶模態框的顯示代碼如文件18-29所示。

<!-- 創建客戶模態框 -->
<div class="modal fade" id="newCustomerDialog" tabindex="-1" role="dialog"
	aria-labelledby="myModalLabel">
	<div class="modal-dialog" role="document">
		<div class="modal-content">
			<div class="modal-header">
				<button type="button" class="close" data-dismiss="modal" aria-label="Close">
					<span aria-hidden="true">&times;</span>
				</button>
				<h4 class="modal-title" id="myModalLabel">新建客戶信息</h4>
			</div>
			<div class="modal-body">
				<form class="form-horizontal" id="new_customer_form">
					<div class="form-group">
						<label for="new_customerName" class="col-sm-2 control-label">
						    客戶名稱
						</label>
						<div class="col-sm-10">
							<input type="text" class="form-control" id="new_customerName" placeholder="客戶名稱" name="cust_name" />
						</div>
					</div>
					<div class="form-group">
						<label for="new_customerFrom" style="float:left;padding:7px 15px 0 27px;">客戶來源</label> 
						<div class="col-sm-10">
							<select	class="form-control" id="new_customerFrom" name="cust_source">
								<option value="">--請選擇--</option>
								<c:forEach items="${fromType}" var="item">
									<option value="${item.dict_id}"<c:if test="${item.dict_id == custSource}">selected</c:if>>
									${item.dict_item_name }									
									</option>
								</c:forEach>
							</select>
						</div>
					</div>
					<div class="form-group">
						<label for="new_custIndustry" style="float:left;padding:7px 15px 0 27px;">所屬行業</label>
						<div class="col-sm-10"> 
							<select	class="form-control" id="new_custIndustry"  name="cust_industry">
								<option value="">--請選擇--</option>
								<c:forEach items="${industryType}" var="item">
									<option value="${item.dict_id}"<c:if test="${item.dict_id == custIndustry}"> selected</c:if>>
									${item.dict_item_name }
									</option>
								</c:forEach>
							</select>
						</div>
					</div>
					<div class="form-group">
						<label for="new_custLevel" style="float:left;padding:7px 15px 0 27px;">客戶級別</label>
						<div class="col-sm-10">
							<select	class="form-control" id="new_custLevel" name="cust_level">
								<option value="">--請選擇--</option>
								<c:forEach items="${levelType}" var="item">
									<option value="${item.dict_id}"<c:if test="${item.dict_id == custLevel}"> selected</c:if>>${item.dict_item_name }</option>
								</c:forEach>
							</select>
						</div>
					</div>
					<div class="form-group">
						<label for="new_linkMan" class="col-sm-2 control-label">聯系人</label>
						<div class="col-sm-10">
							<input type="text" class="form-control" id="new_linkMan" placeholder="聯系人" name="cust_linkman" />
						</div>
					</div>
					<div class="form-group">
						<label for="new_phone" class="col-sm-2 control-label">固定電話</label>
						<div class="col-sm-10">
							<input type="text" class="form-control" id="new_phone" placeholder="固定電話" name="cust_phone" />
						</div>
					</div>
					<div class="form-group">
						<label for="new_mobile" class="col-sm-2 control-label">移動電話</label>
						<div class="col-sm-10">
							<input type="text" class="form-control" id="new_mobile" placeholder="移動電話" name="cust_mobile" />
						</div>
					</div>
					<div class="form-group">
						<label for="new_zipcode" class="col-sm-2 control-label">郵政編碼</label>
						<div class="col-sm-10">
							<input type="text" class="form-control" id="new_zipcode" placeholder="郵政編碼" name="cust_zipcode" />
						</div>
					</div>
					<div class="form-group">
						<label for="new_address" class="col-sm-2 control-label">聯系地址</label>
						<div class="col-sm-10">
							<input type="text" class="form-control" id="new_address" placeholder="聯系地址" name="cust_address" />
						</div>
					</div>
				</form>
			</div>
			<div class="modal-footer">
				<button type="button" class="btn btn-default" data-dismiss="modal">關閉</button>
				<button type="button" class="btn btn-primary" onclick="createCustomer()">創建客戶</button>
			</div>
		</div>
	</div>
</div>

在上述代碼中,第15~121行代碼的form表單中的實現代碼,即為用戶所需要填寫的客戶信息。

為保證每次單擊“新建”按鈕后所彈出的模態框內沒有任何數據,需要在頁面中創建一個clearCustomer()方法來清空模態框中的內容,clearCustomer()方法的實現代碼如下所示。

	//清空新建客戶窗口中的數據
	function clearCustomer() {
	    $("#new_customerName").val("");
	    $("#new_customerFrom").val("")
	    $("#new_custIndustry").val("")
	    $("#new_custLevel").val("")
	    $("#new_linkMan").val("");
	    $("#new_phone").val("");
	    $("#new_mobile").val("");
	    $("#new_zipcode").val("");
	    $("#new_address").val("");
	}

填寫完模態框中的信息后,單擊“創建客戶”按鈕,會執行createCustomer()方法,該方法的實現代碼如下。

	// 創建客戶
	function createCustomer() {
	$.post("<%=basePath%>customer/create.action",
	$("#new_customer_form").serialize(),function(data){
	        if(data =="OK"){
	            alert("客戶創建成功!");
	            window.location.reload();
	        }else{
	            alert("客戶創建失敗!");
	            window.location.reload();
	        }
	    });
	}

createCustomer()方法會通過jQuery Ajax的POST請求將id為new_customer_form的表單序列化,然后提交到以“/create.action”結尾的請求中,如果其返回值為“OK”則表示客戶創建成功,否則創建客戶失敗。

2實現Controller層方法

在客戶控制器類CustomerController中編寫創建客戶的方法,其代碼如下所示。

	/**
	 * 創建客戶
	 * @param customer
	 * @param session
	 * @return
	 */
	@RequestMapping("/customer/create.action")
	@ResponseBody
	public String customerCreate(Customer customer,HttpSession session) {
		// 獲取Session中的當前用戶信息
		User user=(User)session.getAttribute("USER_SESSION");
		// 將當前用戶id存儲在客戶對象中
		customer.setCust_create_id(user.getUser_id());
		// 創建Date對象
		Date date = new Date();
		// 得到一個Timestamp格式的時間,存入mysql中的時間格式“yyyy/MM/dd HH:mm:ss”
//		Timestamp timeStamp = new Timestamp(date.getTime());
		customer.setCust_createtime(date);
		// 執行Service層中的創建方法,返回的是受影響的行數
		int rows = customerService.createCustomer(customer);
		if(rows >0) {
			return "ok";
		}else {
			return "FAIL";
		}
	}

在上述方法代碼中,首先獲取了Session中的當前用戶信息,然后將當前用戶id信息添加到Customer對象的創建人id屬性中。接下來創建了Date對象,並將格式化的時間信息添加到Customer對象的cust_createtime屬性中。最后執行Service層中的createCustomer()方法,其返回值為數據庫中受影響的行數,如果其值大於0,則表示創建成功,返回“OK”字符串信息,否則返回“FAIL”字符串。

小提示
@Responsebody注解

一般在異步獲取數據時使用。在使用@RequestMapping注解后,方法的返回值通常會被解析為跳轉路徑(如某個頁面或某個方法),而加上@Responsebody注解后,其返回結果將不會被解析為跳轉路徑,而是將通過HttpMessageConverter轉換為指定格式后的結果(如json、xml等)直接寫入HTTP Response對象的body中,這樣頁面中的方法就可以獲取其返回值。

3實現Service層方法

(1)創建接口方法。在CustomerService接口中,創建一個createCustomer()方法,其代碼如下所示。

	/**
	 * 創建客戶
	 * @param customer
	 * @return
	 */
	int createCustomer(Customer customer);

(2)創建實現類方法。在CustomerServiceImpl中,實現createCustomer()方法,編輯后的代碼如下所示。

	@Override
	public int createCustomer(Customer customer) {
		return customerDao.createCustomer(customer);
	}

4實現DAO層方法

(1)創建接口方法。在CustomerDao中,編寫創建客戶的方法,其代碼如下所示。

	/**
	 * 創建客戶
	 * @param customer
	 * @return
	 */
	int createCustomer(Customer customer);

(2)創建映射插入語句。在CustomerDao.xml中,編寫執行插入操作的映射插入語句,其代碼如下所示。

	<!-- 添加客戶 -->
	<insert id="createCustomer" parameterType="com.itheima.core.pojo.Customer">
		insert into customer(
			cust_name,
			cust_user_id,
			cust_create_id,
			cust_source,
			cust_industry,
			cust_level,
			cust_linkman,
			cust_phone,
			cust_mobile,
			cust_zipcode,
			cust_address,
			cust_createtime
		)
		values(#{cust_name}),		
			#{cust_user_id},
			#{cust_create_id},
			#{cust_source},
			#{cust_industry},
			#{cust_level},
			#{cust_linkman},
			#{cust_phone},
			#{cust_mobile},
			#{cust_zipcode},
			#{cust_address},
			#{cust_createtime}
		)
	</insert>

5.添加客戶測試至此,添加客戶的實現代碼就已經編寫完成。發布並啟動項目后,進入客戶管理頁面,單擊“新建”按鈕,並填寫新建客戶信息,如圖18-17所示。

單擊圖18-17中的“創建客戶”按鈕后,如果程序正確執行,則會彈出“客戶創建成功!”的彈出窗口,再次單擊“確定”后,瀏覽器就會刷新當前頁面。要查詢所創建的客戶是否已創建成功非常簡單,只需要在條件查詢中查找客戶名稱為“小程”的客戶,如圖18-18所示。

從圖18-18可以看出,新創建的客戶“小程”的信息已被正確查詢出。至此添加客戶的功能就已經成功實現。

5.3 修改客戶

修改操作與添加操作一樣,也是通過頁面彈出窗口實現的,當單擊頁面對應數據的“修改”按鈕時,將彈出“修改客戶信息”窗口,如圖18-19所示。

從圖18-19可以看出,修改客戶信息窗口與新建客戶信息窗口的顯示內容基本相同,但修改客戶信息窗口中回顯出了需要修改的客戶信息。當修改客戶信息后,單擊“保存修改”按鈕,即可執行修改操作。下面就對本系統中修改客戶的功能實現進行詳細講解,具體步驟如下。

1實現頁面功能代碼

在頁面中,“修改”按鈕鏈接的實現代碼如下。

<a href="#" class="btn btn-primary btn-xs" data-toggle="modal" data-target="#customerEditDialog" onclick= "editCustomer(${row.cust_id})">修改</a>

與新建方法一樣,當單擊“修改”按鈕后,會彈出id為customerEditDialog的模態窗口,同時通過執行onclick屬性的editCustomer()方法來獲取需要修改客戶的所有數據。在customer.jsp中,修改客戶模態框的顯示代碼如文件18-30所示。文件18-30 customer.jsp

<!-- 修改客戶模態框 -->
<div class="modal fade" id="customerEditDialog" tabindex="-1" role="dialog"
	aria-labelledby="myModalLabel">
	<div class="modal-dialog" role="document">
		<div class="modal-content">
			<div class="modal-header">
				<button type="button" class="close" data-dismiss="modal" aria-label="Close">
					<span aria-hidden="true">&times;</span>
				</button>
				<h4 class="modal-title" id="myModalLabel">修改客戶信息</h4>
			</div>
			<div class="modal-body">
				<form class="form-horizontal" id="edit_customer_form">
					<input type="hidden" id="edit_cust_id" name="cust_id"/>
					<div class="form-group">
						<label for="edit_customerName" class="col-sm-2 control-label">客戶名稱</label>
						<div class="col-sm-10">
							<input type="text" class="form-control" id="edit_customerName" placeholder="客戶名稱" name="cust_name" />
						</div>
					</div>
					<div class="form-group">
						<label for="edit_customerFrom" style="float:left;padding:7px 15px 0 27px;">客戶來源</label> 
						<div class="col-sm-10">
							<select	class="form-control" id="edit_customerFrom" name="cust_source">
								<option value="">--請選擇--</option>
								<c:forEach items="${fromType}" var="item">
									<option value="${item.dict_id}"<c:if test="${item.dict_id == custSource}"> selected</c:if>>${item.dict_item_name }</option>
								</c:forEach>
							</select>
						</div>
					</div>
					<div class="form-group">
						<label for="edit_custIndustry" style="float:left;padding:7px 15px 0 27px;">所屬行業</label>
						<div class="col-sm-10"> 
							<select	class="form-control" id="edit_custIndustry"  name="cust_industry">
								<option value="">--請選擇--</option>
								<c:forEach items="${industryType}" var="item">
									<option value="${item.dict_id}"<c:if test="${item.dict_id == custIndustry}"> selected</c:if>>${item.dict_item_name }</option>
								</c:forEach>
							</select>
						</div>
					</div>
					<div class="form-group">
						<label for="edit_custLevel" style="float:left;padding:7px 15px 0 27px;">客戶級別</label>
						<div class="col-sm-10">
							<select	class="form-control" id="edit_custLevel" name="cust_level">
								<option value="">--請選擇--</option>
								<c:forEach items="${levelType}" var="item">
									<option value="${item.dict_id}"<c:if test="${item.dict_id == custLevel}"> selected</c:if>>${item.dict_item_name }</option>
								</c:forEach>
							</select>
						</div>
					</div>
					<div class="form-group">
						<label for="edit_linkMan" class="col-sm-2 control-label">聯系人</label>
						<div class="col-sm-10">
							<input type="text" class="form-control" id="edit_linkMan" placeholder="聯系人" name="cust_linkman" />
						</div>
					</div>
					<div class="form-group">
						<label for="edit_phone" class="col-sm-2 control-label">固定電話</label>
						<div class="col-sm-10">
							<input type="text" class="form-control" id="edit_phone" placeholder="固定電話" name="cust_phone" />
						</div>
					</div>
					<div class="form-group">
						<label for="edit_mobile" class="col-sm-2 control-label">移動電話</label>
						<div class="col-sm-10">
							<input type="text" class="form-control" id="edit_mobile" placeholder="移動電話" name="cust_mobile" />
						</div>
					</div>
					<div class="form-group">
						<label for="edit_zipcode" class="col-sm-2 control-label">郵政編碼</label>
						<div class="col-sm-10">
							<input type="text" class="form-control" id="edit_zipcode" placeholder="郵政編碼" name="cust_zipcode" />
						</div>
					</div>
					<div class="form-group">
						<label for="edit_address" class="col-sm-2 control-label">聯系地址</label>
						<div class="col-sm-10">
							<input type="text" class="form-control" id="edit_address" placeholder="聯系地址" name="cust_address" />
						</div>
					</div>
				</form>
			</div>
			<div class="modal-footer">
				<button type="button" class="btn btn-default" data-dismiss="modal">關閉</button>
				<button type="button" class="btn btn-primary" onclick="updateCustomer()">保存修改</button>
			</div>
		</div>
	</div>
</div>

在上述代碼中,第15~125行代碼中的form表單就是修改客戶信息的實現代碼。由於在修改客戶信息時,需要先獲取到該客戶的所有信息,並顯示到修改信息的窗口內,所以需要在頁面中編寫一個獲取客戶信息的方法editCustomer(),該方法的實現代碼如下所示。

	// 通過id獲取修改的客戶信息
	function editCustomer(id) {
	    $.ajax({
	        type:"get",
	        url:"<%=basePath%>customer/queryCustomerById.action",
	        data:{"id":id},
	        success:function(data) {
	            $("#edit_cust_id").val(data.cust_id);
	            $("#edit_customerName").val(data.cust_name);
	            $("#edit_customerFrom").val(data.cust_source)
	            $("#edit_custIndustry").val(data.cust_industry)
	            $("#edit_custLevel").val(data.cust_level)
	            $("#edit_linkMan").val(data.cust_linkman);
	            $("#edit_phone").val(data.cust_phone);
	            $("#edit_mobile").val(data.cust_mobile);
	            $("#edit_zipcode").val(data.cust_zipcode);
	            $("#edit_address").val(data.cust_address);
	            
	        }
	    });
	}

上述方法代碼使用了jQuery Ajax的方式來獲取所需要修改的客戶信息,獲取成功后,會將該客戶信息添加到修改客戶模態框中的相應位置。

2實現Controller層方法

在CustomerController類中,編寫通過id獲取客戶信息和更新客戶的方法,其代碼如下所示。

	/**
	 * 根據id查詢客戶,返回json格式數據
	 * 
	 * @param id
	 * @return
	 */
	@RequestMapping("/customer/getCustomerById.action")
	@ResponseBody
	public Customer queryCustomerById(Long id) {
		Customer customer = this.customerService.queryCustomerById(id);
		return customer;
	}

	/**
	 * 更新客戶
	 * 
	 * @param id
	 * @return
	 */
	@RequestMapping("/customer/update")
	@ResponseBody
	public String updateCustomerById(Customer customer) {
		int rows = this.customerService.updateCustomerById(customer);
		if(rows >0) {
			return "ok";
		}else {
			return "FAIL";
		}
	}

上述兩個方法中,都只是調用了Service層中的相應方法,並將相應的執行結果返回。

3實現Service層方法

(1)創建接口方法。

在CustomerService接口中,創建通過id獲取客戶信息和更新客戶的方法,代碼如下所示。

	/**
	 * 根據id編輯客戶數據
	 * 
	 * @param customer
	 * @return 
	 */
	int updateCustomerById(Customer customer);
	
	/**
	 * 根據id刪除客戶
	 * 
	 * @param id
	 */
	void deleteCustomerById(Long id);

(2)創建實現類方法。

在CustomerServiceImpl中,實現接口中的方法,編輯后的代碼如下所示。

	@Override
	public Customer queryCustomerById(Long id) {
		Customer customer = this.customerDao.queryCustomerById(id);
		return customer;
	}
	
	@Override
	public int updateCustomerById(Customer customer) {		
		return customerDao.updateCustomerById(customer);
	}

在上述代碼中,getCustomerById()方法返回的是所需要修改的客戶對象,而updateCustomer()方法執行后,返回的是數據庫中受影響的行數。

4實現DAO層方法

(1)創建接口方法。

在CustomerDao接口中,編寫通過id獲取客戶信息和更新客戶的方法,其代碼如下所示。

	/**
	 * 根據id查詢用戶
	 * 
	 * @param id
	 * @return
	 */
	Customer queryCustomerById(Long id);
	
	/**
	 * 根據id編輯客戶數據
	 * 
	 * @param customer
	 * @return 
	 */
	int updateCustomerById(Customer customer);

(2)創建映射語句。

在CustomerDao.xml中,編寫執行插入操作的映射插入語句,代碼如下所示。

	<!-- 根據id查詢用戶 -->
	<select id="queryCustomerById" parameterType="Integer"
		resultType="com.itheima.core.pojo.Customer">
		SELECT * FROM customer WHERE cust_id = #{id}
	</select>

	<!-- 更新客戶 -->
	<update id="updateCustomerById"
		parameterType="com.itheima.core.pojo.Customer">
		UPDATE customer
		<set>
			<if test="cust_name !=null and cust_name != ''">
				cust_name = #{cust_name},
			</if>
			<if test="cust_user_id !=null">
				cust_user_id = #{cust_user_id},
			</if>
			<if test="cust_create_id !=null">
				cust_create_id = #{cust_create_id},
			</if>
			<if test="cust_source !=null and cust_source != ''">
				cust_source = #{cust_source},
			</if>
			<if test="cust_industry !=null and cust_industry != ''">
				cust_industry = #{cust_industry},
			</if>
			<if test="cust_level !=null and cust_level != ''">
				cust_level = #{cust_level},
			</if>
			<if test="cust_linkman !=null and cust_linkman != ''">
				cust_linkman = #{cust_linkman},
			</if>
			<if test="cust_phone !=null and cust_phone != ''">
				cust_phone = #{cust_phone},
			</if>
			<if test="cust_mobile !=null and cust_mobile != ''">
				cust_mobile = #{cust_mobile},
			</if>
			<if test="cust_zipcode !=null and cust_zipcode != ''">
				cust_zipcode = #{cust_zipcode},
			</if>
			<if test="cust_address !=null and cust_address != ''">
				cust_address = #{cust_address},
			</if>
			<if test="cust_createtime !=null andcust_createtime != ''">
				cust_createtime = #{cust_createtime},
			</if>
		</set>
		WHERE cust_id = #{cust_id}
	</update>

5修改客戶測試

至此,修改客戶的實現代碼就已經編寫完成。發布並啟動項目后,進入客戶管理頁面,單擊列表中編號為14客戶后面的“修改”按鈕,將所屬行業修改為“對外貿易”,並將客戶級別修改為“VIP客戶”,如圖18-20所示。

單擊圖18-20中的“保存修改”按鈕后,如果程序正確執行,將會出現“客戶信息更新成功”提示框,確認后,將回到客戶管理列表頁面,此時頁面中的信息如圖18-21所示。

從圖18-21可以看出,編號為14客戶的修改后信息已經顯示在列表中,這也就說明系統的修改客戶功能已成功實現。

5.4 刪除客戶

刪除客戶是客戶管理模塊中的最后一個功能。在單擊客戶信息列表中操作列的某個“刪除”鏈接后,會彈出刪除確認框,如圖18-22所示。

單擊“確定”按鈕后,即可執行刪除客戶的操作。接下來,本節將對刪除客戶功能的實現進行詳細講解,具體步驟如下。

1實現頁面功能代碼

頁面中,刪除客戶鏈接的實現代碼如下所示。

<a href="#" class="btn btn-danger btn-xs" onclick="deleteCustomer(${row.cust_id})">刪除</a>

上述代碼中,當單擊“刪除”后,會執行onclick屬性中的deleteCustomer()方法,該方法中的參數${row.cust_id}會獲取當前所在行的客戶id。在頁面中,編寫刪除客戶的方法deleteCustomer(),其方法代碼如下所示。

	// 刪除客戶
	function deleteCustomer(id) {
	    if(confirm('確實要刪除該客戶嗎?')) {
	$.post("<%=basePath%>customer/delete.action",{"id":id},
	function(data){
	            if(data =="OK"){
	                alert("客戶刪除成功!");
	                window.location.reload();
	            }else{
	                alert("刪除客戶失敗!");
	                window.location.reload();
	            }
	        });
	    }
	}

執行上述方法時,會通過jQuery Ajax的方式發送一個以“/delete.action”結尾的請求,該請求會將所要刪除的客戶id傳入后台處理方法中。

2實現Controller層方法

在CustomerController類中,創建一個刪除客戶的方法customerDelete(),編輯后的實現代碼如下所示。

	/**
	 * 刪除客戶
	 * 
	 * @param id
	 * @return
	 */
	@RequestMapping("/customer/delete")
	@ResponseBody
	public String deleteCustomerById(Long id) {
		int rows = this.customerService.deleteCustomerById(id);
		if(rows >0) {
			return "ok";
		}else {
			return "FAIL";
		}
	}

customerDelete()方法並沒有執行太多操作,而是調用了Service層中的deleteCustomer()方法來獲取數據庫中受影響的行數,如果其值大於0,則表示刪除成功,否則表示刪除失敗。

3實現Service層方法

(1)創建接口方法。在CustomerService中,編寫一個刪除客戶的方法,代碼如下所示。

int deleteCustomerById(Long id);

(2)創建實現類方法。在CustomerServiceImpl中,實現接口中的刪除方法,編輯后的代碼如下所示。[插圖]

	@Override
	public int deleteCustomerById(Long id) {
		return customerDao.deleteCustomerById(id);
	}

4實現DAO層方法

(1)創建接口方法。在CustomerDao接口中,編寫通過id刪除客戶的方法,代碼如下所示。

int deleteCustomerById(Long id);

(2)創建映射語句。在CustomerDao.xml中編寫執行刪除操作的映射語句,代碼如下所示。

	<!-- 根據id刪除客戶 -->
	<delete id="deleteCustomerById" parameterType="Long">
		DELETE FROM
		customer WHERE cust_id = #{id}
	</delete>

5刪除客戶測試

至此,刪除客戶功能的實現代碼就已經編寫完成。下面以刪除編號為14的客戶“小張”為例,來測試系統的刪除功能。

單擊編號為14客戶所在行的“刪除”鏈接后,會彈出刪除確認框,單擊“確定”按鈕后,頁面中會彈出客戶刪除成功的提示框,如圖18-23所示。確定后,系統會刷新當前頁面。此時頁面中客戶信息列表所顯示的數據如圖18-24所示。從圖18-24可以看到,執行刪除操作后,編號為14的客戶“小張”並沒有在客戶信息列表中顯示,這也就說明刪除操作執行成功。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM