SSM众筹网站

news/2024/5/9 19:07:43/文章来源:https://blog.csdn.net/weixin_44595287/article/details/102882561

注:本文为伪原创,代码主要参考尚硅谷教程。感谢巨人的肩膀,让我可以看得更远。

本文全部代码见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+'\'" >&nbsp;&nbsp;<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="修改权限信息">&nbsp;&nbsp;<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+'\')">&nbsp;&nbsp;<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+'\'">&nbsp;&nbsp;<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="修改权限信息">&nbsp;&nbsp;<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+'\')">&nbsp;&nbsp;<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

12.抽取menu.jsp和top.jsp

13.被选中的菜单默认展开,并标红

(1)抽取为menu.js文件

(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.分配登录人员的显示菜单

(1)menu.jsp

(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值,设置cookie的生命周期、可访问cookie的路径

 ②服务端中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缓存服务器搭建

 

 

 

 

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

29个非常优秀的纹理背景网站设计

纹理设计一直是不少同学追求的&#xff0c;今天就推荐30个纹理设计出众的网站&#xff0c;希望对你的设计有所帮助。个人比较喜欢第14个&#xff0c;有家的轻松的感觉&#xff0c;更有三维效果&#xff0c;大爱啊&#xff01; 1. Banger’s分享一个最好用的UI前端框架&#xff…

[转载]搭建个人网站 |博客

一个完全的新人如何搭建自己的个人网站 首先&#xff0c;申明下本文所指的新人是指会上网站看网页&#xff0c;会自己聊QQ&#xff0c;会在新浪、网易等平台上写博客的人。当然你要说这个只要会上网的人都会了&#xff0c;是的&#xff0c;我所指的新人就是会上网的人。所以本文…

推荐一个oracle管理和学习资料网站

http://ajava.org/book/oracle/index.html 很多的oracle管理和学习方面的电子书&#xff0c;值得一看

将网站打包成桌面程序并生成安装包(跨平台)

一、Nativefier将网站打包成桌面程序 介绍 Nativefier 是一个命令行工具&#xff0c;仅仅通过一行代码就可以轻松地为任何的网站创建桌面应用程序&#xff0c;应用程序通过 Electron打包成系统可执行文件&#xff08;.app .exe等), 对应的可执行文件分别可在 Windows、macOS …

WSTMall网站系统最新官方版

WSTMall V1.0是在thinkphp 的经典版本3.2.2基础上进行优化开发的&#xff0c; TP 3.2.2不是thinkphp的一个最新的版本&#xff0c;却是thinkphp最金典的一个版本&#xff0c;正所谓站在巨人的肩膀上&#xff0c;WSTMall V1.0继承了thinkphp大道致简的理念&#xff0c;继承了thi…

天涯孤岸软件商城-.net电子商务网站系统案例

Asp.net 电子商务商城 开发技术项目案例 ASP.NET是作为.NET框架体系结构的一部分推出的。2000年ASP.NET 1.0正式发布&#xff0c;2003年ASP.NET升级为1.1版本。ASP.NET 1.1发布之后更加激发了Web应用程序开发人员对ASP.NET的兴趣。于是在2005年11月微软公司又发布了ASP.NET 2.…

使用Java开发高性能网站(二)

数据存取 数据库服务器的优化和数据的存取&#xff0c;什么类型的数据放在什么地方更好是值得去思考的问题&#xff0c;将来的存储很可能是混用的&#xff0c;Cache&#xff0c;NOSQL&#xff0c;DFS&#xff0c;DataBase在一个系统上都会有&#xff0c;生活的餐具和平日里穿的…

html/css 个人网站实例(一)

显示效果 HTML代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>alalasheep的个人网站</titl…

兼容性网站导航主菜单--Head

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml2/DTD/xhtml1-strict.dtd"> <html xmlns"http://www.w3.org/1999/xhtml" xml:lang"en"> <head> <title>兼容性超强的…

18 个锻炼编程技能的网站

编程几乎已经成为了人类所知每个行业的必要组成部分&#xff0c;它帮助组织和维护大型系统的方式是无可比拟的&#xff0c;所以越来越多的人开始了他们的编程之旅。 要学习编程&#xff0c;你可以通过交互式平台或者书本&#xff0c;随便一种你觉得最适合和容易的学习方式。但是…

Chrome“无法添加来自此网站的应用”的解决办法

晚上在Win8系统下在给Chrome添加插件时突然提示“无法添加来自此网站的应用、扩展程序和应用脚本”&#xff0c;这果断让我很诧异~之前用的都是Win7系统的Chrome 24.0&#xff0c;添加插件的方法也很简单&#xff0c;就是直接将下载好的Chrome应用*.crx文件拖到扩展程序界面&am…

监控网站与接口宕机,并推送 App 消息提醒的程序

监控网站与接口宕机&#xff0c;并推送 App 消息提醒的程序 咕咕监控&#xff0c;专注于网站、数据接口与设备在线状态监控&#xff0c;统一管理您所有的网站、API 与设备&#xff0c;10ms 级别的监控频率&#xff0c;宕机时第一时间推送 App 消息、微信消息、短信、电话语音进…

我的SEO之路2013年3月5日最早的一个站突破

只看图不说话。 送大家一句话 坚持才是SEO的神

我的SEO之路2013年3月18日关于个人博客关键字排名30

这两天更新博客有点频繁哈。 今天又来记录一下自己15号提到的博客。 说之前先给大家看个图 这是今天刚刚查询的。 做百度SEO注重的是规律&#xff0c;更新频率&#xff0c;现在我在做个测试等测试结果出来后 会和大家一起分享一下的。 我的站是2月26日建立的&#xff0c;今…

手把手教你如何创建 ASP.NET Dynamic Data 实体网站

从四月份开始接触web开发&#xff0c;至今已经有一段时间了。之前一直忙于在做项目&#xff0c;每天加班加点&#xff0c;包括周末。现在项目总算上线了&#xff0c;有了点喘息的机会&#xff0c;回过头看&#xff0c;其实我对后端的东西懂得其实还很少&#xff0c;于是决定自己…

浏览器访问网站的流程

浏览器访问网站的过程 浏览器访问百度的过程是怎样的 &#xff1f;自我理解如下图 访问流程 A电脑首先发送DNS数据&#xff0c;进行解析www.baidu.com对应的IP地址B收到了来自A电脑的DNS请求&#xff0c;解析出www.baidu.com对应的IP地址返回给A电脑A电脑就会向这个地址进行TC…

KNN实战——约会网站配对效果判定

一 约会网站配对效果判定 上一小结学习了简单的k-近邻算法的实现方法&#xff0c;但是这并不是完整的k-近邻算法流程&#xff0c;k-近邻算法的一般流程&#xff1a; 收集数据&#xff1a;可以使用爬虫进行数据的收集&#xff0c;也可以使用第三方提供的免费或收费的数据。一般…

PHp网站建设,期末大作业-海贼王主题【包含前后台】

欢迎前往我的github上下载 连接如下 https://github.com/fighting-dog/phpstudy-haizeiwanghttps://github.com/fighting-dog/phpstudy-haizeiwang

做网站,虚拟主机与云服务器之间,我们应该如何选择?

虚拟主机已经有了一段时间的历史&#xff0c;近几年随着其技术的不断成熟&#xff0c;以及其低廉的价格&#xff0c;成为众多站长的首选对象。但近两年云计算的出现&#xff0c;衍生出云服务器这个产物。这时&#xff0c;很多站长便对虚拟主机与云服务器应该如何选择感到困扰&a…