注:本文为伪原创,代码主要参考尚硅谷教程。感谢巨人的肩膀,让我可以看得更远。
本文全部代码见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.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsdhttp://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.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://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, stdoutlog4j.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, 显示1000msicon: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})的layerwindow.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 ; //将值传入jsonObjqueryPageUser(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, createtimefrom 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){ //由前端传来的数据自动封装成UserAjaxResult result = new AjaxResult();try {int count = userService.saveUser(user);//需要在impl中手动添加默认密码和createtimeresult.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){ //由前端传来的数据自动封装成UserAjaxResult 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 }<spanclass="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"><iclass="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.htmvar host = window.location.host ;// localhost:8888var index = href.indexOf(host);//7var path = href.substring(index + host.length); // /Atcrowdfunding-main/user/index.htmvar contextPath = "${APP_PATH}"; // /Atcrowdfunding-mainvar pathAddress = path.substring(contextPath.length+1);// user/index.htmvar 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> idsAjaxResult 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> idsAjaxResult 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=2autoParam:["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>();//100for (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.urlFROM t_user,t_role,t_user_role,t_role_permission,t_permissionWHERE 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 {@Overridepublic 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对象时需要执行的方法.@Overridepublic 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 {@Overridepublic 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转换为MultipartHttpServletRequestMultipartFile mfile = mreq.getFile("advpic");String name = mfile.getOriginalFilename();//java.jpgString extname = name.substring(name.lastIndexOf(".")); // .jpgString iconpath = UUID.randomUUID().toString()+extname; //232243343.jpgServletContext 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_pagevar 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){ //参数默认pageIndexjsonObj.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.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsdhttp://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张表@Testpublic 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("zhangsan").list();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());}
}
④历史数据查询
@Test
public void test06(){ProcessDefinition processDefinition = processEngine.getRepositoryService().createProcessDefinitionQuery().latestVersion().singleResult();HistoryService historyService = processEngine.getHistoryService();HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery();HistoricProcessInstance historicProcessInstance = historicProcessInstanceQuery.processInstanceId("301").finished().singleResult();System.out.println("historicProcessInstance="+historicProcessInstance);
}
⑤领取任务 taskService.claim(task.getId(), "zhangsan");
MyProcess3.hpmn
//7.领取任务
@Test
public void test07(){ProcessDefinition processDefinition = processEngine.getRepositoryService().createProcessDefinitionQuery().latestVersion().singleResult();TaskService taskService = processEngine.getTaskService();TaskQuery createTaskQuery = taskService.createTaskQuery();List<Task> list = createTaskQuery.taskCandidateGroup("tl").list();long count = createTaskQuery.taskAssignee("zhangsan").count();System.out.println("zhangsan领取前的任务数量:"+count);for (Task task : list) {System.out.println("id="+task.getId());System.out.println("name="+task.getName());taskService.claim(task.getId(), "zhangsan");}createTaskQuery = taskService.createTaskQuery();count = createTaskQuery.taskAssignee("zhangsan").count();System.out.println("zhangsan领取后的任务数量:"+count);
}
⑥流程变量,如果存在流程变量,那么,在启动流程实例时,要给流程变量赋值.否则,启动流程实例会报错.
变量用值用map传入
@Test
public void test08(){ProcessDefinition processDefinition = processEngine.getRepositoryService().createProcessDefinitionQuery().latestVersion().singleResult();RuntimeService runtimeService = processEngine.getRuntimeService();Map<String,Object> varibales = new HashMap<String,Object>();varibales.put("tl", "zhangsan");//存入变量值varibales.put("pm", "lisi");ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId(),varibales);//放入mapSystem.out.println("processInstance="+processInstance);
}
⑦网关 - 排他网关(互斥)
//9.网关 - 排他网关(互斥)
@Test
public void test09(){ProcessDefinition processDefinition = processEngine.getRepositoryService().createProcessDefinitionQuery().latestVersion().singleResult();RuntimeService runtimeService = processEngine.getRuntimeService();Map<String,Object> varibales = new HashMap<String,Object>();varibales.put("days", "5");ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId(),varibales);System.out.println("processInstance="+processInstance);
}
//9.网关 - 排他网关(互斥) -完成组长审批,观察流程走向
@Test
public void test091(){ProcessDefinition processDefinition = processEngine.getRepositoryService().createProcessDefinitionQuery().latestVersion().singleResult();TaskService taskService = processEngine.getTaskService();TaskQuery taskQuery = taskService.createTaskQuery();List<Task> list0 = taskQuery.taskAssignee("zhangsan").list();for(Task task : list0){taskService.complete(task.getId());}List<Task> list = taskQuery.taskAssignee("lisi").list();for(Task task : list){taskService.complete(task.getId());}
}
⑧网关 - 并行网关(会签)
@Test
public void test10(){ProcessDefinition processDefinition = processEngine.getRepositoryService().createProcessDefinitionQuery().latestVersion().singleResult();RuntimeService runtimeService = processEngine.getRuntimeService();ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId());System.out.println("processInstance="+processInstance);
}//10.网关 - 并行网关(会签) - 项目经理和财务经理都审批后,流程结束;如果只有一个经理审批,流程需要等待.
@Test
public void test101(){ProcessDefinition processDefinition = processEngine.getRepositoryService().createProcessDefinitionQuery().latestVersion().singleResult();TaskService taskService = processEngine.getTaskService();TaskQuery taskQuery = taskService.createTaskQuery();List<Task> list = taskQuery.taskAssignee("zhangsan").list();for (Task task : list) {taskService.complete(task.getId());}
}
⑨网关 - 包含网关(排他+并行)
⑩
@Test
public void test11(){ProcessDefinition processDefinition = processEngine.getRepositoryService().createProcessDefinitionQuery().latestVersion().singleResult();RuntimeService runtimeService = processEngine.getRuntimeService();Map<String,Object> varibales = new HashMap<String,Object>();varibales.put("days", "5");varibales.put("cost", "8000");ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId(),varibales);System.out.println("processInstance="+processInstance);
}
(6)流程监听器
NoListener.java,继承ExecutionListener
//流程监听器
public class NoListener implements ExecutionListener {@Overridepublic void notify(DelegateExecution execution) throws Exception {System.out.println("审批拒绝...");}
}
@Test
public void test12(){ProcessDefinition processDefinition = processEngine.getRepositoryService().createProcessDefinitionQuery().latestVersion().singleResult();RuntimeService runtimeService = processEngine.getRuntimeService();Map<String,Object> varibales = new HashMap<String,Object>();varibales.put("yesListener", new YesListener());varibales.put("noListener", new NoListener());ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId(),varibales);System.out.println("processInstance="+processInstance);TaskService taskService = processEngine.getTaskService();List<Task> tasks = taskService.createTaskQuery().taskAssignee("zhangsan").list();System.out.println( "zhangsan的任务数量 = " + tasks.size() );for ( Task task : tasks ) {taskService.setVariable(task.getId(), "flag", false); //设置变量值taskService.complete(task.getId());
}System.out.println( "流程结束" );
}
(7)配置本地邮箱服务器,以及安装firefox邮件客户端
①安装邮件服务器
- 将“apache-james-3.0-beta4-app.zip”解压到非中文,非空格目录下
- 将“jaxb-impl-2.1.3.jar”拷贝到“apache-james-3.0-beta4-app\conf\lib\jaxb-impl-2.1.3.jar”
- JDK1.6不需要拷贝这个jar包;JDK1.7,1.8需要拷贝这个jar包
- 如果不拷贝jaxb-impl-2.1.3.jar包会报错
②默认数据库是Derby,修改为Mysql数据库
修改 apache-james-3.0-beta4\conf\james-database-template.properties 文件
- 文件名变为 james-database.properties
- 修改内容
database.driverClassName=com.mysql.cj.jdbc.Driver
database.url=jdbc:mysql://10.50.216.235:3305/email?allowMultiQueries=true&rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=false
database.username=root
database.password=123456
vendorAdapter.database=MYSQL
- 将数据库驱动程序jar包拷贝到james/conf/lib文件夹中
③启动邮件服务器,创建用户
- 端口默认为9999
- 通过DOS窗口,进入到james服务器的bin文件目录, 然后执行DOS命令run.bat启动服务器
- 创建邮箱的域名,为atguigui.com,端口9999
james-cli.bat -h localhost -p 9999 adddomain atguigu.com
-
创建邮箱用户
james-cli.bat -h localhost -p 9999 adduser test@atguigu.com test //账号test@atguigu.com,密码test james-cli.bat -h localhost -p 9999 adduser admin@atguigu.com admin
④安装foxmail邮件客户端
- 解压
- 启动foxmail邮件客户端
- 是否导入Outlook,选择【否】
- 设置账号
- 设置密码
- 设置服务器地址及端口
- 设置接收服务器类型:POP3
POP3,全名为“Post Office Protocol - Version 3”,即“邮局协议版本3”。是TCP/IP协议族中的一员,由RFC1939 定义。本协议主要用于支持使用客户端远程管理在服务器上的电子邮件。提供了SSL加密的POP3协议被称为POP3S。 |
- 设置接收邮件服务器(R)的:localhost
- 端口:110,不使用 SSL来连接服务器
- 设置发送邮件服务器(S):localhost
- 端口:25,不使用 SSL来连接服务器
SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层对网络连接进行加密。 |
- 创建账号的信息,点击测试按钮进行测试
- 测试
- 配置后的账号
- 配置其他账号
- 发邮件
- 收邮件
(8)activiti发送邮件,部署——>创建实例
(9)Spring集成JavaMail,程序式发送邮件
①配置spring-mail.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:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"default-autowire="no" default-lazy-init="false" ><bean id="sendMail" class="org.springframework.mail.javamail.JavaMailSenderImpl"><property name="host" value="localhost"></property></bean></beans>
②发送邮件测试
package junit.test;import javax.mail.internet.MimeMessage;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;import com.atguigu.atcrowdfunding.util.DesUtil;public class SpringJavaMailTest {public static void main(String[] args) throws Exception {// 使用JAVA程序发送邮件ApplicationContext application = new ClassPathXmlApplicationContext("spring/spring-*.xml");// 邮件发送器,由Spring框架提供的JavaMailSenderImpl javaMailSender = (JavaMailSenderImpl) application.getBean("sendMail");javaMailSender.setDefaultEncoding("UTF-8");MimeMessage mail = javaMailSender.createMimeMessage();//一份情书MimeMessageHelper helper = new MimeMessageHelper(mail);helper.setSubject("告白书"); //邮件标题StringBuilder content = new StringBuilder();String param = "ILY";param = DesUtil.encrypt(param, "abcdefghijklmnopquvwxyz");//加密content.append("<a href='http://www.atcrowdfunding.com/test/act.do?p=" + param + "'>激活链接</a>");helper.setText(content.toString(), true);helper.setFrom("admin@atguigu.com");helper.setTo("test@atguigu.com");javaMailSender.send(mail);}}
(10)定时任务(Quertz石英调度)
①配置spring-timer.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.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsdhttp://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.1.xsd"><!-- 要执行定时的任务,独立的JAVA类及方法 --><bean id="workJob" class="com.atguigu.atcrowdfunding.task.FinishWorkTask"></bean><!-- 配置任务的具体类和方法 --><bean id="workTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"><!-- 要调用的bean --><property name="targetObject" ref="workJob"></property> <!-- 要调用的Method --><property name="targetMethod" value="finish"></property><!-- 是否并发,false表示 如果发生错误也不影响下一次的调用 --><property name="concurrent" value="false"></property></bean><!-- 克龙(时间单位)表达式. 配置一个触发器 --><bean id="workTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"><property name="jobDetail" ref="workTask"></property><!--石英表达式,定义了任务的启动时间--><property name="cronExpression" value="0-3 24 14 28 11 ?"></property> </bean><!-- 总调度,用于启动定时器 --><bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"><property name="triggers" ><list><ref bean="workTrigger"/></list></property></bean>
</beans>
②com.task.FinishWorkTask.java
package com.atguigu.atcrowdfunding.task;//定义了任务类:
//可以做哪些事情:
//1.备份数据库数据
//2.修改数据的状态
//3.发邮件
//...
public class FinishWorkTask {public void finish() {System.out.println( "完成功能。。。" );}
}
③克龙表达式的使用方法
https://blog.csdn.net/weixin_44595287/article/details/103291815
23.流程管理
(1)异步、插件分页、显示已部署的流程(数据在ACT_RE_PROCDEF表)
① process/index.jsp
引入分页pagination.css
<link rel="stylesheet" href="${APP_PATH }/jquery/pagination/pagination.css">
引入分页jquery.pagination.js,并修改把最后一行注释
<script src="${APP_PATH}/jquery/pagination/jquery.pagination.js"></script>
<table class="table table-bordered"><thead><tr ><th width="30">#</th><th>流程名称</th><th>流程版本</th><th>流程定义Key</th><th width="100">操作</th></tr></thead><tbody><!-- 数据--></tbody><tfoot><tr ><td colspan="6" align="center"><ul id="Pagination" class="pagination"><!-- 分页码 --></ul></td></tr></tfoot>
</table>
$(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);
});var loadingIndex = -1 ;
function queryPageUser(pageIndex){ //参数默认pageIndexjsonObj.pageno = pageIndex + 1;$.ajax({type : "POST",data : jsonObj,url : "${APP_PATH}/process/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>'+n.name+'</td>';content+=' <td>'+n.version+'</td>';content+=' <td>'+n.key+'</td>';content+=' <td>';content+=' <button type="button" onclick="window.location.href=\'${APP_PATH}/process/showimg.do?id='+n.id+'\'" class="btn btn-success btn-xs"><i class=" glyphicon glyphicon-eye-open"></i></button>';content+=' <button type="button" onclick="deleteProcess(\''+n.id+'\',\''+n.name+'\')" 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: 1, //边缘页数 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});}});
}
②ProcessController.java,出现自关联,数据流循环,无法序列化为json串返回,需要改变被序列化的数据形式,改为List<Map<String,Object>>
@ResponseBody
@RequestMapping("/doIndex")
public Object doIndex(@RequestParam(value="pageno",required=false,defaultValue="1") Integer pageno,@RequestParam(value="pagesize",required=false,defaultValue="10") Integer pagesize ){AjaxResult result = new AjaxResult();Page page = new Page(pageno,pagesize);try {List<ProcessDefinition> totallist = repositoryService.createProcessDefinitionQuery().list();List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().listPage(page.getStartIndex(), pagesize);
/* 出现自关联,无线循环,无法序列化为json串
* page.setData(list);page.setTotalsize(list.size());result.setData(page);result.setMessage("加载数据成功");result.setSuccess(true);*/List<Map<String,Object>> mylistPage = new ArrayList<Map<String,Object>>();for(ProcessDefinition professionDefinition : list){Map<String,Object> map = new HashMap<String,Object>();map.put("id",professionDefinition.getId());map.put("name", professionDefinition.getName());map.put("key", professionDefinition.getKey());map.put("version", professionDefinition.getVersion());mylistPage.add(map);}page.setTotalsize(totallist.size());System.out.println(page.getTotalsize());page.setData(mylistPage);result.setPage(page);result.setSuccess(true);result.setMessage("加载数据成功");} catch (Exception e) {result.setMessage("加载数据失败");result.setSuccess(false);e.printStackTrace();}return result;
}
(2)上传流程定义文件,并做文件格式验证
注:本次在controller层验证文件格式,是否可以在前端验证?(我还不会做)
①prcess/index.jsp
a.按钮
<button id="uploadPrcDefBtn" type="button" class="btn btn-primary" style="float:right;" ><i class="glyphicon glyphicon-upload"></i> 上传流程定义文件</button>
b.表单,style="display:none"设置表单隐藏
<form id="deployForm" action="" method="POST" enctype="multipart/form-data"><input id="processDefFile" type="file" style="display:none" name="processDefFile">
</form>
c.按钮关联表单,上传
注:异步刷新后,表单需要重置,否则二次上传失败,个人猜测是change状态没有改变
$("#uploadPrcDefBtn").click(function(){$("#deployForm")[0].reset();//二次提交前重置表单$("#processDefFile").click();
});$("#processDefFile").change(function(){var options = {url:"${APP_PATH}/process/deploy.do",beforeSubmit : function(){loadingIndex = layer.msg('数据正在部署中', {icon: 6});return true ; //必须返回true,否则,请求终止.},success : function(result){layer.close(loadingIndex);if(result.success){layer.msg(result.message, {time:1000, icon:6});queryPageUser(0);//异步刷新后,表单需要重置,否则二次上传失败,个人猜测是change状态没有改变/* window.location.href="${APP_PATH}/process/index.htm"; */}else{layer.msg(result.message, {time:1000, icon:5, shift:6});} } };$("#deployForm").ajaxSubmit(options); //异步提交return ; });
②ProcessController.java ,上传文件格式验证
@ResponseBody
@RequestMapping("/deploy")
public Object deploy(HttpServletRequest request) {AjaxResult result = new AjaxResult();System.out.println(33);try {MultipartHttpServletRequest mreq = (MultipartHttpServletRequest)request;//文件需要将request转换为MultipartHttpServletRequestMultipartFile mfile = mreq.getFile("processDefFile");String originalFilename = mfile.getOriginalFilename();System.out.println(originalFilename);String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")+1).toLowerCase();System.out.println(suffix);if(!suffix.equals("bpmn")){result.setMessage("不是bpmn文件,请重新上传");result.setSuccess(false);}else{repositoryService.createDeployment().addInputStream(mfile.getOriginalFilename(), mfile.getInputStream()).deploy();result.setMessage("部署成功");result.setSuccess(true);}} catch ( Exception e ) {e.printStackTrace();result.setMessage("部署失败");result.setSuccess(false);}return result;
}
(3)删除流程
①发出请求
content+=' <button type="button" onclick="deleteProcess(\''+n.id+'\',\''+n.name+'\')" class="btn btn-danger btn-xs"><i class=" glyphicon glyphicon-remove"></i></button>';
②删除函数
function deleteProcess(id,name){layer.confirm("确认要删除["+name+"]流程吗?", {icon: 3, title:'提示'}, function(cindex){layer.close(cindex);$.ajax({type : "POST",data : {"id" : id},url : "${APP_PATH}/process/doDelete.do",beforeSend : function() { return true ;},success : function(result){if(result.success){layer.msg("删除成功", {icon: 1});queryPageUser(0);}else{layer.msg("删除失败", {time:1000, icon:5, shift:6}); }},error : function(){layer.msg("删除失败", {time:1000, icon:5, shift:6}); }});
}, function(cindex){layer.close(cindex);
});
}
③ProcessController.java
用级联删除(true),根据id查到对应的DeploymentId,再到ACT_RE_DEPLOYMENT中删除,与DeploymentI有关联的其它表的数据就会被一起级联删除
@ResponseBody
@RequestMapping("/doDelete")
public Object doDelete(String id){ AjaxResult result = new AjaxResult();try {ProcessDefinition singleResult = repositoryService.createProcessDefinitionQuery().processDefinitionId(id).singleResult();repositoryService.deleteDeployment(singleResult.getDeploymentId(), true);//true表示级联删除.result.setSuccess(true); } catch (Exception e) {result.setSuccess(false);e.printStackTrace();result.setMessage("删除流程失败!");}return result;
}
(4)显示bpmn图片(图片信息保存在数据库中)
①process/index.jsp发出请求
content+=' <button type="button" onclick="window.location.href=\'${APP_PATH}/process/showimg.htm?id='+n.id+'\'" class="btn btn-success btn-xs"><i class=" glyphicon glyphicon-eye-open"></i></button>';
②经controller跳转到process/showimg.jsp
a.默认图片为loading-1.gif(转圈圈)。若图片加载失败,显示文字“流程定义图片”,alt指定
<div class="panel-heading"><img id="showimg" alt="流程定义图片" src="${APP_PATH }/jquery/layer/skin/default/loading-1.gif">
</div>
b.用后台传回的图片替换默认图片
$("#showimg").attr("src","${APP_PATH}/process/showimgProDef.do?id=${param.id}");//替换图片源,${param.id}取传递过来的参数
③ProcessController.java
根据ACT_RE_PROCDEF表的DEPLOYMENT_ID和DGRM_RESOURCE_NAME就可以到ACT_GE_BYTEARRAY表拿到Binary/Image的字节值,再以流的形式让response接收
@ResponseBody
@RequestMapping("/showimgProDef")
public void showimgProDef(String id, ServletResponse response){ try {ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(id).singleResult();InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getDiagramResourceName());ServletOutputStream outputStream = response.getOutputStream();IOUtils.copy(inputStream, outputStream); } catch (Exception e) {e.printStackTrace();}
}
24.会员登录
(1)login.jsp
①选择所选身份
<select id="ftype" class="form-control" name="type"><option value="member" selected>会员</option><option value="user" >管理</option>
</select>
②判断登录身份,跳转不同界面
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":"登录失败!"}layer.close(loadingIndex);//关闭loadingIndex = layer.msg('处理中', {icon: 16})的layerif(result.success){ if(ftype.val()=="user"){ //判断登录身份window.location.href="${APP_PATH}/main.htm";}else if(ftype.val()=="member"){ layer.close(loadingIndex);window.location.href="${APP_PATH}/member.htm";}else{layer.msg("登录身份有误!", {time:1000, icon:5, shift:6});} }else{layer.msg(result.message, {time:1000, icon:5, shift:6});}},error : function(){layer.msg("登录出错啦!", {time:1000, icon:5, shift:6});} });
(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);if(type.equals("member")){Member member = memberService.queryMemberLogin(paramMap);session.setAttribute(Const.LOGIN_MEMBER, member);result.setData(type);result.setSuccess(true);}else if(type.equals("user")){User user = userService.queryUserLogin(paramMap);session.setAttribute(Const.LOGIN_USER, user);//加载当前用户所拥有的许可权限List<Permission> myPermissions = userService.queryPermissionByUserid(user.getId());Permission permissionRoot = null;Set<String> myUris = new HashSet<String>();//存放登录者拥有的uriMap<Integer,Permission> map = new HashMap<Integer,Permission>();for(Permission innerpermission: myPermissions){map.put(innerpermission.getId(),innerpermission );//把查出来的数据全部放入map中使用,不myUris.add("/"+innerpermission.getUrl()); //数据库里的uri无"/"}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);session.setAttribute(Const.MY_URIS,myUris );result.setData(type);result.setSuccess(true);}else{result.setMessage("登录出错");result.setSuccess(false);}} catch (Exception e) {result.setMessage("登录失败,用户名或密码错误");e.printStackTrace();result.setSuccess(false);}return result;
}
(3)MemberServicempl.java,若数据库中查出为null,抛出登录失败异常
@Override
public Member queryMemberLogin(Map<String, Object> paramMap) {Member member = memberMapper.queryMemberLogin(paramMap);if(member==null){throw new LoginFailException("登录失败,用户名或密码错误");}return member;
}
(4)自定义登录失败异常
public class LoginFailException extends RuntimeException {private static final long serialVersionUID = 1L;public LoginFailException(String message){super(message);}}
25.保存账号密码两周(Cookie)
(1)login.jsp
选择是否记住账户密码
<div class="checkbox"><label><input id="rememberme" type="checkbox" value="1"> 记住我两周</label>
var flag = $("#rememberme")[0].checked; //选择为true,否则为false/* var ff = $("#rememberme").val(); */
rememberme : flag?"1":"0"
整体代码为
<script>
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 ;var flag = $("#rememberme")[0].checked; //选择为true,否则为false
/* var ff = $("#rememberme").val(); */alert(ff);$.ajax({type : "POST",data : {loginacct : floginacct.val(),userpswd : fuserpswd.val(),type : ftype.val(),rememberme : flag?"1":"0"},url : "${APP_PATH}/doLogin.do",beforeSend : function(){loadingIndex = layer.msg('处理中', {icon: 16});//转圈的图案//一般做表单数据校验.return true ;},success : function(result){ //{"success":true} 或 {"success":false,"message":"登录失败!"}layer.close(loadingIndex);//关闭loadingIndex = layer.msg('处理中', {icon: 16})的layerif(result.success){ if(ftype.val()=="user"){window.location.href="${APP_PATH}/main.htm";}else if(ftype.val()=="member"){ window.location.href="${APP_PATH}/member.htm";}else{layer.msg("登录身份有误!", {time:1000, icon:5, shift:6});} }else{layer.msg(result.message, {time:1000, icon:5, shift:6});}},error : function(){layer.msg("登录出错啦!", {time:1000, icon:5, shift:6});} });
}
</script>
(2)DispatcherController.java
①登录查询后,保存账号密码cookie值,设置cookie的生命周期、可访问cookie的路径
Cookie实质上是一个字符串放入在客户端中;session是服务端对象,通过sessionid(名字为JsessionId,是cookie的一种)返回给客户端(用浏览器可以查看客户端的Cookie值)
@ResponseBody
@RequestMapping("/doLogin")
public Object doLogin(String loginacct,String userpswd,String type,String rememberme,HttpSession session,HttpServletResponse response){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);if(type.equals("member")){Member member = memberService.queryMemberLogin(paramMap);session.setAttribute(Const.LOGIN_MEMBER, member);if("1".equals(rememberme)){
// String logincode = "\"loginacct="+member.getLoginacct()+"&userpswd="+member.getUserpswd()+"&logintype=member\"";//tomcat6String logincode = "loginacct="+member.getLoginacct()+"&userpswd="+member.getUserpswd()+"&logintype=member";//tomcat7Cookie c = new Cookie("logincode", logincode);c.setMaxAge(60*60*24*14);c.setPath("/");//任何路径都能访问到cookieresponse.addCookie(c);}result.setData(type);result.setSuccess(true);}else if(type.equals("user")){User user = userService.queryUserLogin(paramMap);session.setAttribute(Const.LOGIN_USER, user);if("1".equals(rememberme)){String logincode = "loginacct="+user.getLoginacct()+"&userpswd="+user.getUserpswd()+"&logintype=user";Cookie c = new Cookie("logincode", logincode);c.setMaxAge(60*60*24*14);c.setPath("/");//任何路径都能访问到cookieresponse.addCookie(c);}//加载当前用户所拥有的许可权限List<Permission> myPermissions = userService.queryPermissionByUserid(user.getId());Permission permissionRoot = null;Set<String> myUris = new HashSet<String>();//存放登录者拥有的uriMap<Integer,Permission> map = new HashMap<Integer,Permission>();for(Permission innerpermission: myPermissions){map.put(innerpermission.getId(),innerpermission );//把查出来的数据全部放入map中使用,不myUris.add("/"+innerpermission.getUrl()); //数据库里的uri无"/"}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);session.setAttribute(Const.MY_URIS,myUris );result.setData(type);result.setSuccess(true);}else{result.setMessage("登录出错");result.setSuccess(false);} } catch (Exception e) {result.setMessage("登录失败,用户名或密码错误");e.printStackTrace();result.setSuccess(false);}return result;
}
②服务端中Cookie值
(3)登录前先检查浏览器中是否存在Cookie值
若有,直接跳转到/main.jsp,否则跳到login.jsp
对于user,要重新查询出权限集合放入session,否则seeion域中不存在
@RequestMapping("/login")
public String login(HttpServletRequest request,HttpSession session){ boolean needLogin = true;String logintype = null;Cookie[] cookies = request.getCookies();if(cookies!=null){for(Cookie cookie : cookies){if("logincode".equals(cookie.getName())){String logincode = cookie.getValue();//loginacct=zhangsan&userpswd=202cb962ac59075b964b07152d234b70&logintype=memberString[] split = logincode.split("&");if(split.length==3){String loginacct = split[0].split("=")[1];String userpswd = split[1].split("=")[1];logintype = split[2].split("=")[1]; Map<String, Object> paramMap = new HashMap<String,Object>();paramMap.put("loginacct",loginacct );paramMap.put("userpswd",userpswd );if("user".equals(logintype)){User user = userService.queryUserLogin(paramMap);session.setAttribute(Const.LOGIN_USER, user);//加载当前用户所拥有的许可权限List<Permission> myPermissions = userService.queryPermissionByUserid(user.getId());Permission permissionRoot = null;Set<String> myUris = new HashSet<String>();//存放登录者拥有的uriMap<Integer,Permission> map = new HashMap<Integer,Permission>();for(Permission innerpermission: myPermissions){map.put(innerpermission.getId(),innerpermission );//把查出来的数据全部放入map中使用,不myUris.add("/"+innerpermission.getUrl()); //数据库里的uri无"/"}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);session.setAttribute(Const.MY_URIS,myUris );needLogin = true;return "redirect:/main.htm";}else if("member".equals(logintype)){Member member = memberService.queryMemberLogin(paramMap);session.setAttribute(Const.LOGIN_MEMBER, member);needLogin = false;return "redirect:/member.htm";}else{needLogin = false;}}}}}return "login";
}
26.申请实名认证流程(Activiti5)
(1)member.jsp查数据库显示实名认证状态。若未实名认证,跳转认证页面
引入
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:choose><c:when test="${sessionScope.member.authstatus eq '1'}"> <span class="label label-warning" style="cursor:pointer;">实名认证申请中</span></c:when><c:when test="${sessionScope.member.authstatus eq '2'}"> <span class="label label-success" style="cursor:pointer;">已实名认证</span></c:when><c:otherwise> <span class="label label-danger" style="cursor:pointer;" onclick="window.location.href='${APP_PATH}/member/apply.htm'">未实名认证,点击认证</span></c:otherwise>
</c:choose>
(2)在t_ticket表中保存认证的进度,点击认证时自动跳转到还未登记信息的页面
如当填到资质文件上传一页时,退出后重新进行认证,应该直接跳转到资质文件上传一页,而不是从头开始重填一次
①t_ticket表
②MemberController.java
//判断流程已经进行到的步骤
@RequestMapping("/apply")
public String apply(HttpSession session){Member loginMember = (Member)session.getAttribute(Const.LOGIN_MEMBER)Ticket ticket = ticketService.getTicketByMemberId(loginMember.getId());if(ticket==null){ticket = new Ticket();ticket.setMemberid(loginMember.getId());ticket.setPstep("apply");ticket.setStatus("0");ticketService.saveTicket(ticket);}else{if("accttype".equals(ticket.getPstep())){return "redirect:/member/basicinfo.htm";}else if("basicinfo".equals(ticket.getPstep())){return "redirect:/member/uploadCert.htm";}else if("uploadCert".equals(ticket.getPstep())){return "redirect:/member/checkemail.htm";}else if("checkemail".equals(ticket.getPstep())){return "redirect:/member/checkauthcode.htm";}}return "member/accttype";
}
(3)认证页面accttype.jsp
①给每个图片资源前加入路径的方法
图片表单
<h2>商业公司</h2>
<a href="#" class="thumbnail" accttype="0"><img alt="100%x180" src="img/services-box1.jpg" data-holder-rendered="true" style="height: 180px; width: 100%; display: block;">
</a>
</div>
<div class="col-xs-6 col-md-3">
<h2>个体工商户</h2>
<a href="#" class="thumbnail" accttype="1"><img alt="100%x180" src="img/services-box2.jpg" data-holder-rendered="true" style="height: 180px; width: 100%; display: block;">
</a>
</div>
<div class="col-xs-6 col-md-3">
<h2>个人经营</h2>
<a href="#" class="thumbnail" accttype="2"><img alt="100%x180" src="img/services-box3.jpg" data-holder-rendered="true" style="height: 180px; width: 100%; display: block;">
</a>
</div>
<div class="col-xs-6 col-md-3" >
<h2>政府及非营利组织</h2>
<a href="#" class="thumbnail" accttype="3"><img alt="100%x180" src="img/services-box4.jpg" data-holder-rendered="true" style="height: 180px; width: 100%; display: block;">
</a>
</div>
统一添加路径方法
$.each($(".thumbnail img"), function(i, n){ //给每个图片资源前加入路径 $(this).attr("src", "${APP_PATH}/" + $(this).attr("src"));
});
②选中的图片打红勾,未选中的图片取消红勾
var accttype = 0 ;$(".thumbnail").click(function(){ //选中的打红勾,其他的取消红勾$('.seltype').remove();$(this).prepend('<div class="glyphicon glyphicon-ok seltype"></div>');accttype = $(this).attr("accttype"); //取当前选中的值
});
③存储账户类型,并跳转下一个界面basicinfo.jsp
var accttype = 0 ;$(".thumbnail").click(function(){ //选中的打红勾,其他的取消红勾$('.seltype').remove();$(this).prepend('<div class="glyphicon glyphicon-ok seltype"></div>');accttype = $(this).attr("accttype"); //取当前选中的值});$.each($(".thumbnail img"), function(i, n){ //给每个图片资源前加入路径 $(this).attr("src", "${APP_PATH}/" + $(this).attr("src"));});$("#applyBtn").click(function(){//判断是否选中var len = $('.seltype').length;if(len==0){layer.msg("请选择账户类型再继续申请", {time:1000, icon:5, shift:6}); }else{//保存账户类型 $.ajax({type : "POST",data : {accttype : accttype },url : "${APP_PATH}/member/updateAcctType.do",beforeSend : function() { return true ;},success : function(result){if(result.success){window.location.href="${APP_PATH}/member/basicinfo.htm";}else{layer.msg("申请失败,请重新选择申请类型", {time:1000, icon:5, shift:6}); }},error : function(){layer.msg("出错啦,请稍后再试,工程师努力修复中", {time:1000, icon:5, shift:6}); }});}});
(4)MemberController.java
①取出session中存在的member,根据其id做主键更新数据库,同时更新session域中的值,更新流程步骤为“accttype”
@ResponseBody
@RequestMapping("/updateAcctType")
public Object updateAcctType(String accttype,HttpSession session){ AjaxResult result = new AjaxResult();try {Member loginMember = (Member)session.getAttribute(Const.LOGIN_MEMBER);loginMember.setAccttype(accttype);// 更新账户类型int count = memberService.updateAcctType(loginMember);//记录流程步骤:Ticket ticket = ticketService.getTicketByMemberId(loginMember.getId()) ;ticket.setPstep("accttype");ticketService.updatePstep(ticket);result.setSuccess(1==count);} catch (Exception e) {result.setSuccess(false);e.printStackTrace();result.setMessage("添加数据失败!");}return result; //以流的形式传入序列化JSON数据
}
(5)详细信息登记界面,返回时回显
①basicinfo.jsp
表单
<div class="form-group"><label for="realname">真实名称</label><input type="text" class="form-control" id="realname" value="${sessionScope.member.realname }" placeholder="请输入真实名称">
</div>
<div class="form-group"><label for="cardnum">身份证号码</label><input type="text" class="form-control" id="cardnum" value="${sessionScope.member.cardnum }" placeholder="请输入身份证号码">
</div>
<div class="form-group"><label for="tel">电话号码</label><input type="text" class="form-control" id="tel" value="${sessionScope.member.tel }" placeholder="请输入电话号码">
</div>
提交
$("#nextBtn").click(function(){var realname = $("#realname");var cardnum = $("#cardnum");var tel = $("#tel");$.ajax({type : "POST",data : {"realname" : realname.val(),"cardnum" : cardnum.val(),"tel" : tel.val() },url : "${APP_PATH}/member/updateBasicinfo.do",beforeSend : function() { return true ;},success : function(result){if(result.success){window.location.href="${APP_PATH}/member/uploadCert.htm";}else{layer.msg("保存基本信息更新失败", {time:1000, icon:5, shift:6}); }},error : function(){layer.msg("保存失败", {time:1000, icon:5, shift:6}); }});});
②保存数据,更新流程步骤为“basicinfo”,
MemberController.java
@ResponseBody
@RequestMapping("/updateBasicinfo")
public Object updateBasicinfo(Member member, HttpSession session){ AjaxResult result = new AjaxResult();try {// 获取登录会员的id值用于做主键更新,同时更新session域中的值Member loginMember = (Member)session.getAttribute(Const.LOGIN_MEMBER);loginMember.setRealname(member.getRealname());loginMember.setCardnum(member.getCardnum());loginMember.setTel(member.getTel());int count = memberService.updateBasicinfo(loginMember);//更新流程步骤:Ticket ticket = ticketService.getTicketByMemberId(loginMember.getId()) ;ticket.setPstep("basicinfo");ticketService.updatePstep(ticket);result.setSuccess(count==1); } catch (Exception e) {result.setSuccess(false);e.printStackTrace();result.setMessage("添加数据失败!");}return result; //以流的形式传入序列化JSON数据
}
(6)后端设置用户类型需要上传的证件
①显示数据库里的证件列表,已经被分配的证件类型打勾
/certtype/index.jsp,给每个复选框赋值
<table class="table table-bordered"><thead><tr ><th>名称</th><th >商业公司</th><th >个体工商户</th><th >个人经营</th><th >政府及非营利组织</th></tr></thead><tbody><c:forEach items="${allCert }" var="cert"><tr><td>${cert.name }</td><td><input type="checkbox" certid="${cert.id}" accttype="0"></td>//给每个复选框赋值<td><input type="checkbox" certid="${cert.id}" accttype="1"></td><td><input type="checkbox" certid="${cert.id}" accttype="2"></td><td><input type="checkbox" certid="${cert.id}" accttype="3"></td></tr> </c:forEach></tbody>
</table>
查数据库,已经被分配的打勾
<c:forEach items="${certAccttypeList }" var="cert">$(":checkbox[certid='${cert.certid}'][accttype='${cert.accttype}']")[0].checked=true ;
</c:forEach>
②数据库查值
CerttypeController.java
a.查证件列表
b.查每类用户已经被分配的证件类型
@RequestMapping("/index")
public String index(Map<String, Object> map) {//查证件列表List<Cert> allCert = certtypeService.queryAllCert();map.put("allCert", allCert);//查询资质与账户类型之间关系(每类用户已经被分配的证件类型)List<Map<String,Object>> certAccttypeList = certtypeService.queryCertAccttype();System.out.println(certAccttypeList.toString());map.put("certAccttypeList",certAccttypeList);return "certtype/index";
}
③mybatis自动封装List<Map<String,Object>>
CertMapper.java
public interface CerttypeMapper {List<Map<String, Object>> queryCertAccttype();
}
CertMapper.xml
<select id="queryCertAccttype" resultType="map">select accttype,certid from t_account_type_cert
</select>
封装效果图,[{certid=1, accttype=0}, {certid=2, accttype=0}, {certid=4, accttype=1}, {certid=3, accttype=2}]
④点击复选框增加值,取消复选框删除值(取复选框值var certid = $(this).attr("certid");)
certtype/index.jsp
$(":checkbox").click(function(){var flg = this.checked; //打勾为true,取消勾为false//通过this.certid不能获取自定义的属性值var certid = $(this).attr("certid");var accttype = $(this).attr("accttype");if ( flg ) {// 增加账户类型和资质的关系$.ajax({type : "POST",url : "${APP_PATH}/certtype/insertAcctTypeCert.do",data : {certid : certid,accttype : accttype},success : function(result) {if ( result.success ) {layer.msg("分配成功", {time:500, icon:6});} else {layer.msg("分类关系保存失败", {time:500, icon:5, shift:6});setTimeout(function(){//延时要执行的事件window.location.href = "${APP_PATH}/certtype/index.htm";},500); }}});} else {// 删除账户类型和资质的关系$.ajax({type : "POST",url : "${APP_PATH}/certtype/deleteAcctTypeCert.do",data : {certid : certid,accttype : accttype},success : function(result) {if ( result.success ) {layer.msg("取消成功", {time:500, icon:6}); } else {layer.msg("分类关系删除失败", {time:500, icon:5, shift:6});setTimeout(function(){//要执行的事件window.location.href = "${APP_PATH}/certtype/index.htm";},500); }}});}
});
CertTypeController.java
//增加
@ResponseBody
@RequestMapping("/insertAcctTypeCert")
public Object insertAcctTypeCert(AccountTypeCert accountTypeCert){ //由前端传来的数据自动封装AjaxResult result = new AjaxResult();try {if(accountTypeCert.getCertid()==null || accountTypeCert.getAccttype()==null){result.setSuccess(false);result.setMessage("添加数据失败!");}else{ int count = certtypeService.insertAcctTypeCert(accountTypeCert);result.setSuccess(count==1); }} catch (Exception e) {result.setSuccess(false);e.printStackTrace();result.setMessage("添加数据失败!");}return result; //以流的形式传入序列化JSON数据
}//删除
@ResponseBody
@RequestMapping("/deleteAcctTypeCert")
public Object deleteAcctTypeCert(AccountTypeCert accountTypeCert){ //由前端传来的数据自动封装AjaxResult result = new AjaxResult();try {int count = certtypeService.deleteAcctTypeCert(accountTypeCert);result.setSuccess(count==1); } catch (Exception e) {result.setSuccess(false);e.printStackTrace();result.setMessage("添加数据失败!");}return result; //以流的形式传入序列化JSON数据
}
⑤jsp中延时事件
延时500毫秒跳转
setTimeout(function(){//延时要执行的事件window.location.href = "${APP_PATH}/certtype/index.htm";
},500);
(7)根据member的accttype显示要上传的证件,图片预览功能
①查出certtype相应的证件,放入session域
potal-api需要依赖manager-api,用到CerttypeService.java
CerttypeController.java
@RequestMapping("/uploadCert")
public String uploadCert(HttpSession session){Member loginMember = (Member)session.getAttribute(Const.LOGIN_MEMBER);String accttype = loginMember.getAccttype();List<Cert> queryCertByAccttype = certtypeService.queryCertByAccttype(accttype);session.setAttribute("queryCertByAccttype", queryCertByAccttype);return "member/uploadCert";
}
②数据库查询语句
CerttypeMapper.xml
<select id="queryCertByAccttype" resultType="Cert" parameterType="string">SELECT * FROM t_cert WHERE id IN (SELECT certid FROM t_account_type_cert WHERE accttype=#{accttype})
</select>
③前端显示
certtype/uploadCert.jsp
<form id="certForm" role="form" style="margin-top:20px;"><c:forEach items="${queryCertByAccttype }" var="cert" varStatus="status"><div class="form-group"><label for="name">${cert.name }</label><input type="file" class="form-control" ><br><img src="${APP_PATH }/img/pic.jpg" style="display:none"></div></c:forEach>
④图片预览
$(":file").change(function(event){ 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();} });
(8)同时上传多张图片
①标记上传图片的cert.id值,设置表单的每一项属性名称,在后台Data类会自动封装
{certimgs[0].id, certimgs[0].fileImg}
{certimgs1].id, certimgs[1].fileImg}
a. member/uploadCert.jsp
<ul class="nav nav-tabs" role="tablist"><li role="presentation" ><a href="${APP_PATH}/member/basicinfo.htm"><span class="badge">1</span> 基本信息</a></li><li role="presentation" class="active"><a href="${APP_PATH}/member/uploadCert.htm"><span class="badge">2</span> 资质文件上传</a></li><li role="presentation"><a href="${APP_PATH}/member/email.htm"><span class="badge">3</span> 邮箱确认</a></li><li role="presentation"><a href="#"><span class="badge">4</span> 申请确认</a></li>
</ul><form id="uploadCertForm" role="form" method="post" action="" enctype="multipart/form-data" style="margin-top:20px;"><c:forEach items="${queryCertByAccttype }" var="cert" varStatus="status"><div class="form-group"><label for="name">${cert.name }</label><input type="hidden" name="certimgs[${status.index }].certid" value="${cert.id }"><input type="file" name="certimgs[${status.index }].fileImg" class="form-control" ><br><img src="${APP_PATH }/img/pic.jpg" style="display:none"></div></c:forEach><button type="button" onclick="window.location.href='${APP_PATH}/member/basicinfo.htm'" class="btn btn-default">上一步</button><button type="button" id = "nextBtn" class="btn btn-success">下一步</button>
b. 异步提交含文件表单
$("#nextBtn").click(function(){var loadingIndex = -1 ;var options = {url:"${APP_PATH}/member/doUploadCert.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}/member/apply.htm";}else{layer.msg("数据保存失败", {time:1000, icon:5, shift:6});} } };$("#uploadCertForm").ajaxSubmit(options); //异步提交return ;
});
②Data类
public class Data {private List<User> userList = new ArrayList<User>();private List<User> datas = new ArrayList<User>();private List<Integer> ids ;private List<MemberCert> certimgs = new ArrayList<MemberCert>();。。。。get/set。。。
}
③MemberCert类
public class MemberCert {private Integer id;private Integer memberid;private Integer certid;private String iconpath;private MultipartFile fileImg;。。。get/set。。。
}
④保存图片iconpath到数据库逻辑
a. MemberController.java,
Data中的private List<MemberCert> certimgs = new ArrayList<MemberCert>()自动封装数据
@ResponseBody
@RequestMapping("/doUploadCert")
public Object doUploadCert( HttpSession session, Data ds) { //Data自动封装前端的表单数据AjaxResult result = new AjaxResult();try {// 获取登录会员信息Member loginMember = (Member)session.getAttribute(Const.LOGIN_MEMBER); String realPath = session.getServletContext().getRealPath("/pics");//pics文件的路径//D:\a\b\c\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtpwebapps\Atcrowdfunding-main\picsList<MemberCert> certimgs = ds.getCertimgs();for(MemberCert memberCert : certimgs){MultipartFile fileImg = memberCert.getFileImg();String originalFilename = fileImg.getOriginalFilename();String extName = originalFilename.substring(originalFilename.lastIndexOf(".")); //扩展名.imgString tmpName = UUID.randomUUID().toString() + extName;String filename = realPath + "/cert" + "/" + tmpName;//图片绝对路径fileImg.transferTo(new File(filename)); //上传文件//在数据库中保存的文件名和idmemberCert.setIconpath(tmpName);memberCert.setMemberid(loginMember.getId());}//在保存数据库中保存文件名和idcerttypeService.saveMemberCert(certimgs);//记录流程步骤Ticket ticket = ticketService.getTicketByMemberId(loginMember.getId());ticket.setPstep("uploadCert");ticketService.updatePstep(ticket);result.setSuccess(true);} catch (Exception e) {e.printStackTrace();result.setSuccess(false);}return result;
}
⑤Mybatis插入List
a. CertMapper.java
public interface CerttypeMapper {void saveMemberCert(List<MemberCert> certimgs);
}
b. CertMapper.xml
<insert id="saveMemberCert" parameterType="java.util.List">insert into t_member_cert (memberid,certid,iconpath) values <foreach collection="list" item="item" index= "index" separator =",">(#{item.memberid}, #{item.certid},#{item.iconpath})</foreach></insert>
(9)启动activiti流程,发送验证码到邮件,验证验证码
①发送验证码
a. member/checkmial.jsp
<form role="form" style="margin-top:20px;"><div class="form-group"><label for="exampleInputEmail1">邮箱地址</label><input type="text" class="form-control" id="memberEmail" value="${loginMember.email }" placeholder="请输入用于接收验证码的邮箱地址"></div><button type="button" onclick="window.location.href='uploadCert.htm'" class="btn btn-default">上一步</button><button type="button" id="nextBtn" class="btn btn-success">下一步</button>
</form>
$("#nextBtn").click(function(){var memberEmail = $("#memberEmail"); $.ajax({type : "POST",data : {"email" : memberEmail.val(), },url : "${APP_PATH}/member/startProcess.do",beforeSend : function() { return true ;},success : function(result){if(result.success){window.location.href="${APP_PATH}/member/apply.htm";}else{layer.msg("发送验证码失败", {time:1000, icon:5, shift:6});}},error : function(){layer.msg("操作失败", {time:1000, icon:5, shift:6}); }});});
②后台随机生成4位验证码,启动activi流程,发送验证码
a. memberController.java
@ResponseBody
@RequestMapping("/startProcess")
public Object startProcess( HttpSession session, String email) { //Data自动封装前端的表单数据AjaxResult result = new AjaxResult();try {// 获取登录会员信息Member loginMember = (Member)session.getAttribute(Const.LOGIN_MEMBER);// 如果用户输入新的邮箱,将旧的邮箱地址替换if(!loginMember.getEmail().equals(email)){loginMember.setEmail(email);memberService.updateEmail(loginMember);}//启动实名认证流程 - 系统自动发送邮件,生成验证码.验证邮箱地址是否合法. //1.根据流程id 查询出流程ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("auth").singleResult();//随机生成4位验证码StringBuilder authcode = new StringBuilder();for (int i = 1; i <= 4; i++) {authcode.append(new Random().nextInt(10)); //0-9的随机整数}//2.准备流程变量值Map<String, Object> variables = new HashMap<String,Object>();variables.put("toEmail", email);variables.put("authcode",authcode );//分配完成“审核验证码”任务的人variables.put("loginacct", loginMember.getLoginacct());variables.put("passListener",new PassListener() );variables.put("refuseListener", new RefuseListener());//3.注入流程变量,启动流程,获得实例//发送验证码ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId(), variables);//4.在t_ticket记录流程步骤,piid,验证码,用于验证Ticket ticket = ticketService.getTicketByMemberId(loginMember.getId());ticket.setPstep("checkemail");ticket.setPiid(processInstance.getProcessInstanceId());ticket.setAuthcode(authcode.toString());ticketService.updatePiidAndPstep(ticket);result.setSuccess(true);} catch (Exception e) {e.printStackTrace();result.setSuccess(false);}return result;
}
③输入验证码,并完成审核验证码任务
a. checkauthcode.jsp
<form role="form" style="margin-top:20px;"><div class="form-group"><label for="exampleInputEmail1">验证码</label><input type="text" class="form-control" id="authcode" placeholder="请输入你邮箱中接收到的验证码"></div><button type="button" onclick="javascript:;" class="btn btn-primary">重新发送验证码</button><button type="button" id="finsh" class="btn btn-success">申请完成</button>
</form>
$("#finsh").click(function(){var authcode = $("#authcode"); $.ajax({type : "POST",data : {"authcode" : authcode.val(), },url : "${APP_PATH}/member/finishApply.do",beforeSend : function() { return true ;},success : function(result){if(result.success){window.location.href="${APP_PATH}/member.htm";}else{layer.msg("验证失败", {time:1000, icon:5, shift:6});}},error : function(){layer.msg("操作失败", {time:1000, icon:5, shift:6}); }});});
④后台验证验证码,并完成“审核验证码”任务
a. memberController.java
@ResponseBody
@RequestMapping("/finishApply")
public Object finishApply( HttpSession session, String authcode) { //Data自动封装前端的表单数据AjaxResult result = new AjaxResult();try {// 获取登录会员信息Member loginMember = (Member)session.getAttribute(Const.LOGIN_MEMBER);//查询数据库中的验证码Ticket ticket = ticketService.getTicketByMemberId(loginMember.getId());if(authcode.equals(ticket.getAuthcode())){//查询当前用户的任务(即被分配的“审核验证码”任务)TaskQuery processInstanceId = taskService.createTaskQuery().processInstanceId(ticket.getPiid());org.activiti.engine.task.Task task = processInstanceId.taskAssignee(loginMember.getLoginacct()).singleResult();//完成审核taskService.complete(task.getId());//更新用户申请状态loginMember.setAuthstatus("1");memberService.updateAuthstatus(loginMember);//记录流程步骤: ticket.setPstep("finishapply");ticketService.updatePstep(ticket);result.setSuccess(true);}else{result.setSuccess(false);result.setMessage("验证码不正确,请重新输入!");}}catch (Exception e) {e.printStackTrace();result.setSuccess(false);}return result;}
27.后台认证审核任务
(1)前端显示需要审核的数据(分页)
①authcert/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");}}});showMenu();pageQuery(0);});var loadingIndex = -1 ;function pageQuery(pageIndex){var obj = {"pageno" : pageIndex+1,"pagesize" : 10};$.ajax({type : "POST",data : obj,url : "${APP_PATH}/authcert/pageQuery.do",beforeSend : function(){loadingIndex = layer.msg('数据查询中', {icon: 16});return true ;},success : function(result){layer.close(loadingIndex);if(result.success){var page = result.page ;var data = page.data ;var content = '';$.each(result.page.data ,function(i,n){ //result.page.data可写成任何单词如datacontent+='<tr>';content+=' <td>'+(i+1)+'</td>';content+=' <td>'+n.procDefName+'</td>';content+=' <td>'+n.procDefVersion+'</td>';content+=' <td>'+n.taskName+'</td>';content+=' <td>'+n.member.username+'</td>';content+=' <td>';content+= ' <button type="button" onclick="window.location.href=\'${APP_PATH}/authcert/show.htm?taskid='+n.taskid+'&memberid='+n.member.id+'\'" class="btn btn-success btn-xs"><i class=" glyphicon glyphicon-check"></i></button>';content+=' </td>';content+='</tr>';}); $("tbody").html(content);// 创建分页$("#Pagination").pagination(page.totalsize, { //page.totalsize查询出的总条数num_edge_entries: 2, //边缘页数 num_display_entries: 4, //主体页数 callback: pageQuery, //回调函数,回调自己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});}});}</script>
(2)查询出前端需要的数据
①AuthcertController.java
@ResponseBody
@RequestMapping("/pageQuery")
public Object pageQuery(@RequestParam(value="pageno",required=false,defaultValue="1") Integer pageno,@RequestParam(value="pagesize",required=false,defaultValue="10") Integer pagesize ){AjaxResult result = new AjaxResult();Page page = new Page(pageno,pagesize);try {//查询auth流程的属于backuser组的任务TaskQuery taskQuery = taskService.createTaskQuery().processDefinitionKey("auth").taskCandidateGroup("backuser");List<Task> list = taskQuery.listPage(page.getStartIndex(), pagesize);List<Map<String,Object>> mylistPage = new ArrayList<Map<String,Object>>();for(Task task : list){Map<String,Object> map = new HashMap<String,Object>();map.put("taskid", task.getId());map.put("taskName", task.getName());//根据task查询出流程ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();map.put("procDefName",processDefinition.getName());map.put("procDefVersion", processDefinition.getVersion());//查询出申请人信息Member member = ticketService.getMemberByPiid(task.getProcessInstanceId());map.put("member", member);mylistPage.add(map);}Long count = taskQuery.count();page.setTotalsize(count.intValue());page.setData(mylistPage);result.setPage(page);result.setSuccess(true);result.setMessage("加载数据成功");} catch (Exception e) {result.setMessage("加载数据失败");result.setSuccess(false);e.printStackTrace();}return result;
}
②联合查询语句
TickerMapper.xml
<select id="getMemberByPiid" resultType="com.atguigu.atcrowdfunding.bean.Member" >select t_member.* from t_member join t_ticket on t_member.id=t_ticket.memberidwhere t_ticket.piid=#{piid}
</select>
(3)显示审核详细信息,如用户名,上传的图片(图片信息在本地,数据库中只有文件名)
①设置图片路径即可显示
a. authcetr/show.jsp
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"><ol class="breadcrumb"><li><a href="#">首页</a></li><li><a href="#">数据列表</a></li><li class="active">显示会员资质信息</li></ol><div class="panel panel-default"><div class="panel-heading">表单数据<div style="float:right;cursor:pointer;" data-toggle="modal" data-target="#myModal"><i class="glyphicon glyphicon-question-sign"></i></div></div><div class="panel-body"><div class="panel-body"><form><div class="form-group"><label for="frealname">会员真实姓名: </label>${member.realname }</div><div class="form-group"><label for="fcardnum">会员身份证号: </label>${member.cardnum }</div><div class="form-group"><label for="ftel">会员电话号: </label>${member.tel }</div><hr><c:forEach items="${certimgs }" var="map"> <div class="form-group"><label for="frealname">${map.name }</label><br><img src="${APP_PATH }/pics/cert/${map.iconpath}"></div></c:forEach><button id="passBtn" type="button" class="btn btn-success"><i class="glyphicon glyphicon-plus"></i> 通过</button><button id="refuseBtn" type="button" class="btn btn-danger"><i class="glyphicon glyphicon-refresh"></i> 拒绝</button></form></div></div></div>
</div>
$("#passBtn").click(function(){layer.confirm("确认要通过吗?", {icon: 3, title:'提示'}, function(cindex){$.ajax({type : "POST",url : "${APP_PATH}/authcert/pass.do",data : {taskid : "${param.taskid}",memberid : "${param.memberid}"},success : function(result) {window.location.href = "${APP_PATH}/authcert/index.htm";}});
}, function(cindex){layer.close(cindex);
});
});$("#refuseBtn").click(function(){layer.confirm("确认要拒绝吗?", {icon: 3, title:'提示'}, function(cindex){$.ajax({type : "POST",url : "${APP_PATH}/authcert/refuse.do",data : {taskid : "${param.taskid}",memberid : "${param.memberid}"},success : function(result) {window.location.href = "${APP_PATH}/authcert/index.htm";}});
}, function(cindex){layer.close(cindex);
});
});
②后台查询前端所需数据,查出的数据来自不同表的,用Map封装
a. AuthcertController.java
@RequestMapping("/show")
public String show(int memberid, Map<String,Object> map){Member member = memberService.getMemberById(memberid);List<Map<String,Object>> certimgs = memberService.queryCertByMemberid(memberid);map.put("member", member);map.put("certimgs", certimgs);return "authcert/show";
}
b. MemberMapper.xml, 数据来自不同表,使用map封装,键名就是属性名
<select id="queryCertByMemberid" resultType="map">SELECT t_cert.id,t_cert.name,t_member_cert.iconpath FROM t_cert JOIN t_member_cert ON t_cert.id = t_member_cert.certidWHERE t_member_cert.memberid = #{memberid}
</select>
结果:
(4)点击审核,通过或拒绝,流程监听器启动
①点击审核,通过或拒绝,给流程变量flag赋值,完成“后台审核任务”
@RequestMapping("/pass")
public Object pass( String taskid, Integer memberid ) {AjaxResult result = new AjaxResult();try {taskService.setVariable(taskid, "flag", true); //给流程变量flag赋值taskService.setVariable(taskid, "memberid", memberid);//监听器需要的值taskService.complete(taskid);result.setSuccess(true);} catch (Exception e) {result.setSuccess(false);}return result;
}@RequestMapping("/refuse")
public Object refuse( String taskid, Integer memberid ) {AjaxResult result = new AjaxResult();try {taskService.setVariable(taskid, "flag", false);taskService.setVariable(taskid, "memberid", memberid);taskService.complete(taskid);result.setSuccess(true);} catch (Exception e) {result.setSuccess(false);}return result;
}
②监听器应该不能写在main或common中,因为main/common依赖potal,如果potal再依赖main/common,会形成死循环
③获取IOC的工具类, 实现Spring接口,以接口注入的方式获取IOC容器对象.(此方法不需要application对象)
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;@Component
public class ApplicationContextUtils implements ApplicationContextAware{public static ApplicationContext applicationContext ;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {ApplicationContextUtils.applicationContext = applicationContext ;}}
④PassListener.java
public class PassListener implements ExecutionListener {@Overridepublic void notify(DelegateExecution execution) throws Exception {Integer memberid = (Integer)execution.getVariable("memberid");//ApplicationContext ioc = new ClassPathXmlApplicationContext(""); //不能自己创建ioc容器,保证容器是唯一的.//ApplicationContext ioc = WebApplicationContextUtils.getWebApplicationContext(application); //需要获取application对象,可以使用流程变量,或ThreadLocal //获取IOC容器.通过自定义的工具类,实现Spring接口,以接口注入的方式获取IOC容器对象.ApplicationContext applicationContext = ApplicationContextUtils.applicationContext;//获取TicketService, MemberService对象.TicketService ticketService = applicationContext.getBean(TicketService.class);MemberService memberService = applicationContext.getBean(MemberService.class);//更新t_member表的authstatus字段: 1 -> 2 - 已实名认证Member member = memberService.getMemberById(memberid);member.setAuthstatus("2");memberService.updateAuthstatus(member);//更新t_ticket表的status字段 0 -> 1 表示流程结束ticketService.updateStatus(member);}}
⑤RefuseListener.java, 删除member_cert里的图片信息
public class RefuseListener implements ExecutionListener {@Overridepublic void notify(DelegateExecution execution) throws Exception {Integer memberid = (Integer)execution.getVariable("memberid");//ApplicationContext ioc = new ClassPathXmlApplicationContext(""); //不能自己创建ioc容器,保证容器是唯一的.//ApplicationContext ioc = WebApplicationContextUtils.getWebApplicationContext(application); //需要获取application对象,可以使用流程变量,或ThreadLocal //获取IOC容器.通过自定义的工具类,实现Spring接口,以接口注入的方式获取IOC容器对象.ApplicationContext applicationContext = ApplicationContextUtils.applicationContext;//获取TicketService, MemberService对象.TicketService ticketService = applicationContext.getBean(TicketService.class);MemberService memberService = applicationContext.getBean(MemberService.class);//更新t_member表的authstatus字段: 1 ->0 - 未实名认证Member member = memberService.getMemberById(memberid);member.setAuthstatus("0");memberService.updateAuthstatus(member);//更新t_ticket表的status字段 0 -> 1 表示流程结束ticketService.updateStatus(member);//删除member_cert里的图片信息memberService.deleteMemberCertbyMemberId(memberid);}}
28. 分布式搭建
(1)Tomcat集群部署
(2)Nginx负载均衡,动静态资源分离搭建
(3)memcached缓存服务器搭建
见操作文档//download.csdn.net/download/weixin_44595287/12021691
目录
1.系统架构
2.pom.xml
(1)parent
(2)bean
(3)common
(4)main
(5)manager-api
(6)manager-impl
(7)potal-api
(8)potal-impl
3.main/web.xml
4.main/spring-context.xml
5.main/springmvc-context.xml
6./Atcrowdfunding-main/src/main/resources/config/jdbc.properties
7.log4j.properties
8.使用弹窗提示消息
(1)加入弹窗库
(2)代码
9.用户查询,自己手写分页
(1)时序图
(2)手写分页,同步,见
(3)手写分页,异步,见
(4)用户模糊查询
①从表单中用id 提取模糊查询文本id="queryText" , 和激发按钮函数 id="queryBtn"
②用#提取queryText, 和激活按钮
③修改UserController,注意对出现%时需要转义
④修改UserMapper.java
⑤修改UsrMapper.xml,使用条件进行模糊查询。concat用于sql拼接字符串
10.新增user功能
(1)发出请求
(2)UserController
(3)提取表单数据 id="floginacct",for使用和id相同的值,可以使点击lable标签也能选中input表单,使用体验好
(4)提取表单对象和值,传到后方封装
(5)成功后调回主页面
(6)重置功能
11.修改(加回显)、删除、批量删除
(1)先回显,再修改,
①同步使用发出请求,带上id用于查询后回显
②/user/update.jsp
③修改,异步刷新
(2)删除
①发出请求,异步
②在UserController中添加doDelete方法,并返回result
(3)批量删除(每一行封装为难点)
①取出全选框allcheckbox的对象
②取出allcheckbox的当前是否被选中的值"#allCheckbox".checked
③点批量删除操作
④checkbox中放入值供取出
⑤Data.java
⑥UserController
⑦UserService
⑧UserServiceImpl
⑨UserMapper.java (MyBatis中使用List必须要指名@Param("userList")
⑩UserMapper.xml
13.被选中的菜单默认展开,并标红
(2)引用
(3)在函数中使用showMenu()调用
14.用户管理模块,分配角色
(1)首先显示已有角色
①发出请求user/index.jsp
②UserController
③UserMapper.xml
④显示已有和未有角色user/assignrole.jsp
(2)添加和删除角色
①异步操作
②UserController
③UserMapper.xml
15.以树形z-tree展示权限图,添加,修改,删除【本项目一共3层,根节点->一级节点->叶子节点】
(1)引入z-tree插件
(2)permission/index.jsp
(3)PermissionController.java
(4)修改、删除和role操作差不多,省略
16.为每位角色分配权限
(1)显示权限树,并在已有权限复选框打勾
(2)在已有的权限复选框打勾,用checked控制,checke为true时打勾(由z-tree提供)
(3)点击分配许可,流程为,先删除所有的权限,再将勾选的权限保存
①RoleController
②RoleServiceImpl
17.分配登录人员的显示菜单
(2)DispatcherController.java
(3)UserMapper.xml,五表联查
18.登录权限拦截
(1)LoginInterceptor.java
(2)在springmvc_context中声明拦截器
19.访问权限拦截
(1)服务器加载时,查询出所有需要控制权限的路径,放入application域供使用,避免每次都要查询数据库
(2)AuthInterceptor.java
(3)在springmvc_context中声明拦截器
20.广告表单文件功能上传(单个图片格式校验,照片预览,上传)
(1)表单要设属性enctype="multipart/form-data", method="post",并在springmv-context.xml中配置
(2)表单值异步提交
①需要加入jquery-form.min.js
②add.jsp
③AdvertController.java
④AdvertService,AdvertServieImpl,AdvertMapper省略
⑤异步提交文件表单方法解析
21.利用分页插件做分页(以user/index.jsp为例)
(1)引入pagination.css
(2)引入jquery.pagination.js,并修改把最后一行注释
(3)插件的页数pageIndex默认从0开始,所以0代表第1页
(4)分页插件参数详解
22.Activiti5(test)
(1)首先引入Activiti5插件,并配置spring/spring-flow.xml
(2)创建请假流程图,可以用property操作框操作值
(3)activiti流程初始化,创建引擎,数据库产生23张表
(4)部署自己创建的流程
(5)查询、部署、分配任务、变量、网关
①查询部署流程定义(内容)
②启动流程实例
③查询流程实例的任务数据
④历史数据查询
⑤领取任务 taskService.claim(task.getId(), "zhangsan");
⑥流程变量,如果存在流程变量,那么,在启动流程实例时,要给流程变量赋值.否则,启动流程实例会报错.
变量用值用map传入
⑦网关 - 排他网关(互斥)
⑧网关 - 并行网关(会签)
⑨网关 - 包含网关(排他+并行)
(6)流程监听器
(7)配置本地邮箱服务器,以及安装firefox邮件客户端
①安装邮件服务器
②默认数据库是Derby,修改为Mysql数据库
③启动邮件服务器,创建用户
④安装foxmail邮件客户端
(8)activiti发送邮件,部署——>创建实例
(9)Spring集成JavaMail,程序式发送邮件
①配置spring-mail.xml
②发送邮件测试
(10)定时任务(Quertz石英调度)
①配置spring-timer.xml
②com.task.FinishWorkTask.java
③克龙表达式的使用方法
23.流程管理
(1)异步、插件分页、显示已部署的流程(数据在ACT_RE_PROCDEF表)
① process/index.jsp
②ProcessController.java,出现自关联,数据流循环,无法序列化为json串返回,需要改变被序列化的数据形式,改为List>
(2)上传流程定义文件,并做文件格式验证
①prcess/index.jsp
②ProcessController.java ,上传文件格式验证
(3)删除流程
①发出请求
②删除函数
③ProcessController.java
(4)显示bpmn图片(图片信息保存在数据库中)
①process/index.jsp发出请求
②经controller跳转到process/showimg.jsp
③ProcessController.java
24.会员登录
(1)login.jsp
①选择所选身份
②判断登录身份,跳转不同界面
(2)DispatcherController.java
①判断登录类别查询数据库
(3)MemberServicempl.java,若数据库中查出为null,抛出登录失败异常
(4)自定义登录失败异常
25.保存账号密码两周(Cookie)
(1)login.jsp
(2)DispatcherController.java
②服务端中Cookie值
(3)登录前先检查浏览器中是否存在Cookie值
26.申请实名认证流程(Activiti5)
(1)member.jsp查数据库显示实名认证状态。若未实名认证,跳转认证页面
(2)在t_ticket表中保存认证的进度,点击认证时自动跳转到还未登记信息的页面
①t_ticket表
②MemberController.java
(3)认证页面accttype.jsp
①给每个图片资源前加入路径的方法
②选中的图片打红勾,未选中的图片取消红勾
③存储账户类型,并跳转下一个界面basicinfo.jsp
(4)MemberController.java
①取出session中存在的member,根据其id做主键更新数据库,同时更新session域中的值,更新流程步骤为“accttype”
(5)详细信息登记界面,返回时回显
①basicinfo.jsp
②保存数据,更新流程步骤为“basicinfo”,
(6)后端设置用户类型需要上传的证件
①显示数据库里的证件列表,已经被分配的证件类型打勾
②数据库查值
③mybatis自动封装List>
④点击复选框增加值,取消复选框删除值(取复选框值var certid = $(this).attr("certid");)
⑤jsp中延时事件
(7)根据member的accttype显示要上传的证件,图片预览功能
①查出certtype相应的证件,放入session域
②数据库查询语句
③前端显示
④图片预览
(8)同时上传多张图片
①标记上传图片的cert.id值,设置表单的每一项属性名称,在后台Data类会自动封装
②Data类
③MemberCert类
④保存图片iconpath到数据库逻辑
⑤Mybatis插入List
(9)启动activiti流程,发送验证码到邮件,验证验证码
①发送验证码
②后台随机生成4位验证码,启动activi流程,发送验证码
③输入验证码,并完成审核验证码任务
④后台验证验证码,并完成“审核验证码”任务
27.后台认证审核任务
(1)前端显示需要审核的数据(分页)
①authcert/index.jsp
(2)查询出前端需要的数据
①AuthcertController.java
②联合查询语句
(3)显示审核详细信息,如用户名,上传的图片(图片信息在本地,数据库中只有文件名)
①设置图片路径即可显示
②后台查询前端所需数据,查出的数据来自不同表的,用Map封装
b. MemberMapper.xml, 数据来自不同表,使用map封装,键名就是属性名
(4)点击审核,通过或拒绝,流程监听器启动
①点击审核,通过或拒绝,给流程变量flag赋值,完成“后台审核任务”
②监听器应该不能写在main或common中,因为main/common依赖potal,如果potal再依赖main/common,会形成死循环
③获取IOC的工具类, 实现Spring接口,以接口注入的方式获取IOC容器对象.(此方法不需要application对象)
④PassListener.java
⑤RefuseListener.java, 删除member_cert里的图片信息
28. 分布式搭建
(1)Tomcat集群部署
(2)Nginx负载均衡,动静态资源分离搭建
(3)memcached缓存服务器搭建