注:本文为伪原创,代码主要参考尚硅谷教程。感谢巨人的肩膀,让我可以看得更远。
本文全部代码见https://github.com/BnKes/crowdfunding
详细目录见文章尾巴
1.系统架构
- Atcrowdfunding-parent 父工程,聚合其他工程(pom)
- Atcrowdfunding-main Web工程,存放所有页面,框架配置文件(war)
- Atcrowdfunding-manager-impl 后台管理系统,存放控制器类,业务层实现类(jar)
- Atcrowdfunding-manager-api 后台管理系统,存放业务层接口和DAO层接口(jar)
- Atcrowdfunding-potal-impl 前台系统,存放控制器类,业务层实现类(jar)
- Atcrowdfunding-potal-api 前台系统,存放业务层接口和DAO层接口(jar)
- Atcrowdfunding-common 存放所有模块所需要的公共类(jar)
- Atcrowdfunding-bean 存放所有模块的实体类(jar)
各工程依赖:
2.pom.xml
(1)parent
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.atguigu.maven</groupId>
<artifactId>01-Atcrowdfunding-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>../Atcrowdfunding-bean</module>
<module>../Atcrowdfunding-common</module>
<module>../Atcrowdfunding-main</module>
<module>../Atcrowdfunding-manager-api</module>
<module>../Atcrowdfunding-manager-impl</module>
<module>../Atcrowdfunding-potal-api</module>
<module>../Atcrowdfunding-potal-impl</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1.3-b06</version>
<scope>provided</scope><!-- 注意!!!! -->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.4</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<!-- Spring整合MyBatis -->
<!-- MyBatis中延迟加载需要使用Cglib -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>
<!-- 控制日志输出:结合log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- ********其他****************************** -->
<!-- Ehcache二级缓存 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>1.6.2</version>
</dependency>
<!-- 石英调度 - 开始 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
<!-- 石英调度 - 结束 -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.jfree</groupId>
<artifactId>jfreechart</artifactId>
<version>1.0.19</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.19</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>5.15.1</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>5.15.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-explorer</artifactId>
<version>5.15.1</version>
<exclusions>
<exclusion>
<artifactId>groovy-all</artifactId>
<groupId>org.codehaus.groovy</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
</project>
(2)bean
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>Atcrowdfunding-bean</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
</dependencies>
<parent>
<groupId>com.atguigu.maven</groupId>
<artifactId>01-Atcrowdfunding-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
</project>
(3)common
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>Atcrowdfunding-common</artifactId>
<parent>
<groupId>com.atguigu.maven</groupId>
<artifactId>01-Atcrowdfunding-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../01-Atcrowdfunding-parent/pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>Atcrowdfunding-bean</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope><!-- 注意!!!! -->
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<scope>provided</scope><!-- 注意!!!! -->
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<!-- Spring整合MyBatis -->
<!-- MyBatis中延迟加载需要使用Cglib -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</dependency>
<!-- 控制日志输出:结合log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!-- ********其他****************************** -->
<!-- Ehcache二级缓存 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<!-- 石英调度 - 开始 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</dependency>
<!-- 石英调度 - 结束 -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
</dependency>
<dependency>
<groupId>org.jfree</groupId>
<artifactId>jfreechart</artifactId>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-explorer</artifactId>
<exclusions>
<exclusion>
<artifactId>groovy-all</artifactId>
<groupId>org.codehaus.groovy</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
(4)main
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.atguigu.maven</groupId>
<artifactId>Atcrowdfunding-main</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<parent>
<groupId>com.atguigu.maven</groupId>
<artifactId>01-Atcrowdfunding-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../01-Atcrowdfunding-parent/pom.xml</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>Atcrowdfunding-potal-impl</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>Atcrowdfunding-manager-impl</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
(5)manager-api
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.atguigu.maven</groupId>
<artifactId>Atcrowdfunding-manager-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>Atcrowdfunding-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>Atcrowdfunding-bean</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
(6)manager-impl
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>Atcrowdfunding-manager-impl</artifactId>
<dependencies>
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>Atcrowdfunding-manager-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</dependency>
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>Atcrowdfunding-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>Atcrowdfunding-potal-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<parent>
<groupId>com.atguigu.maven</groupId>
<artifactId>01-Atcrowdfunding-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../01-Atcrowdfunding-parent/pom.xml</relativePath>
</parent>
</project>
(7)potal-api
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.atguigu.maven</groupId>
<artifactId>Atcrowdfunding-potal-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>Atcrowdfunding-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>Atcrowdfunding-bean</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>Atcrowdfunding-manager-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
(8)potal-impl
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.atguigu.maven</groupId>
<artifactId>Atcrowdfunding-potal-impl</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>Atcrowdfunding-potal-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>Atcrowdfunding-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</dependency>
</dependencies>
<parent>
<groupId>com.atguigu.maven</groupId>
<artifactId>01-Atcrowdfunding-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../01-Atcrowdfunding-parent/pom.xml</relativePath>
</parent>
</project>
3.main/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>atcrowdfunding</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/spring-*.xml</param-value>
</context-param>
<!-- 监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 字符编码过滤器 -->
<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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<servlet-name>springmvc</servlet-name>
</filter-mapping>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.htm</url-pattern>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!-- 会话超时时间 -->
<session-config>
<session-timeout>60</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
4.main/spring-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.atguigu.atcrowdfunding.*" >
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 加载外部属性配置文件 -->
<bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:config/jdbc.properties" />
</bean>
<!-- 配置C3P0数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"></property>
<property name="dataSource" ref="dataSource"></property>
<property name="mapperLocations">
<list>
<value>classpath*:mybatis/mapper-*.xml</value>
</list>
</property>
</bean>
<!-- 扫描Mapper映射配置 -->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- <property name="basePackage" value="com.atguigu.atcrowdfunding.manager.dao,com.atguigu.atcrowdfunding.potal.dao"/> -->
<property name="basePackage" value="com.atguigu.atcrowdfunding.*.dao"/>
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" rollback-for="java.lang.Exception"/>
<tx:method name="query*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor advice-ref="transactionAdvice" pointcut="execution(* com.atguigu.atcrowdfunding..*Service.*(..))"/>
</aop:config>
</beans>
5.main/springmvc-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.atguigu.atcrowdfunding.*" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 启用注解功能 -->
<context:annotation-config />
<!-- 字符串字符编码转换 -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" >
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</list>
</property>
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<bean id="exceptionResolver"
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">error/error</prop>
</props>
</property>
</bean>
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" p:defaultEncoding="UTF-8">
<property name="maxUploadSize" value="2097152"/>
<property name="resolveLazily" value="true"/>
</bean>
</beans>
6./Atcrowdfunding-main/src/main/resources/config/jdbc.properties
jdbc.user=root
jdbc.password=123456
jdbc.url=jdbc:mysql://172.20.184.149:3305/atcrowdfunding170615?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=false
jdbc.driver=com.mysql.cj.jdbc.Driver
等号前后不能有空格,用户名密码前后不能用空格
7.log4j.properties
# DEBUG < INFO < WARN < ERROR < FATAL
log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss S} %5p %c:%l - %m%n
#log4j.logger.org.hibernate=INFO
8.使用弹窗提示消息
(1)加入弹窗库
<script type="text/javascript" src="${APP_PATH }/jquery/layer/layer.js"></script>
(2)代码
function dologin() {
/* $("#loginForm").submit(); */
var floginacct = $("#floginacct");
var fuserpswd = $("#fuserpswd");
var ftype = $("#ftype");
if($.trim(floginacct.val())==""){
// alert("用户账号不能为空,请重新输入!");
/**
* time:1000, 显示1000ms
icon:5, 显示第5个图案,一个哭脸
shift:6 弹窗抖动
*/
layer.msg("用户账号不能为空,请重新输入!",{time:1000, icon:5, shift:6}, function(){
floginacct.val("");
floginacct.focus();
});
return false; //结束if
}
if($.trim(fuserpswd.val())==""){
// alert("用户密码不能为空,请重新输入!");
layer.msg("用户密码不能为空,请重新输入!",{time:1000, icon:5, shift:6}, function(){
fuserpswd.val("");
fuserpswd.focus();
});
return false;
}
var loadingIndex = -1 ;
$.ajax({
type : "POST",
data : {
loginacct : floginacct.val(),
userpswd : fuserpswd.val(),
type : ftype.val()
},
url : "${APP_PATH}/doLogin.do",
beforeSend : function(){
loadingIndex = layer.msg('处理中', {icon: 16});//转圈的图案
//一般做表单数据校验.
return true ;
},
success : function(result){ //{"success":true} 或 {"success":false,"message":"登录失败!"}
if(result.success){
layer.close(loadingIndex);//关闭loadingIndex = layer.msg('处理中', {icon: 16})的layer
window.location.href="${APP_PATH}/main.htm";
}else{
layer.msg(result.message, {time:1000, icon:5, shift:6});
}
},
error : function(){
layer.msg("登录失败!", {time:1000, icon:5, shift:6});
}
});
}
9.用户查询,自己手写分页
(1)时序图
(2)手写分页,同步,见
https://blog.csdn.net/weixin_44595287/article/details/102949425
(3)手写分页,异步,见
https://blog.csdn.net/weixin_44595287/article/details/102953678
(4)用户模糊查询
①从表单中用id 提取模糊查询文本id="queryText" , 和激发按钮函数 id="queryBtn"
<form class="form-inline" role="form" style="float: left;">
<div class="form-group has-feedback">
<div class="input-group">
<div class="input-group-addon">查询条件</div>
<input id="queryText" class="form-control has-success" type="text"
placeholder="请输入查询条件">
</div>
</div>
<button id="queryBtn" type="button" class="btn btn-warning">
<i class="glyphicon glyphicon-search"></i> 查询
</button>
</form>
②用#提取queryText, 和激活按钮
$("#queryBtn").click(function(){
var queryText = $("#queryText").val();
jsonObj.queryText = queryText ; //将值传入jsonObj
queryPageUser(1);
});
③修改UserController,注意对出现%时需要转义
//异步分页模糊查询
@ResponseBody
@RequestMapping("/doIndex")
public Object index(@RequestParam(value="pageno",required=false,defaultValue="1") Integer pageno,
@RequestParam(value="pagesize",required=false,defaultValue="10") Integer pagesize, String queryText){
AjaxResult result = new AjaxResult();
try {
Map paramMap = new HashMap();
paramMap.put("pageno", pageno);
paramMap.put("pagesize", pagesize);
if(StringUtil.isNotEmpty(queryText)){
if(queryText.contains("%")){
//sql語句中默认%为匹配任何字符,需要转义,还需要其他转义,所以一共需要4个\
queryText = queryText.replaceAll("%", "\\\\%");
}
paramMap.put("queryText", queryText); // \%
}
Page page = userService.queryPage(paramMap);//Map可以使用Mybatis的条件函数
result.setSuccess(true);
result.setPage(page);
} catch (Exception e) {
result.setSuccess(false);
e.printStackTrace();
result.setMessage("查询数据失败!");
}
return result; //以流的形式传入序列化JSON数据
}
④修改UserMapper.java
//加模糊查询
List<User> queryList(Map<String, Object> paramMap);
Integer queryCount(Map<String, Object> paramMap);
⑤修改UsrMapper.xml,使用条件进行模糊查询。concat用于sql拼接字符串
<select id="queryList" resultMap="BaseResultMap ">
select id, loginacct, userpswd,
username, email, createtime
from t_user
<where>
<if test="queryText!=null">loginacct like concat("%",#{queryText},"%")</if>
</where>
limit #{startIndex},#{pagesize}
</select>
<select id="queryCount" resultType="int">
select count(*)
from t_user
<where>
<if test="queryText!=null">loginacct like concat("%",#{queryText},"%")</if>
</where>
</select>
10.新增user功能
(1)发出请求
<button type="button" class="btn btn-primary"
style="float: right;" onclick="window.location.href='${APP_PATH}/user/add.htm'">
<i class="glyphicon glyphicon-plus"></i> 新增
</button>
(2)UserController
@RequestMapping("/add")
public String add(){
return "user/add";
}
//增加
@ResponseBody
@RequestMapping("/doAdd")
public Object doAdd(User user){ //由前端传来的数据自动封装成User
AjaxResult result = new AjaxResult();
try {
int count = userService.saveUser(user);//需要在impl中手动添加默认密码和createtime
result.setSuccess(count==1);
} catch (Exception e) {
result.setSuccess(false);
e.printStackTrace();
result.setMessage("添加数据失败!");
}
return result; //以流的形式传入序列化JSON数据
}
(3)提取表单数据 id="floginacct",for使用和id相同的值,可以使点击lable标签也能选中input表单,使用体验好
<div class="form-group">
<label for="floginacct">登陆账号</label>
<input type="text" class="form-control" id="floginacct" placeholder="请输入登陆账号">
</div>
<div class="form-group">
<label for="fusername">用户名称</label>
<input type="text" class="form-control" id="fusername" placeholder="请输入用户名称">
</div>
<div class="form-group">
<label for="femail">邮箱地址</label>
<input type="email" class="form-control" id="femail" placeholder="请输入邮箱地址">
<p class="help-block label label-warning">请输入合法的邮箱地址, 格式为: xxxx@xxxx.com</p>
</div>
(4)提取表单对象和值,传到后方封装
var floginacct = $("#floginacct");提取对象
"loginacct" : floginacct.val() 提取值
<script type="text/javascript">
$(function () {
$(".list-group-item").click(function(){
if ( $(this).find("ul") ) {
$(this).toggleClass("tree-closed");
if ( $(this).hasClass("tree-closed") ) {
$("ul", this).hide("fast");
} else {
$("ul", this).show("fast");
}
}
});
});
$("#addBtn").click(function(){
var floginacct = $("#floginacct");
var fusername = $("#fusername");
var femail = $("#femail");
$.ajax({
type : "POST",
data : {
"loginacct" : floginacct.val(),
"username" : fusername.val(),
"email" : femail.val()
},
url : "${APP_PATH}/user/doAdd.do",
beforeSend : function() {
return true ;
},
success : function(result){
if(result.success){
window.location.href="${APP_PATH}/user/index.htm";
}else{
layer.msg("保存用户失败", {time:1000, icon:5, shift:6});
}
},
error : function(){
layer.msg("保存失败", {time:1000, icon:5, shift:6});
}
});
});
</script>
(5)成功后调回主页面
window.location.href="${APP_PATH}/user;成功后调回主页面
JavaScript中使用window.location.href跳转,为同步请求,异步用function
(6)重置功能
见笔记
$("#resetBtn").click(function(){
$("#updateForm")[0].reset();
});
11.修改(加回显)、删除、批量删除
(1)先回显,再修改,
①同步使用发出请求,带上id用于查询后回显
οnclick="window.location.href=\'${APP_PATH}/user/toUpdate.htm?id='+n.id+'\' "
content+=' <button type="button" onclick="window.location.href=\'${APP_PATH}/user/toUpdate.htm?id='+n.id+'\'" class="btn btn-primary btn-xs"><i class=" glyphicon glyphicon-pencil"></i></button>';
UserController.
查询回显数据,存入map返回
@RequestMapping("/toUpdate")
public String toUpdate(Integer id,Map map){
User user = userService.getUserById(id);
map.put("user", user);
return "user/update";
}
②/user/update.jsp
回显,提取回显值
value="${user.loginacct }
<form id="updateForm">
<div class="form-group">
<label for="exampleInputPassword1">登陆账号</label> <input type="text"
class="form-control" id="floginacct" value="${user.loginacct }">
</div>
<div class="form-group">
<label for="exampleInputPassword1">用户名称</label> <input type="text"
class="form-control" id="fusername" value="${user.username }">
</div>
<div class="form-group">
<label for="exampleInputEmail1">邮箱地址</label> <input type="email"
class="form-control" id="femail" value="${user.email }">
<p class="help-block label label-warning">请输入合法的邮箱地址, 格式为:
xxxx@xxxx.com</p>
</div>
③修改,异步刷新
$("#updateBtn").click(function(){
var floginacct = $("#floginacct");
var fusername = $("#fusername");
var femail = $("#femail");
$.ajax({
type : "POST",
data : {
"loginacct" : floginacct.val(),
"username" : fusername.val(),
"email" : femail.val(),
"id" : "${user.id}"
},
url : "${APP_PATH}/user/doUpdate.do",
beforeSend : function() {
return true ;
},
success : function(result){
if(result.success){
window.location.href="${APP_PATH}/user/index.htm";
}else{
layer.msg("修改用户失败", {time:1000, icon:5, shift:6});
}
},
error : function(){
layer.msg("修改失败", {time:1000, icon:5, shift:6});
}
});
});
(2)删除
①发出请求,异步
异步使用
οnclick="deleteUser('+n.id+',\''+n.loginacct+'\')" //进入方法
content+=' <button type="button" onclick="deleteUser('+n.id+',\''+n.loginacct+'\')" class="btn btn-danger btn-xs"><i class=" glyphicon glyphicon-remove"></i></button>';
function deleteUser(id,loginacct){
layer.confirm("确认要删除["+loginacct+"]用户吗?", {icon: 3, title:'提示'}, function(cindex){
layer.close(cindex);
$.ajax({
type : "POST",
data : {
"id" : id
},
url : "${APP_PATH}/user/doDelete.do",
beforeSend : function() {
return true ;
},
success : function(result){
if(result.success){
window.location.href="${APP_PATH}/user/index.htm";
}else{
layer.msg("删除用户失败", {time:1000, icon:5, shift:6});
}
},
error : function(){
layer.msg("删除失败", {time:1000, icon:5, shift:6});
}
});
}, function(cindex){
layer.close(cindex);
});
}
②在UserController中添加doDelete方法,并返回result
@ResponseBody
@RequestMapping("/doDelete")
public Object doDelete(Integer id){
AjaxResult result = new AjaxResult();
try {
int count = userService.deleteUser(id);
result.setSuccess(count==1);
} catch (Exception e) {
result.setSuccess(false);
e.printStackTrace();
result.setMessage("删除数据失败!");
}
return result;
}
}
(3)批量删除(每一行封装为难点)
①取出全选框allcheckbox的对象
<th width="30"><input id="allCheckbox" type="checkbox"></th>
content+=' <td><input id="'+n.id+'" name="'+n.loginacct+'" type="checkbox"></td>';
②取出allcheckbox的当前是否被选中的值"#allCheckbox".checked
当点击全选框时,将全选框的值赋给表单的每个框
var checkStatus = this.checked; // 选中时为true,未选中时为false
$("tbody tr td input[type='checkbox']") //表示标签tbody下的tr下的td下的input里面type='checkbox'对象
prop("checked",checkStatus);赋值操作,将checkStatus复制给checked属性
$("#allCheckbox").click(function(){
var checkStatus = this.checked;
/* alert(checkStatus); */
$("tbody tr td input[type='checkbox']").prop("checked",checkStatus);
});
③点批量删除操作
<button type="button" class="btn btn-danger" id="deleteBatchBtn"
style="float: right; margin-left: 10px;">
<i class=" glyphicon glyphicon-remove"></i> 删除
</button>
④checkbox中放入值供取出
content+=' <td><input id="'+n.id+'" loginacct="'+n.loginacct+'" type="checkbox"></td>';
$("#deleteBatchBtn").click(function(){
var selectCheckbox = $("tbody tr td input:checked"); //true取出被选择的对象
if(selectCheckbox.length==0){ //计算选择对象的个数
layer.msg("请至少选择一个用户删除", {time:1000, icon:5, shift:6});
return false;
}
var jsonObj = {}; //将选择的每一行封装为一个对象
$.each(selectCheckbox,function(i,n){ //n为selectCheckbox里的元素,i为当前元素索引
jsonObj["datas["+i+"].id"] = n.id;
jsonObj["datas["+i+"].loginacct"] = n.loginacct; //从checkbox中取值
})
layer.confirm("确认要删除所选用户吗?", {icon: 3, title:'提示'}, function(cindex){
layer.close(cindex);
$.ajax({
type : "POST",
data : jsonObj,
url : "${APP_PATH}/user/doDeleteBatch.do",
beforeSend : function() {
return true ;
},
success : function(result){
if(result.success){
window.location.href="${APP_PATH}/user/index.htm";
}else{
layer.msg("删除用户失败", {time:1000, icon:5, shift:6});
}
},
error : function(){
layer.msg("删除失败", {time:1000, icon:5, shift:6});
}
});
}, function(cindex){
layer.close(cindex);
});
});
⑤Data.java
public class Data {
private List<User> datas = new ArrayList<User>();
public List<User> getDatas() {
return datas;
}
public void setDatas(List<User> datas) {
this.datas = datas;
}
}
⑥UserController
@ResponseBody
@RequestMapping("/doDeleteBatch")
public Object doDeleteBatch(Data data){ //由前端传来的数据自动封装成User
AjaxResult result = new AjaxResult();
try {
int count = userService.deleteBatchUserByVO(data);
result.setSuccess(count==data.getDatas().size());
} catch (Exception e) {
result.setSuccess(false);
e.printStackTrace();
result.setMessage("批量删除数据失败!");
}
return result;
}
⑦UserService
int deleteBatchUserByVO(Data data);
⑧UserServiceImpl
@Override
public int deleteBatchUserByVO(Data data) {
return userMapper.deleteBatchUserByVO(data.getDatas());
}
⑨UserMapper.java (MyBatis中使用List必须要指名@Param("userList")
int deleteBatchUserByVO(@Param("userList") List<User> userList);
⑩UserMapper.xml
<delete id="deleteBatchUserByVO" >
delete from t_user where id in
<foreach collection="userList" open="(" close=")" separator="," item="user">
#{user.id}
</foreach>
</delete>
12.抽取menu.jsp和top.jsp
两种方法引用
方法1:
<jsp:include page="/WEB-INF/common/top.jsp"></jsp:include>
方法2:
<%@ include file="/WEB-INF/jsp/common/top.jsp" %>
top.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<li style="padding-top: 8px;">
<div class="btn-group">
<button type="button"
class="btn btn-default btn-success dropdown-toggle"
data-toggle="dropdown">
<i class="glyphicon glyphicon-user"></i>${sessionScope.user.username }<span
class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li><a href="#"><i class="glyphicon glyphicon-cog"></i> 个人设置</a></li>
<li><a href="#"><i class="glyphicon glyphicon-comment"></i>
消息</a></li>
<li class="divider"></li>
<li><a href="${APP_PATH }/logout.do"><i
class="glyphicon glyphicon-off"></i> 退出系统</a></li>
</ul>
</div>
</li>
<li style="margin-left: 10px; padding-top: 8px;">
<button type="button" class="btn btn-default btn-danger">
<span class="glyphicon glyphicon-question-sign"></span> 帮助
</button>
</li>
menu.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<ul style="padding-left:0px;" class="list-group">
<c:forEach items="${sessionScope.permissionRoot.children }" var="permission">
<c:if test="${empty permission.children}">
<li class="list-group-item tree-closed" >
<a href="${APP_PATH }/${permission.url }"><i class="${permission.icon}"></i> ${permission.name }</a>
</li>
</c:if>
<c:if test="${not empty permission.children}" >
<li class="list-group-item tree-closed">
<span><i class="${permission.icon}"></i> ${permission.name } <span class="badge" style="float:right">${fn:length(permission.children)} <%-- ${permission.children.size() } --%></span></span>
<ul style="margin-top:10px;display:none;">
<c:forEach items="${permission.children }" var="innerPermission">
<li style="height:30px;">
<a href="${APP_PATH }/${innerPermission.url}"><i class="${innerPermission.icon }"></i> ${innerPermission.name }</a>
</li>
</c:forEach>
</ul>
</li>
</c:if>
</c:forEach>
</ul>
13.被选中的菜单默认展开,并标红
(1)抽取为menu.js文件
function showMenu(){
var href = window.location.href ; // http://localhost:8888/Atcrowdfunding-main/user/index.htm
var host = window.location.host ;// localhost:8888
var index = href.indexOf(host);//7
var path = href.substring(index + host.length); // /Atcrowdfunding-main/user/index.htm
var contextPath = "${APP_PATH}"; // /Atcrowdfunding-main
var pathAddress = path.substring(contextPath.length+1);// user/index.htm
var alink = $(".list-group a[href*='"+pathAddress+"']");//取出class="list-group"里面<a href*与pathAddress匹配成功的标签
alink.css("color","red");//设为红色
alink.parent().parent().parent().removeClass("tree-closed");//alink 的父父父标签,即<li class="list-group-item去掉tree-closed,即不关闭
alink.parent().parent().show();
}
(2)引用
<script type="text/javascript" src="${APP_PATH }/script/menu.js"></script>
(3)在函数中使用showMenu()调用
<script type="text/javascript">
$(function () {
$(".list-group-item").click(function(){
if ( $ (this).find("ul") ) {
$(this).toggleClass("tree-closed");
if ( $(this).hasClass("tree-closed") ) {
$("ul", this).hide("fast");
} else {
$("ul", this).show("fast");
}
}
});
queryPageUser(0);
showMenu();
});
14.用户管理模块,分配角色
(1)首先显示已有角色
①发出请求user/index.jsp
content+=' <button type="button" onclick="window.location.href=\'${APP_PATH}/user/assignRole.htm?id='+n.id+'\'" class="btn btn-success btn-xs"><i class=" glyphicon glyphicon-check"></i></button>';
②UserController
@RequestMapping("/assignRole")
public String assignRole(Integer id,Map map){
List<Role> allListRole = userService.queryAllRole();
List<Integer> roleIds = userService.queryRoleByUserid(id);
List<Role> leftRoleList = new ArrayList<Role>(); //未分配角色
List<Role> rightRoleList = new ArrayList<Role>(); //已分配角色
for (Role role : allListRole) {
if(roleIds.contains(role.getId())){
rightRoleList.add(role);
}else{
leftRoleList.add(role);
}
}
map.put("leftRoleList", leftRoleList);
map.put("rightRoleList", rightRoleList);
return "user/assignrole";
}
③UserMapper.xml
<select id="queryRoleByUserid" parameterType="int" resultType="int">
select roleid from t_user_role where userid = #{id}
</select>
④显示已有和未有角色user/assignrole.jsp
<form role="form" class="form-inline">
<div class="form-group">
<label for="exampleInputPassword1">未分配角色列表</label><br>
<select id="leftRoleList" class="form-control" multiple size="10" style="width:250px;overflow-y:auto;">
<c:forEach items="${leftRoleList }" var="role">
<option value="${role.id }">${role.name }</option>
</c:forEach>
</select>
</div>
<div class="form-group">
<ul>
<li id="leftToRightBtn" class="btn btn-default glyphicon glyphicon-chevron-right"></li>
<br/>
<li id="rightToLeftBtn" class="btn btn-default glyphicon glyphicon-chevron-left" style="margin-top:20px;"></li>
</ul>
</div>
<div class="form-group" style="margin-left:40px;">
<label for="exampleInputPassword1">已分配角色列表</label><br>
<select id="rightRoleList" class="form-control" multiple size="10" style="width:250px;overflow-y:auto;">
<c:forEach items="${rightRoleList }" var="role">
<option value="${role.id }">${role.name }</option>
</c:forEach>
</select>
</div>
</form>
(2)添加和删除角色
①异步操作
$("#leftToRightBtn").click(function(){
var selectedOptions = $("#leftRoleList option:selected");//选中的对象
var num = selectedOptions.size();//计算选中的个数
if(num==0){
layer.msg("左侧请至少选择一个角色", {time:1000, icon:5, shift:6});
return false;
}
var jsonObj = {
userid : "${param.id}"
};
$.each(selectedOptions,function(i,n){
jsonObj["ids["+i+"]"] = this.value ;
});
var loadingIndex = -1 ;
$.ajax({
type : "POST",
data : jsonObj,
url : "${APP_PATH}/user/doAssignRole.do",
beforeSend : function(){
loadingIndex = layer.load(2, {time: 10*1000});
return true ;
},
success : function(result){
layer.close(loadingIndex);
if(result.success){
$("#rightRoleList").append(selectedOptions.clone());//rightRoleList中添加选中的option对象
selectedOptions.remove();//移除选中的option对象
}else{
layer.msg(result.message, {time:1000, icon:5, shift:6});
}
},
error : function(){
layer.msg("操作失败!", {time:1000, icon:5, shift:6});
}
});
});
$("#rightToLeftBtn").click(function(){
var selectedOptions = $("#rightRoleList option:selected");
var num = selectedOptions.size();
if(num==0){
layer.msg("右侧请至少选择一个角色", {time:1000, icon:5, shift:6});
return false;
}
var jsonObj = {
userid : "${param.id}"
};
$.each(selectedOptions,function(i,n){
jsonObj["ids["+i+"]"] = this.value ;
});
var loadingIndex = -1 ;
$.ajax({
type : "POST",
data : jsonObj,
url : "${APP_PATH}/user/doUnAssignRole.do",
beforeSend : function() {
loadingIndex = layer.load(2, {time: 10*1000});
return true ;
},
success : function(result){
layer.close(loadingIndex);
if(result.success){
$("#leftRoleList").append(selectedOptions.clone());
selectedOptions.remove();
}else{
layer.msg(result.message, {time:1000, icon:5, shift:6});
}
},
error : function(){
layer.msg("操作失败!", {time:1000, icon:5, shift:6});
layer.close(loadingIndex);
}
});
});
②UserController
@ResponseBody
@RequestMapping("/doAssignRole")
public Object doAssignRole(Integer userid, Data data){ //List<Integer> ids
AjaxResult result = new AjaxResult();
try {
int count = userService.saveUserRoleRelationship(userid,data);
result.setSuccess(true);
} catch (Exception e) {
result.setSuccess(false);
e.printStackTrace();
result.setMessage("添加角色失败!");
}
return result;
}
@ResponseBody
@RequestMapping("/doUnAssignRole")
public Object doUnAssignRole(Integer userid,Data data){ //List<Integer> ids
AjaxResult result = new AjaxResult();
try {
userService.deleteUserRoleRelationship(userid,data);;
result.setSuccess(true);
} catch (Exception e) {
result.setSuccess(false);
e.printStackTrace();
result.setMessage("取消角色失败!");
}
return result;
}
③UserMapper.xml
<insert id="saveUserRoleRelationship">
<foreach collection="data.ids" item="roleid" separator=";">
insert into t_user_role(userid,roleid) values(#{userid},#{roleid})
</foreach>
</insert>
<delete id="deleteUserRoleRelationship" >
delete from t_user_role where userid=#{userid} and roleid in
<foreach collection="data.ids" item="roleid" open="(" separator="," close=")">
#{roleid}
</foreach>
</delete>
15.以树形z-tree展示权限图,添加,修改,删除【本项目一共3层,根节点->一级节点->叶子节点】
(1)引入z-tree插件
<script src="${APP_PATH }/ztree/jquery.ztree.all-3.5.min.js"></script>
(2)permission/index.jsp
<script type="text/javascript">
$(function () {
$(".list-group-item").click(function(){
if ( $(this).find("ul") ) {
$(this).toggleClass("tree-closed");
if ( $(this).hasClass("tree-closed") ) {
$("ul", this).hide("fast");
} else {
$("ul", this).show("fast");
}
}
});
loadData();
});
var setting = {
view : {
addDiyDom: function(treeId, treeNode){
var icoObj = $("#" + treeNode.tId + "_ico"); // tId = permissionTree_1, $("#permissionTree_1_ico")
if ( treeNode.icon ) {
icoObj.removeClass("button ico_docu ico_open").addClass(treeNode.icon).css("background","");
}
},
addHoverDom: function(treeId, treeNode){ //设置自定义按钮组,在节点后面悬停显示增删改按钮组.
var aObj = $("#" + treeNode.tId + "_a"); // tId = permissionTree_1, ==> $("#permissionTree_1_a")
aObj.attr("href", "javascript:;"); // 取消当前链接事件.
if (treeNode.editNameFlag || $("#btnGroup"+treeNode.tId).length>0) return;
var s = '<span id="btnGroup'+treeNode.tId+'">';
if ( treeNode.level == 0 ) { //根节点
s += '<a class="btn btn-info dropdown-toggle btn-xs" style="margin-left:10px;padding-top:0px;" href="#" onclick="window.location.href=\'${APP_PATH}/permission/toAdd.htm?id='+treeNode.id+'\'" > <i class="fa fa-fw fa-plus rbg "></i></a>';
} else if ( treeNode.level == 1 ) { //分支节点
s += '<a class="btn btn-info dropdown-toggle btn-xs" style="margin-left:10px;padding-top:0px;" href="#" onclick="window.location.href=\'${APP_PATH}/permission/toUpdate.htm?id='+treeNode.id+'\'" title="修改权限信息"> <i class="fa fa-fw fa-edit rbg "></i></a>';
if (treeNode.children.length == 0) {
s += '<a class="btn btn-info dropdown-toggle btn-xs" style="margin-left:10px;padding-top:0px;" href="#" onclick="deletePermission('+treeNode.id+',\''+treeNode.name+'\')"> <i class="fa fa-fw fa-times rbg "></i></a>';
}
s += '<a class="btn btn-info dropdown-toggle btn-xs" style="margin-left:10px;padding-top:0px;" href="#" onclick="window.location.href=\'${APP_PATH}/permission/toAdd.htm?id='+treeNode.id+'\'"> <i class="fa fa-fw fa-plus rbg "></i></a>';
} else if ( treeNode.level == 2 ) { //叶子节点
s += '<a class="btn btn-info dropdown-toggle btn-xs" style="margin-left:10px;padding-top:0px;" href="#" onclick="window.location.href=\'${APP_PATH}/permission/toUpdate.htm?id='+treeNode.id+'\'" title="修改权限信息"> <i class="fa fa-fw fa-edit rbg "></i></a>';
s += '<a class="btn btn-info dropdown-toggle btn-xs" style="margin-left:10px;padding-top:0px;" href="#" onclick="deletePermission('+treeNode.id+',\''+treeNode.name+'\')"> <i class="fa fa-fw fa-times rbg "></i></a>';
}
s += '</span>';
aObj.after(s);
},
removeHoverDom: function(treeId, treeNode){
$("#btnGroup"+treeNode.tId).remove();
}
}
};
/* var zNodes =[
{ name:"父节点1 - 展开", open:true,
children: [
{ name:"父节点11 - 折叠",
children: [
{ name:"叶子节点111"},
{ name:"叶子节点112"},
{ name:"叶子节点113"},
{ name:"叶子节点114"}
]},
{ name:"父节点12 - 折叠",
children: [
{ name:"叶子节点121"},
{ name:"叶子节点122"},
{ name:"叶子节点123"},
{ name:"叶子节点124"}
]},
{ name:"父节点13 - 没有子节点", isParent:true}
]},
{ name:"父节点2 - 折叠",
children: [
{ name:"父节点21 - 展开", open:true,
children: [
{ name:"叶子节点211"},
{ name:"叶子节点212"},
{ name:"叶子节点213"},
{ name:"叶子节点214"}
]},
{ name:"父节点22 - 折叠",
children: [
{ name:"叶子节点221"},
{ name:"叶子节点222"},
{ name:"叶子节点223"},
{ name:"叶子节点224"}
]},
{ name:"父节点23 - 折叠",
children: [
{ name:"叶子节点231"},
{ name:"叶子节点232"},
{ name:"叶子节点233"},
{ name:"叶子节点234"}
]}
]},
{ name:"父节点3 - 没有子节点", isParent:true}
]; */
function loadData(){
$.ajax({
url:"${APP_PATH}/permission/loadData.do",
type:"post",
success:function(result){
if(result.success){
var zNodes = result.data ;
$.fn.zTree.init($("#treeDemo"), setting, zNodes);
}else{
alert("加载数据失败...");
}
}
});
}
/* $(document).ready(function(){
$.fn.zTree.init($("#treeDemo"), setting, zNodes);
}); */
function deletePermission(id,name){
layer.confirm("确定要删除["+name+"]许可吗?", {icon: 3, title:'提示'}, function(cindex){
layer.close(cindex);
$.ajax({
url:"${APP_PATH}/permission/deletePermission.do",
data : {
"id" : id
},
type:"post",
success:function(result){
if(result.success){
loadData();
}else{
alert("删除许可数据失败...");
}
}
});
}, function(cindex){
layer.close(cindex);
});
}
</script>
(3)PermissionController.java
只查询一次数据库,数据放入map中,后用于调用,效率高
@RequestMapping("/index")
public String index(){
return "permission/index";
}
@ResponseBody
@RequestMapping("/loadData")
public Object loadData(){
AjaxResult result = new AjaxResult();
try {
List<Permission> root = new ArrayList<Permission>();
List<Permission> childrenPermissions = permissionService.queryAllPermission();
Map<Integer,Permission> map = new HashMap<Integer,Permission>();
for(Permission innerpermission: childrenPermissions){
map.put(innerpermission.getId(),innerpermission );//把查出来的数据全部放入map中使用,不
}
for(Permission permission : childrenPermissions){ //利用递归,一个嵌套的Jason数据,通过F12-network看请求发送的数据
Permission child = permission;
if(child.getPid()==null){
root.add(child);
}else{
Permission parent = map.get(child.getPid());
parent.getChildren().add(child);
}
}
result.setSuccess(true);
result.setData(root);
} catch (Exception e) {
result.setSuccess(false);
e.printStackTrace();
result.setMessage("加载许可数失败!");
}
return result ;
}
递归最后得到的data(root)结果为嵌套的json,传回前端由z-tree自动解析树形展示
/* * {"success":true,"message":null,"page":null,
* "data":[{"id":null,"pid":null,"name":"系统权限菜单","icon":null,"url":null,"open":true,
* "children":[{"id":null,"pid":null,"name":"控制面板","icon":null,"url":null,"open":false,"children":null},
* {"id":null,"pid":null,"name":"权限管理","icon":null,"url":null,"open":false,"children":null}]}]}*/
(4)修改、删除和role操作差不多,省略
16.为每位角色分配权限
(1)显示权限树,并在已有权限复选框打勾
role/assignPermission.jsp
<script src="${APP_PATH }/jquery/jquery-2.1.1.min.js"></script>
<script src="${APP_PATH }/bootstrap/js/bootstrap.min.js"></script>
<script src="${APP_PATH }/script/docs.min.js"></script>
<script src="${APP_PATH }/ztree/jquery.ztree.all-3.5.min.js"></script>
<script src="${APP_PATH}/jquery/layer/layer.js"></script>
<script type="text/javascript">
$(function () {
$(".list-group-item").click(function(){
if ( $(this).find("ul") ) {
$(this).toggleClass("tree-closed");
if ( $(this).hasClass("tree-closed") ) {
$("ul", this).hide("fast");
} else {
$("ul", this).show("fast");
}
}
});
var setting = {
check : {
enable : true //在树节点前显示复选框
},
view: {
selectedMulti: true,//不支持多选
addDiyDom: function(treeId, treeNode){
var icoObj = $("#" + treeNode.tId + "_ico"); // tId = permissionTree_1, $("#permissionTree_1_ico")
if ( treeNode.icon ) {
icoObj.removeClass("button ico_docu ico_open").addClass(treeNode.icon).css("background","");
}
},
},
async: {
enable: true, //采用异步
url:"${APP_PATH}/role/loadDataAsync.do?roleid=${param.roleid}", // ?id=1&n=xxx&lv=2
autoParam:["id", "name=n", "level=lv"]
},
callback: {
onClick : function(event, treeId, json) {
}
}
};
//异步加载树:注意问题,服务器端返回的结果必须是一个数组.
$.fn.zTree.init($("#treeDemo"), setting); //异步加载树的数据.
//$.fn.zTree.init($("#treeDemo"), setting , ztreeJSON);//同步加载树的数据.
});
$("#assignPermissionBtn").click(function(){
var jsonObj = {
"roleid" : "${param.roleid}"
};
var treeObj = $.fn.zTree.getZTreeObj("treeDemo");
var checkedNodes = treeObj.getCheckedNodes(true); // 获取被选中的节点
$.each(checkedNodes,function(i,n){
jsonObj["ids["+i+"]"] = n.id;
});
if(checkedNodes.length == 0){
layer.msg("请选择分配许可,至少分配一个许可!", {time:1000, icon:5, shift:6});
}else{
var loadingIndex = -1 ;
$.ajax({
type : "POST",
url : "${APP_PATH}/role/doAssignPermission.do",
data : jsonObj,
beforeSend : function(){
loadingIndex = layer.msg('正在分配许可...', {icon: 16});
return true ;
},
success : function(result){
layer.close(loadingIndex);
if(result.success){
layer.msg("分配成功", {time:1000, icon:6});
}else{
layer.msg("分配失败", {time:1000, icon:5, shift:6});
}
},
error : function(){
layer.msg("操作失败!", {time:1000, icon:5, shift:6});
}
});
}
});
</script>
(2)在已有的权限复选框打勾,用checked控制,checke为true时打勾(由z-tree提供)
RoleController.java
@ResponseBody
@RequestMapping("/loadDataAsync")
public Object loadDataAsync(Integer roleid){
List<Permission> root = new ArrayList<Permission>();
List<Permission> childredPermissons = permissionService.queryAllPermission();
//根据角色id查询该角色之前所分配过的许可.
List<Integer> permissonIdsForRoleid = permissionService.queryPermissionidsByRoleid(roleid);
Map<Integer,Permission> map = new HashMap<Integer,Permission>();//100
for (Permission innerpermission : childredPermissons) {
map.put(innerpermission.getId(), innerpermission);
if(permissonIdsForRoleid.contains(innerpermission.getId())){
innerpermission.setChecked(true); //checked默认false,如果权限已有设checked为true
}
}
for (Permission permission : childredPermissons) { //100
//通过子查找父
//子菜单
Permission child = permission ; //假设为子菜单
if(child.getPid() == null ){
root.add(permission);
}else{
//父节点
Permission parent = map.get(child.getPid());
parent.getChildren().add(child);
}
}
return root ;
}
(3)点击分配许可,流程为,先删除所有的权限,再将勾选的权限保存
①RoleController
@ResponseBody
@RequestMapping("/doAssignPermission")
public Object doAssignPermission(Integer roleid, Data datas){
AjaxResult result = new AjaxResult();
try {
int count = roleService.saveRolePermissionRelationship(roleid,datas);
result.setSuccess(count==datas.getIds().size());
} catch (Exception e) {
e.printStackTrace();
result.setSuccess(false);
}
return result;
}
②RoleServiceImpl
@Override
public int saveRolePermissionRelationship(Integer roleid, Data datas) {
roleDao.deleteRolePermissionRelationship(roleid);//先删除
int totalCount = 0 ;
List<Integer> ids = datas.getIds();
for (Integer permissionid : ids) {
RolePermission rp = new RolePermission();
rp.setRoleid(roleid);
rp.setPermissionid(permissionid);
int count = roleDao.insertRolePermission(rp);//后保存
totalCount += count ;
}
return totalCount;
}
17.分配登录人员的显示菜单
(1)menu.jsp
<c:forEach items="${sessionScope.permissionRoot.children }" var="permission">
<c:if test="${empty permission.children}">
<li class="list-group-item tree-closed" >
<a href="${APP_PATH }/${permission.url }"><i class="${permission.icon}"></i> ${permission.name }</a>
</li>
</c:if>
<c:if test="${not empty permission.children}" >
<li class="list-group-item tree-closed">
<span><i class="${permission.icon}"></i> ${permission.name } <span class="badge" style="float:right">${fn:length(permission.children)} <%-- ${permission.children.size() } --%></span></span>
<ul style="margin-top:10px;display:none;">
<c:forEach items="${permission.children }" var="innerPermission">
<li style="height:30px;">
<a href="${APP_PATH }/${innerPermission.url}"><i class="${innerPermission.icon }"></i> ${innerPermission.name }</a>
</li>
</c:forEach>
</ul>
</li>
</c:if>
</c:forEach>
(2)DispatcherController.java
@ResponseBody
@RequestMapping("/doLogin")
public Object doLogin(String loginacct,String userpswd,String type,HttpSession session){
AjaxResult result = new AjaxResult();
try {
Map<String,Object> paramMap = new HashMap<String,Object>();
paramMap.put("loginacct", loginacct);
paramMap.put("userpswd", MD5Util.digest(userpswd));
paramMap.put("type", type);
User user = userService.queryUserLogin(paramMap);
session.setAttribute(Const.LOGIN_USER, user);
//加载当前用户所拥有的许可权限
List<Permission> myPermissions = userService.queryPermissionByUserid(user.getId());
Permission permissionRoot = null;
Map<Integer,Permission> map = new HashMap<Integer,Permission>();
for(Permission innerpermission: myPermissions){
map.put(innerpermission.getId(),innerpermission );//把查出来的数据全部放入map中使用,不
}
for(Permission permission : myPermissions){ //利用递归,一个嵌套的Jason数据,通过F12-network看请求发送的数据
Permission child = permission;
if(child.getPid()==null){
permissionRoot=permission;
}else{
Permission parent = map.get(child.getPid());
parent.getChildren().add(child);
}
}
session.setAttribute("permissionRoot", permissionRoot);
result.setSuccess(true);
} catch (Exception e) {
result.setMessage("登录失败,用户名或密码错误");
e.printStackTrace();
result.setSuccess(false);
}
return result;
}
(3)UserMapper.xml,五表联查
<select id="queryPermissionByUserid" parameterType="int" resultType="com.atguigu.atcrowdfunding.bean.Permission">
SELECT DISTINCT t_permission.id, t_permission.pid, t_permission.name,t_permission.icon,t_permission.url
FROM t_user,t_role,t_user_role,t_role_permission,t_permission
WHERE
t_user.`id`=t_user_role.`userid`
AND t_user_role.`roleid`=t_role_permission.`roleid`
AND t_role_permission.`permissionid`=t_permission.`id`
AND t_user.id=#{id} order by t_permission.id
</select>
18.登录权限拦截
(1)LoginInterceptor.java
public class LoginInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
Set<String> uri = new HashSet<String>(); //白名单,不拦截
uri.add("/index.htm");
uri.add("/user/reg.do");
uri.add("/user/reg.htm");
uri.add("/login.htm");
uri.add("/doLogin.do");
uri.add("/loginout.do");
//获取请求路径
String servletPath = request.getServletPath();
if(uri.contains(servletPath)){
return true; //放行,进入postHandle
}
HttpSession session = request.getSession();
User user = (User) session.getAttribute(Const.LOGIN_USER);
if(user!=null){ //已登录
return true;
}else{
response.sendRedirect(request.getContextPath()+"/login.htm");
return false;
}
}
}
(2)在springmvc_context中声明拦截器
<!-- 声明拦截对象 -->
<mvc:interceptors>
<bean id="loginInterceptor" class="com.atguigu.atcrowdfunding.interceptor.LoginInterceptor"></bean>
</mvc:interceptors>
19.访问权限拦截
(1)服务器加载时,查询出所有需要控制权限的路径,放入application域供使用,避免每次都要查询数据库
StartSystemListener.java
public class StartSystemListener implements ServletContextListener {
//在服务器启动时,创建application对象时需要执行的方法.
@Override
public void contextInitialized(ServletContextEvent sce) {
//1.将项目上下文路径(request.getContextPath())放置到application域中.
ServletContext application = sce.getServletContext();
String contextPath = application.getContextPath();
application.setAttribute("APP_PATH", contextPath);
//加载所有的pemission路径
ApplicationContext ioc = WebApplicationContextUtils.getWebApplicationContext(application);
PermissionService permissionService = ioc.getBean(PermissionService.class);
List<Permission> queryAllPermission = permissionService.queryAllPermission();
Set<String> allURIs = new HashSet<String>();
for(Permission permission:queryAllPermission){
allURIs.add("/"+permission.getUrl());
}
application.setAttribute(Const.ALL_PERMISSION_URI, allURIs);
}
}
(2)AuthInterceptor.java
public class AuthInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
Set<String> allURIs = (Set<String>) request.getSession().getServletContext().getAttribute(Const.ALL_PERMISSION_URI);
String servletPath = request.getServletPath();
if(allURIs.contains(servletPath)){
Set<String> myURIs = (Set<String>) request.getSession().getAttribute(Const.MY_URIS);
if(myURIs.contains(servletPath)){
return true;
}else{
response.sendRedirect(request.getContextPath()+"/login.htm");//权限不足,返回登录界面
return false;
}
}else{
return true; //不属于分辨是否要拦截的路径,任何人都可以访问
}
}
}
(3)在springmvc_context中声明拦截器
<!-- 声明拦截对象 -->
<mvc:interceptors>
<bean id="loginInterceptor" class="com.atguigu.atcrowdfunding.interceptor.LoginInterceptor"></bean>
<bean id="authInterceptor" class="com.atguigu.atcrowdfunding.interceptor.AuthInterceptor"></bean>
</mvc:interceptors>
20.广告表单文件功能上传(单个图片格式校验,照片预览,上传)
(1)表单要设属性enctype="multipart/form-data", method="post",并在springmv-context.xml中配置
①add.jsp
<form id="advertForm" method="post" action="" enctype="multipart/form-data">
<div class="form-group">
<label for="name">广告名称</label>
<input type="text" class="form-control" id="name" name="name" placeholder="请输入广告名称">
</div>
<div class="form-group">
<label for="url">广告地址</label>
<input type="text" class="form-control" id="url" name="url" placeholder="请输入广告地址">
<br>
<img src="" style="display:none">
</div>
<div class="form-group">
<label for="advpic">广告图片</label>
<input type="file" class="form-control" id="advpic" name="advpic" placeholder="请输入广告图片">
</div>
<button id="saveBtn" type="button" class="btn btn-success"><i class="glyphicon glyphicon-plus"></i> 新增</button>
<button id="resetBtn" type="button" class="btn btn-danger"><i class="glyphicon glyphicon-refresh"></i> 重置</button>
</form>
②springmvc-context.xml
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" p:defaultEncoding="UTF-8">
<property name="maxUploadSize" value="2097152"/> //所有文件最大的大小,不是单个的大小
<property name="resolveLazily" value="true"/>
</bean>
(2)表单值异步提交
①需要加入jquery-form.min.js
<script src="${APP_PATH }/jquery/jquery-form/jquery-form.min.js"></script>
②add.jsp
图片预览,表单中一定要有
<br>
<img src="" style="display:none">
//图片格式校验
$(":file").change(function(event){
var f = $("#advpic").val();
if(!/\.(gif|jpg|jpeg|png|GIF|JPG|PNG)$/.test(f)){
layer.msg("图片类型必须是.gif,jpeg,jpg,png中的一种", {time:1000, icon:5, shift:6});
setTimeout(function(){
//延时要执行的事件
/* window.location.href = "${APP_PATH}/advert/add.htm"; */
$("#advertForm")[0].reset();
},500);
return false;
}
//图片预览
var files = event.target.files;
var file;
if (files && files.length > 0) {
file = files[0];
var URL = window.URL || window.webkitURL;
// 本地图片路径
var imgURL = URL.createObjectURL(file);
var imgObj = $(this).next().next(); //获取同辈紧邻的下一个元素
//console.log(imgObj);
imgObj.attr("src", imgURL);
imgObj.show();
}
});
//异步保存带图片的表单数据
$("#saveBtn").click(function(){
var options = {
url:"${APP_PATH}/advert/doAdd.do",
beforeSubmit : function(){
loadingIndex = layer.msg('数据正在保存中', {icon: 6});
return true ; //必须返回true,否则,请求终止.
},
success : function(result){
layer.close(loadingIndex);
if(result.success){
layer.msg("广告数据保存成功", {time:1000, icon:6});
window.location.href="${APP_PATH}/advert/index.htm";
}else{
layer.msg("广告数据保存失败", {time:1000, icon:5, shift:6});
}
}
};
$("#advertForm").ajaxSubmit(options);
});
$("#resetBtn").click(function(){
$("#advertForm")[0].reset();
});
③AdvertController.java
@ResponseBody
@RequestMapping("/doAdd")
public Object doAdd(HttpServletRequest request, Advert advert ,HttpSession session) {
AjaxResult result = new AjaxResult();
System.out.println(33);
try {
MultipartHttpServletRequest mreq = (MultipartHttpServletRequest)request;//文件需要将request转换为MultipartHttpServletRequest
MultipartFile mfile = mreq.getFile("advpic");
String name = mfile.getOriginalFilename();//java.jpg
String extname = name.substring(name.lastIndexOf(".")); // .jpg
String iconpath = UUID.randomUUID().toString()+extname; //232243343.jpg
ServletContext servletContext = session.getServletContext();
String realpath = servletContext.getRealPath("/pics");
System.out.println(realpath);
String path =realpath+ "\\adv\\"+iconpath;
mfile.transferTo(new File(path)); //上传图片
User user = (User)session.getAttribute(Const.LOGIN_USER);
advert.setUserid(user.getId());
advert.setStatus("1"); //未审核
advert.setIconpath(iconpath);
int count = advertService.insertAdvert(advert);
result.setSuccess(count==1);
} catch ( Exception e ) {
e.printStackTrace();
result.setSuccess(false);
}
return result;
}
④AdvertService,AdvertServieImpl,AdvertMapper省略
⑤异步提交文件表单方法解析
var options = {
target: '#output', //把服务器返回的内容放入id为output的元素中
beforeSubmit: showRequest, //提交前的回调函数
success: showResponse, //提交后的回调函数
//url: url, //默认是form的action, 如果申明,则会覆盖
//type: type, //默认是form的method(get or post),如果申明,则会覆盖
//dataType: null, //html(默认), xml, script, json...接受服务端返回的类型
//clearForm: true, //成功提交后,清除所有表单元素的值
//resetForm: true, //成功提交后,重置所有表单元素的值
timeout: 3000 //限制请求的时间,当请求大于3秒后,跳出请求
}
function showRequest(formData, jqForm, options){
//formData: 数组对象,提交表单时,Form插件会以Ajax方式自动提交这些数据,格式如:[{name:user,value:val },{name:pwd,value:pwd}]
//jqForm: jQuery对象,封装了表单的元素
//options: options对象
var queryString = $.param(formData); //name=1&address=2
var formElement = jqForm[0]; //将jqForm转换为DOM对象
var address = formElement.address.value; //访问jqForm的DOM元素
return true; //只要不返回false,表单都会提交,在这里可以对表单元素进行验证
};
function showResponse(responseText, statusText){
//dataType=xml
var name = $('name', responseXML).text();
var address = $('address', responseXML).text();
$("#xmlout").html(name + " " + address);
//dataType=json
$("#jsonout").html(data.name + " " + data.address);
};
$("#formID").ajaxSubmit(options);
21.利用分页插件做分页(以user/index.jsp为例)
(1)引入pagination.css
<link rel="stylesheet" href="${APP_PATH }/jquery/pagination/pagination.css">
(2)引入jquery.pagination.js,并修改把最后一行注释
<script src="${APP_PATH}/jquery/pagination/jquery.pagination.js"></script>
修改,否则会一直触发回调函数:
/**
* This jQuery plugin displays pagination links inside the selected elements.
*
* @author Gabriel Birke (birke *at* d-scribe *dot* de)
* @version 1.2
* @param {int} maxentries Number of entries to paginate
* @param {Object} opts Several options (see README for documentation)
* @return {Object} jQuery Object
*/
jQuery.fn.pagination = function(maxentries, opts){
opts = jQuery.extend({
items_per_page:10,
num_display_entries:10,
current_page:0,
num_edge_entries:0,
link_to:"#",
prev_text:"Prev",
next_text:"Next",
ellipse_text:"...",
prev_show_always:true,
next_show_always:true,
callback:function(){return false;}
},opts||{});
return this.each(function() {
/**
* 计算最大分页显示数目
*/
function numPages() {
return Math.ceil(maxentries/opts.items_per_page);
}
/**
* 极端分页的起始和结束点,这取决于current_page 和 num_display_entries.
* @返回 {数组(Array)}
*/
function getInterval() {
var ne_half = Math.ceil(opts.num_display_entries/2);
var np = numPages();
var upper_limit = np-opts.num_display_entries;
var start = current_page>ne_half?Math.max(Math.min(current_page-ne_half, upper_limit), 0):0;
var end = current_page>ne_half?Math.min(current_page+ne_half, np):Math.min(opts.num_display_entries, np);
return [start,end];
}
/**
* 分页链接事件处理函数
* @参数 {int} page_id 为新页码
*/
function pageSelected(page_id, evt){
current_page = page_id;
drawLinks();
var continuePropagation = opts.callback(page_id, panel);
if (!continuePropagation) {
if (evt.stopPropagation) {
evt.stopPropagation();
}
else {
evt.cancelBubble = true;
}
}
return continuePropagation;
}
/**
* 此函数将分页链接插入到容器元素中
*/
function drawLinks() {
panel.empty();
var interval = getInterval();
var np = numPages();
// 这个辅助函数返回一个处理函数调用有着正确page_id的pageSelected。
var getClickHandler = function(page_id) {
return function(evt){ return pageSelected(page_id,evt); }
}
//辅助函数用来产生一个单链接(如果不是当前页则产生span标签)
var appendItem = function(page_id, appendopts){
page_id = page_id<0?0:(page_id<np?page_id:np-1); // 规范page id值
appendopts = jQuery.extend({text:page_id+1, classes:""}, appendopts||{});
if(page_id == current_page){
var lnk = jQuery("<span class='current'>"+(appendopts.text)+"</span>");
}else{
var lnk = jQuery("<a>"+(appendopts.text)+"</a>")
.bind("click", getClickHandler(page_id))
.attr('href', opts.link_to.replace(/__id__/,page_id));
}
if(appendopts.classes){lnk.addClass(appendopts.classes);}
panel.append(lnk);
}
// 产生"Previous"-链接
if(opts.prev_text && (current_page > 0 || opts.prev_show_always)){
appendItem(current_page-1,{text:opts.prev_text, classes:"prev"});
}
// 产生起始点
if (interval[0] > 0 && opts.num_edge_entries > 0)
{
var end = Math.min(opts.num_edge_entries, interval[0]);
for(var i=0; i<end; i++) {
appendItem(i);
}
if(opts.num_edge_entries < interval[0] && opts.ellipse_text)
{
jQuery("<span>"+opts.ellipse_text+"</span>").appendTo(panel);
}
}
// 产生内部的些链接
for(var i=interval[0]; i<interval[1]; i++) {
appendItem(i);
}
// 产生结束点
if (interval[1] < np && opts.num_edge_entries > 0)
{
if(np-opts.num_edge_entries > interval[1]&& opts.ellipse_text)
{
jQuery("<span>"+opts.ellipse_text+"</span>").appendTo(panel);
}
var begin = Math.max(np-opts.num_edge_entries, interval[1]);
for(var i=begin; i<np; i++) {
appendItem(i);
}
}
// 产生 "Next"-链接
if(opts.next_text && (current_page < np-1 || opts.next_show_always)){
appendItem(current_page+1,{text:opts.next_text, classes:"next"});
}
}
//从选项中提取current_page
var current_page = opts.current_page;
//创建一个显示条数和每页显示条数值
maxentries = (!maxentries || maxentries < 0)?1:maxentries;
opts.items_per_page = (!opts.items_per_page || opts.items_per_page < 0)?1:opts.items_per_page;
//存储DOM元素,以方便从所有的内部结构中获取
var panel = jQuery(this);
// 获得附加功能的元素
this.selectPage = function(page_id){ pageSelected(page_id);}
this.prevPage = function(){
if (current_page > 0) {
pageSelected(current_page - 1);
return true;
}
else {
return false;
}
}
this.nextPage = function(){
if(current_page < numPages()-1) {
pageSelected(current_page+1);
return true;
}
else {
return false;
}
}
// 所有初始化完成,绘制链接
drawLinks();
// 回调函数
// opts.callback(current_page, this);
});
}
(3)插件的页数pageIndex默认从0开始,所以0代表第1页
<ul id="Pagination" class="pagination">
//插入分页组件部分
</ul>
var jsonObj = {
"pageno" : 1,
"pagesize" : 10
};
var loadingIndex = -1 ;
function queryPageUser(pageIndex){ //参数默认pageIndex
jsonObj.pageno = pageIndex + 1;
$.ajax({
type : "POST",
data : jsonObj,
url : "${APP_PATH}/user/doIndex.do",
beforeSend : function(){
loadingIndex = layer.load(2, {time: 10*1000});
return true ;
},
success : function(result){
layer.close(loadingIndex);
if(result.success){
var page = result.page ;
var data = page.data ;
var content = '';
$.each(data,function(i,n){
content+='<tr>';
content+=' <td>'+(i+1)+'</td>';
content+=' <td><input id="'+n.id+'" loginacct="'+n.loginacct+'" type="checkbox"></td>';
content+=' <td>'+n.loginacct+'</td>';
content+=' <td>'+n.username+'</td>';
content+=' <td>'+n.email+'</td>';
content+=' <td>';
content+=' <button type="button" onclick="window.location.href=\'${APP_PATH}/user/assignRole.htm?id='+n.id+'\'" class="btn btn-success btn-xs"><i class=" glyphicon glyphicon-check"></i></button>';
content+=' <button type="button" onclick="window.location.href=\'${APP_PATH}/user/toUpdate.htm?id='+n.id+'\'" class="btn btn-primary btn-xs"><i class=" glyphicon glyphicon-pencil"></i></button>';
content+=' <button type="button" onclick="deleteUser('+n.id+',\''+n.loginacct+'\')" class="btn btn-danger btn-xs"><i class=" glyphicon glyphicon-remove"></i></button>';
content+=' </td>';
content+='</tr>';
});
$("tbody").html(content);
// 创建分页
$("#Pagination").pagination(page.totalsize, { //page.totalsize查询出的总条数
num_edge_entries: 2, //边缘页数
num_display_entries: 3, //主体页数
callback: queryPageUser, //回调函数,回调自己
items_per_page: 10, //每页显示页数 //与自己设置的pagesize一致
current_page:(page.pageno-1), //当前页,0代表第1页
prev_text : "上一页", //“前一页”分页按钮上显示的文字
next_text : "下一页" //“下一页”分页按钮上显示的文字
});
}else{
layer.msg(result.message, {time:1000, icon:5, shift:6});
}
},
error : function(){
layer.msg("加载数据失败!", {time:1000, icon:5, shift:6});
}
});
}
(4)分页插件参数详解
插件简介:
- 此jQuery插件为Ajax分页插件,一次性加载,故分页切换时无刷新与延迟,如果数据量较大不建议用此方法,因为加载会比较慢。
- 原插件CSS不太合理,使用浮动,故无法方便实现左右方向的定位,且未清除浮动,在中文修改版中我对其进行了优化,使其支持text-align的定位。
使用方法:
跟一般的jQuery插件一样,此插件使用也很简单便捷。方法是pagination
,例如$("#page").pagination(100);
参数:
参数名 | 描述 | 参数值 |
---|---|---|
maxentries | 总条目数 | 必选参数,整数 |
items_per_page | 每页显示的条目数 | 可选参数,默认是10 |
num_display_entries | 连续分页主体部分显示的分页条目数 | 可选参数,默认是10 |
current_page | 当前选中的页面 | 可选参数,默认是0,表示第1页 |
num_edge_entries | 两侧显示的首尾分页的条目数 | 可选参数,默认是0 |
link_to | 分页的链接 | 字符串,可选参数,默认是"#" |
prev_text | “前一页”分页按钮上显示的文字 | 字符串参数,可选,默认是"Prev" |
next_text | “下一页”分页按钮上显示的文字 | 字符串参数,可选,默认是"Next" |
ellipse_text | 省略的页数用什么文字表示 | 可选字符串参数,默认是"..." |
prev_show_always | 是否显示“前一页”分页按钮 | 布尔型,可选参数,默认为true,即显示“前一页”按钮 |
next_show_always | 是否显示“下一页”分页按钮 | 布尔型,可选参数,默认为true,即显示“下一页”按钮 |
callback | 回调函数 | 默认无执行效果 |
举例:
$("#Pagination").pagination(56, { num_edge_entries: 2, num_display_entries: 4, callback: pageselectCallback, items_per_page:1 });
这段代码表示的含义是:总共有56(maxentries)个列表项,首尾两侧分页显示2(num_edge_entries)个,连续分页主体数目显示4(num_display_entries)个,回调函数为pageselectCallback(callback),每页显示的列表项为1(items_per_page)个。
22.Activiti5(test)
(1)首先引入Activiti5插件,并配置spring/spring-flow.xml
将activiti-designer-5.14拷贝到eclipse安装目录dropins目录下,并重启查看是否安装成功
spring-flow.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd">
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchemaUpdate" value="true" />
<property name="jobExecutorActivate" value="true" />
<property name="labelFontName" value="宋体" />
<property name="activityFontName" value="宋体" />
<property name="customFormTypes">
<list>
<bean class="org.activiti.explorer.form.UserFormType"/>
<bean class="org.activiti.explorer.form.ProcessDefinitionFormType"/>
<bean class="org.activiti.explorer.form.MonthFormType"/>
</list>
</property>
</bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean" destroy-method="destroy">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
<bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
<bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
<bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />
<bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />
</beans>
(2)创建请假流程图,可以用property操作框操作值
MyProcess1.bpmn
(3)activiti流程初始化,创建引擎,数据库产生23张表
TestActiviti.java
public class TestActiviti {
ApplicationContext ioc = new ClassPathXmlApplicationContext("spring/spring-*.xml");
ProcessEngine processEngine = (ProcessEngine) ioc.getBean("processEngine");
//1.创建流程引擎,创建23张表
@Test
public void test01(){
System.out.println("processEngine="+processEngine);
}
}
(4)部署自己创建的流程
//2.部署流程定义,存入数据库中
@Test
public void test02(){
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deploy = repositoryService.createDeployment().addClasspathResource("MyProcess1.bpmn").deploy();
System.out.println("deploy="+deploy);
}
(5)查询、部署、分配任务、变量、网关
①查询部署流程定义(内容)
//3.查询部署流程定义(内容)
@Test
public void test03(){
RepositoryService repositoryService = processEngine.getRepositoryService();
ProcessDefinitionQuery processDefinitionQuery= repositoryService.createProcessDefinitionQuery();
List<ProcessDefinition> list = processDefinitionQuery.list();
for(ProcessDefinition processionDefinition : list){
System.out.println("Id="+processionDefinition.getId());
System.out.println("Key="+processionDefinition.getKey());
System.out.println("Name="+processionDefinition.getName());
System.out.println("Version="+processionDefinition.getVersion());
System.out.println("-------------------------");
}
long count = processDefinitionQuery.count();
System.out.println("count="+count);
System.out.println("*****************");
//查询最后一次部署的流程定义
ProcessDefinition processDefinition = processDefinitionQuery.latestVersion().singleResult();
System.out.println("Id="+processDefinition.getId());
System.out.println("Key="+processDefinition.getKey());
System.out.println("Name="+processDefinition.getName());
System.out.println("Version="+processDefinition.getVersion());
System.out.println("################################");
//排序、分页查询流程定义
ProcessDefinitionQuery definitionQuery = processDefinitionQuery.orderByProcessDefinitionVersion().desc();
List<ProcessDefinition> listPage = definitionQuery.listPage(0, 2);
for(ProcessDefinition processDefinition2 : listPage){
System.out.println("Id="+processDefinition2.getId());
System.out.println("Key="+processDefinition2.getKey());
System.out.println("Name="+processDefinition2.getName());
System.out.println("Version="+processDefinition2.getVersion());
System.out.println("====================");
}
System.out.println("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
//根据流程定义的 Key 查询流程定义对象
ProcessDefinition processDefinition2 = processDefinitionQuery.processDefinitionKey("myProcess").latestVersion().singleResult();
System.out.println("Id="+processDefinition2.getId());
System.out.println("Key="+processDefinition2.getKey());
System.out.println("Name="+processDefinition2.getName());
System.out.println("Version="+processDefinition2.getVersion());
}
②启动流程实例
//4.启动流程实例
/**
* act_hi_actinst, 历史的活动的任务表.
* act_hi_procinst, 历史的流程实例表.
* act_hi_taskinst, 历史的流程任务表
* act_ru_execution, 正在运行的任务表.
* act_ru_task, 运行的任务数据表.
*/
@Test
public void test04(){
ProcessDefinition processDefinition = processEngine.getRepositoryService().createProcessDefinitionQuery().latestVersion().singleResult();
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId());
System.out.println("processInstance"+processInstance);
}
③查询流程实例的任务数据
@Test
public void test05(){
ProcessDefinition processDefinition = processEngine.getRepositoryService().createProcessDefinitionQuery().latestVersion().singleResult();
TaskService taskService = processEngine.getTaskService();
TaskQuery createTaskQuery = taskService.createTaskQuery();
List<Task> list1 = createTaskQuery.taskAssignee("zhangsan").list();
List<Task> list2 = createTaskQuery.taskAssignee("lisi").list();
//zhangsan的任务:
System.out.println("zhangsan的任务:");
for (Task task : list1) {
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
//zhangsan完成任务
taskService.complete(task.getId());
}
System.out.println("------------------------------------");
//lisi的任务:
System.out.println("lisi的任务:");
for (Task task : list2) {
System.out.println("id="+task.getId());
System.out.println("name="+task.getName());
taskService.complete(task.getId());
}
System.out.println("========================================");
list1 = createTaskQuery.taskAssignee(&