`

第一个JBPM5.2的人工任务例子

 
阅读更多
第一个JBPM5的人工任务例子:
1.启动任务任务的服务端
2.启动人工任务
3.访问和操作人工任务

说明:源码有两个人工任务,下面的这些代码,是server工程的,client工程包含另一个人工任务。

pom.xml
<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/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com</groupId>
	<artifactId>JBPM5Server</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>JBPM5Server Maven Webapp</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<junit.version>4.8.2</junit.version>
		<spring.version>3.0.5.RELEASE</spring.version>
		<jbpm.version>5.2.0.Final</jbpm.version>
		<drools.version>5.3.1.Final</drools.version>
		<slf4j.version>1.6.1</slf4j.version>
		<mysql.version>5.1.10</mysql.version>
		<jstl.version>1.1.2</jstl.version>
		<servlet.version>2.5</servlet.version>
		<javaee-api.version>6.0</javaee-api.version>
		<jsp-api.version>2.1</jsp-api.version>
		<jboss.netty.version>3.2.0.Final</jboss.netty.version>
		<codehaus.btm.version>2.1.1</codehaus.btm.version>
		<thoughtworks.xstream.version>1.3.1</thoughtworks.xstream.version>
		<aspectj.version>1.6.2</aspectj.version>
		<cglib.version>2.2</cglib.version>
		<jsp.version>2.0</jsp.version>
		<log4j.version>1.2.16</log4j.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.6.1</version>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.16</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.6.1</version>
		</dependency>
		<dependency>
			<groupId>org.lazyluke</groupId>
			<artifactId>log4jdbc-remix</artifactId>
			<version>0.2.7</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.8.2</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>${cglib.version}</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>${servlet.version}</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jsp-api</artifactId>
			<version>${jsp.version}</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>${jstl.version}</version>
		</dependency>
		<dependency>
			<groupId>taglibs</groupId>
			<artifactId>standard</artifactId>
			<version>${jstl.version}</version>
		</dependency>


		<!-- <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> 
			<version>${log4j.version}</version> </dependency> -->


		<dependency>
			<groupId>org.jbpm</groupId>
			<artifactId>jbpm-human-task</artifactId>
			<version>${jbpm.version}</version>
		</dependency>

		<dependency>
			<groupId>org.jboss.netty</groupId>
			<artifactId>netty</artifactId>
			<version>${jboss.netty.version}</version>
		</dependency>
		<dependency>
			<groupId>org.jbpm</groupId>
			<artifactId>jbpm-flow</artifactId>
			<version>${jbpm.version}</version>
		</dependency>

		<dependency>
			<groupId>org.codehaus.btm</groupId>
			<artifactId>btm</artifactId>
			<version>${codehaus.btm.version}</version>
		</dependency>
		<dependency>
			<groupId>org.jbpm</groupId>
			<artifactId>jbpm-bpmn2</artifactId>
			<version>${jbpm.version}</version>
		</dependency>
		<dependency>
			<groupId>org.jbpm</groupId>
			<artifactId>jbpm-persistence-jpa</artifactId>
			<version>${jbpm.version}</version>
		</dependency>
		<dependency>
			<groupId>org.jbpm</groupId>
			<artifactId>jbpm-flow-builder</artifactId>
			<version>${jbpm.version}</version>
		</dependency>
		<dependency>
			<groupId>org.jbpm</groupId>
			<artifactId>jbpm-bam</artifactId>
			<version>${jbpm.version}</version>
		</dependency>
		<!-- <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> 
			<version>${slf4j.version}</version> </dependency> -->
		<!-- <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> 
			<version>${slf4j.version}</version> </dependency> -->
		<dependency>
			<groupId>com.thoughtworks.xstream</groupId>
			<artifactId>xstream</artifactId>
			<version>${thoughtworks.xstream.version}</version>
			<type>jar</type>
		</dependency>


		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>${aspectj.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-asm</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aspects</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-expression</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring.version}</version>
			<optional>true</optional>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>


		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>${mysql.version}</version>
		</dependency>
		<dependency>
			<groupId>javax.validation</groupId>
			<artifactId>validation-api</artifactId>
			<version>1.0.0.GA</version>
		</dependency>
		<dependency>
			<groupId>jaxen</groupId>
			<artifactId>jaxen</artifactId>
			<version>1.1.3</version>
			<exclusions>
				<exclusion>
					<artifactId>maven-cobertura-plugin</artifactId>
					<groupId>maven-plugins</groupId>
				</exclusion>
				<exclusion>
					<artifactId>maven-findbugs-plugin</artifactId>
					<groupId>maven-plugins</groupId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.4</version>
		</dependency>
		<dependency>
			<groupId>commons-pool</groupId>
			<artifactId>commons-pool</artifactId>
			<version>1.6</version>
		</dependency>
	</dependencies>



	<build>
		<finalName>JBPM5Server</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<version>2.5</version>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.1.1</version>
				<configuration>
					<archive>
						<manifest>
							<!--<addClasspath>true</addClasspath> -->
						</manifest>
						<manifestEntries>
							<Built-By>org-builder</Built-By>
							<Build-Jdk>${java.version}</Build-Jdk>
						</manifestEntries>
					</archive>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.mortbay.jetty</groupId>
				<artifactId>maven-jetty-plugin</artifactId>
				<version>6.1.20</version>
				<configuration>
					<contextPath>/JBPM5Server</contextPath>
					<!--<webDefaultXml>webdefault.xml</webDefaultXml> -->
					<scanIntervalSeconds>0</scanIntervalSeconds>
					<scanTargetPatterns>
						<scanTargetPattern>
							<directory>src/main/webapp/WEB-INF</directory>
							<excludes>
								<exclude>**/*.jsp</exclude>
							</excludes>
							<includes>
								<include>**/*.properties</include>
								<include>**/*.xml</include>
							</includes>
						</scanTargetPattern>
					</scanTargetPatterns>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.0.2</version>
				<configuration>
					<archive>
						<manifest>
							<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
						</manifest>
						<manifestEntries>
							<Implementation-Build>${buildNumber}</Implementation-Build>
						</manifestEntries>
					</archive>
					<dependentWarExcludes>
						**/jdbc.properties,**/web.xml,WEB-INF/classes/META-INF/**
					</dependentWarExcludes>
				</configuration>
			</plugin>

		</plugins>
	</build>

</project>


log4j.properties
# log4jdbc
log4j.logger.jdbc.sqlonly=INFO 
log4j.logger.jdbc.sqltiming=INFO
log4j.logger.jdbc.audit=OFF
log4j.logger.jdbc.resultset=OFF
log4j.logger.jdbc.connection=OFF

### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yy-MM-dd HH:mm} %5p %c{1}:%L - %m%n

log4j.appender.logfile=org.apache.log4j.DailyRollingFileAppender
#log4j.appender.logfile.File=c:/deimos.log
log4j.appender.logfile.DatePattern='.'yyyy-MM-dd
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yy-MM-dd HH:mm} %5p %c{1}:%L - %m%n

log4j.rootLogger=info, stdout, logfile

log4j.logger.com.mosso=debug
log4j.logger.org.springframework.batch=DEBUG
log4j.logger.org.springframework.transaction=INFO
log4j.logger.org.springframework=error
log4j.category.org.springframework.beans.factory=DEBUG


Server:
src/main/resources/org/jbpm/LoadUsers.mvel
users = [ 'darth'  : new User('Darth Vader'),  
          'bobba'  : new User( 'Bobba Fet'),     'jabba'      : new User('Jabba Hutt'),
          'dalai'  : new User('Dalai Lama'),     'christoper' : new User('Christoper Columbus'),
          'stuart' : new User('Stuart Little'),  'jane'       : new User('Jane Austin'),
          'peter'  : new User('Peter Parker'),   'steve'      : new User('Steve Rogers'),
          'sly'    : new User('Sly Stalone'),    'liz'        : new User('Elizabeth Windsor'),
          'bruce'  : new User('Bruce Wayne' ),   'tony'       : new User('Tony Stark'),
          'luke'   : new User('Luke Cage'),      'admin'      : new User('Administrator'),
          'krisv'  : new User('krisv'),          'john'       : new User('john'),
          'mary'   : new User('mary'),           'sales'      : new User('sales-rep')
        ];       

return users;

       

src/main/resources/org/jbpm/LoadGroups.mvel
groups = [ 'knightsTempler' : new Group( "Knights Templer" ),
           'crusaders' : new Group( "Crusaders" ),
           'HR' : new Group( "HR" ),
           'PM' : new Group( "PM" ),
           'sales' : new Group( "sales" )
         ];

return groups;



src/main/resources/org/jbpm/jbpm.usergroup.callback.properties
jbpm.usergroup.callback=org.jbpm.task.service.DefaultUserGroupCallbackImpl


src/main/resources/META-INF/persistence.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence version="1.0"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
                                 http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd
                                 http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
	xmlns:orm="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/persistence">
	<persistence-unit name="org.jbpm.task">
		<provider>org.hibernate.ejb.HibernatePersistence</provider>
		<mapping-file>META-INF/Taskorm.xml</mapping-file>
		<class>org.jbpm.task.Attachment</class>
		<class>org.jbpm.task.Content</class>
		<class>org.jbpm.task.BooleanExpression</class>
		<class>org.jbpm.task.Comment</class>
		<class>org.jbpm.task.Deadline</class>
		<class>org.jbpm.task.Comment</class>
		<class>org.jbpm.task.Deadline</class>
		<class>org.jbpm.task.Delegation</class>
		<class>org.jbpm.task.Escalation</class>
		<class>org.jbpm.task.Group</class>
		<class>org.jbpm.task.I18NText</class>
		<class>org.jbpm.task.Notification</class>
		<class>org.jbpm.task.EmailNotification</class>
		<class>org.jbpm.task.EmailNotificationHeader</class>
		<class>org.jbpm.task.PeopleAssignments</class>
		<class>org.jbpm.task.Reassignment</class>
		<class>org.jbpm.task.Status</class>
		<class>org.jbpm.task.Task</class>
		<class>org.jbpm.task.TaskData</class>
		<class>org.jbpm.task.SubTasksStrategy</class>
		<class>org.jbpm.task.OnParentAbortAllSubTasksEndStrategy</class>
		<class>org.jbpm.task.OnAllSubTasksEndParentEndStrategy</class>
		<class>org.jbpm.task.User</class>
		<!-- <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> 
			<property name="hibernate.connection.driver_class" value="org.h2.Driver"/> 
			<property name="hibernate.connection.url" value="jdbc:h2:mem:mydb" /> <property 
			name="hibernate.connection.username" value="sa"/> <property name="hibernate.connection.password" 
			value="sasa"/> <property name="hibernate.connection.autocommit" value="false" 
			/> <property name="hibernate.max_fetch_depth" value="3"/> <property name="hibernate.hbm2ddl.auto" 
			value="create" /> <property name="hibernate.show_sql" value="false" /> </properties> -->
		<properties>
			<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
			<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
			<property name="hibernate.connection.url"
				value="jdbc:mysql://localhost:3306/jbpmtest?createDatabaseIfNotExist=true" />
			<property name="hibernate.connection.username" value="root" />
			<property name="hibernate.connection.password" value="root" />
			<property name="hibernate.connection.autocommit" value="false" />
			<property name="hibernate.max_fetch_depth" value="3" />
			<property name="hibernate.hbm2ddl.auto" value="update" />
			<property name="hibernate.show_sql" value="false" />
		</properties>
	</persistence-unit>
</persistence>


DemoTaskService.java
package org.jbpm;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import org.jbpm.task.AccessType;
import org.jbpm.task.AllowedToDelegate;
import org.jbpm.task.Attachment;
import org.jbpm.task.BooleanExpression;
import org.jbpm.task.Comment;
import org.jbpm.task.Deadline;
import org.jbpm.task.Deadlines;
import org.jbpm.task.Delegation;
import org.jbpm.task.Escalation;
import org.jbpm.task.Group;
import org.jbpm.task.I18NText;
import org.jbpm.task.Notification;
import org.jbpm.task.OrganizationalEntity;
import org.jbpm.task.PeopleAssignments;
import org.jbpm.task.Reassignment;
import org.jbpm.task.Status;
import org.jbpm.task.Task;
import org.jbpm.task.TaskData;
import org.jbpm.task.User;
import org.jbpm.task.query.TaskSummary;
import org.jbpm.task.service.TaskService;
import org.jbpm.task.service.TaskServiceSession;
import org.jbpm.task.service.UserGroupCallbackManager;
import org.jbpm.task.service.mina.MinaTaskServer;
import org.drools.SystemEventListenerFactory;
import org.mvel2.MVEL;
import org.mvel2.ParserContext;
import org.mvel2.compiler.ExpressionCompiler;

/**
 * 
 * @author pandy
 * 第一次运行不能注释掉Line63,72,它会插入数据库, 第二次运行,必须注释掉Line63,72, 
 *
 */
public class DemoTaskService {
	@SuppressWarnings("rawtypes")
	public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("org.jbpm.task");
        TaskService taskService = new TaskService(emf, SystemEventListenerFactory.getSystemEventListener());
        TaskServiceSession taskSession = taskService.createSession();
        // Add users
        Map vars = new HashMap();
        InputStream usersin = DemoTaskService.class.getResourceAsStream( "LoadUsers.mvel" );
        if(usersin != null) {
        	Reader reader = new InputStreamReader( usersin );   
        	@SuppressWarnings("unchecked")
        	Map<String, User> users = ( Map<String, User> ) eval( reader, vars );   
        	for ( User user : users.values() ) {
        		taskSession.addUser( user );//第二次运行,必须注释,否则提示重复User异常
        	}           
        }
        InputStream groupsin = DemoTaskService.class.getResourceAsStream( "LoadGroups.mvel" );
        if(groupsin != null) {
        	Reader reader = new InputStreamReader( groupsin );   
        	@SuppressWarnings("unchecked")
        	Map<String, Group> groups = ( Map<String, Group> ) eval( reader, vars );     
        	for ( Group group : groups.values() ) {
        		taskSession.addGroup( group );//第二次运行,必须注释,否则提示重复Group异常
        	}
        }
        // try to get the usergroup callback properties
        InputStream usergroupsin = DemoTaskService.class.getResourceAsStream(  "jbpm.usergroup.callback.properties" );
        if(usergroupsin != null) {
        	Reader reader = new InputStreamReader( usergroupsin );
        	Properties callbackproperties = new Properties();
        	try {
        		callbackproperties.load(reader);
        		UserGroupCallbackManager.getInstance().setCallbackFromProperties(callbackproperties);
        		System.out.println("@Task service registered usergroup callback ...");
        	} catch (Exception e) {
        		System.out.println("@Task service unable to register usergroup callback ...");
        	}
        }
        // start server
        MinaTaskServer server = new MinaTaskServer(taskService);
        Thread thread = new Thread(server);
        thread.start();
        taskSession.dispose();
        System.out.println("@正确启动: Task Server");
        System.out.println("@服务已经运行中: Task Service.");
    }

    public static Object eval(Reader reader, Map vars) {
        try {
            return eval( readerToString( reader ), vars );
        } catch ( IOException e ) {
            throw new RuntimeException( "Exception Thrown", e );
        }
    }
    
    public static String readerToString(Reader reader) throws IOException {
        int charValue = 0;
        StringBuffer sb = new StringBuffer( 1024 );
        while ( (charValue = reader.read()) != -1 ) {
            //result = result + (char) charValue;
            sb.append( (char) charValue );
        }
        return sb.toString();
    }

    @SuppressWarnings("deprecation")
	public static Object eval(String str, Map vars) {
        ExpressionCompiler compiler = new ExpressionCompiler( str.trim() );

        ParserContext context = new ParserContext();
        context.addPackageImport( "org.jbpm.task" );
        context.addPackageImport( "java.util" );
        
        context.addImport( "AccessType", AccessType.class );
        context.addImport( "AllowedToDelegate", AllowedToDelegate.class );
        context.addImport( "Attachment", Attachment.class );
        context.addImport( "BooleanExpression", BooleanExpression.class );
        context.addImport( "Comment", Comment.class );
        context.addImport( "Deadline", Deadline.class );
        context.addImport( "Deadlines", Deadlines.class );
        context.addImport( "Delegation", Delegation.class );
        context.addImport( "Escalation", Escalation.class );
        context.addImport( "Group", Group.class );
        context.addImport( "I18NText", I18NText.class );
        context.addImport( "Notification", Notification.class );
        context.addImport( "OrganizationalEntity", OrganizationalEntity.class );
        context.addImport( "PeopleAssignments", PeopleAssignments.class );
        context.addImport( "Reassignment", Reassignment.class );
        context.addImport( "Status", Status.class );
        context.addImport( "Task", Task.class );
        context.addImport( "TaskData", TaskData.class );
        context.addImport( "TaskSummary", TaskSummary.class );
        context.addImport( "User", User.class );

        return MVEL.executeExpression( compiler.compile( context ), vars );
    }

}




Client:
src/main/resources/Evaluation.bpmn
<?xml version="1.0" encoding="UTF-8"?> 
<definitions id="Definition"
             targetNamespace="http://www.jboss.org/drools"
             typeLanguage="http://www.java.com/javaTypes"
             expressionLanguage="http://www.mvel.org/2.0"
             xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"
             xmlns:g="http://www.jboss.org/drools/flow/gpd"
             xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
             xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
             xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
             xmlns:tns="http://www.jboss.org/drools">

  <itemDefinition id="_employeeItem" />
  <itemDefinition id="_reasonItem" />
  <itemDefinition id="_performanceItem" />
  <itemDefinition id="_contentItem" />

  <itemDefinition id="_2-employeeItem" />
  <itemDefinition id="_2-reasonItem" />
  <itemDefinition id="_2-performanceItem" />
  <itemDefinition id="_2-contentItem" />

  <itemDefinition id="_3-employeeItem" />
  <itemDefinition id="_3-reasonItem" />
  <itemDefinition id="_3-performanceItem" />
  <itemDefinition id="_3-contentItem" />

  <itemDefinition id="_4-employeeItem" />
  <itemDefinition id="_4-reasonItem" />
  <itemDefinition id="_4-performanceItem" />
  <itemDefinition id="_4-contentItem" />

  <process processType="Private" isExecutable="true" id="com.sample.evaluation" name="Evaluation" tns:packageName="defaultPackage" >

    <!-- process variables -->
    <property id="employee" itemSubjectRef="_employeeItem"/>
    <property id="reason" itemSubjectRef="_reasonItem"/>
    <property id="performance" itemSubjectRef="_performanceItem"/>
    <property id="content" itemSubjectRef="_contentItem"/>

    <!-- nodes -->
    <startEvent id="_1" name="Start" />
    <userTask id="_2" name="Self Evaluation" >
      <extensionElements>
        <tns:onEntry-script scriptFormat="http://www.java.com/java">
          <script>java.util.Map contentParam = new java.util.HashMap();
contentParam.put("reason", reason);
kcontext.setVariable("content", contentParam);</script>
        </tns:onEntry-script>
      </extensionElements>
      <ioSpecification>
        <dataInput id="_2_ContentInput" name="Content" />
        <dataInput id="_2_CommentInput" name="Comment" />
        <dataInput id="_2_SkippableInput" name="Skippable" />
        <dataInput id="_2_TaskNameInput" name="TaskName" />
        <dataOutput id="_2_performanceOutput" name="performance" />
        <inputSet>
          <dataInputRefs>_2_ContentInput</dataInputRefs>
          <dataInputRefs>_2_CommentInput</dataInputRefs>
          <dataInputRefs>_2_SkippableInput</dataInputRefs>
          <dataInputRefs>_2_TaskNameInput</dataInputRefs>
        </inputSet>
        <outputSet>
          <dataOutputRefs>_2_performanceOutput</dataOutputRefs>
        </outputSet>
      </ioSpecification>
      <dataInputAssociation>
        <sourceRef>content</sourceRef>
        <targetRef>_2_ContentInput</targetRef>
      </dataInputAssociation>
      <dataInputAssociation>
        <targetRef>_2_CommentInput</targetRef>
        <assignment>
          <from xsi:type="tFormalExpression">Please perform a self-evalutation.</from>
          <to xsi:type="tFormalExpression">_2_CommentInput</to>
        </assignment>
      </dataInputAssociation>
      <dataInputAssociation>
        <targetRef>_2_SkippableInput</targetRef>
        <assignment>
          <from xsi:type="tFormalExpression">false</from>
          <to xsi:type="tFormalExpression">_2_SkippableInput</to>
        </assignment>
      </dataInputAssociation>
      <dataInputAssociation>
        <targetRef>_2_TaskNameInput</targetRef>
        <assignment>
          <from xsi:type="tFormalExpression">Performance Evaluation Self</from>
          <to xsi:type="tFormalExpression">_2_TaskNameInput</to>
        </assignment>
      </dataInputAssociation>
      <dataOutputAssociation>
        <sourceRef>_2_performanceOutput</sourceRef>
        <targetRef>performance</targetRef>
      </dataOutputAssociation>
      <potentialOwner>
        <resourceAssignmentExpression>
          <formalExpression>#{employee}</formalExpression>
        </resourceAssignmentExpression>
      </potentialOwner>
    </userTask>
    <userTask id="_3" name="PM Evaluation" >
      <extensionElements>
        <tns:onEntry-script>
          <script>java.util.Map contentParam = new java.util.HashMap();
contentParam.put("reason", reason);
contentParam.put("performance", performance);
kcontext.setVariable("content", contentParam);</script>
        </tns:onEntry-script>
      </extensionElements>
      <ioSpecification>
        <dataInput id="_3_ContentInput" name="Content" />
        <dataInput id="_3_CommentInput" name="Comment" />
        <dataInput id="_3_SkippableInput" name="Skippable" />
        <dataInput id="_3_TaskNameInput" name="TaskName" />
        <inputSet>
          <dataInputRefs>_3_ContentInput</dataInputRefs>
          <dataInputRefs>_3_CommentInput</dataInputRefs>
          <dataInputRefs>_3_SkippableInput</dataInputRefs>
          <dataInputRefs>_3_TaskNameInput</dataInputRefs>
        </inputSet>
        <outputSet>
        </outputSet>
      </ioSpecification>
      <dataInputAssociation>
        <sourceRef>content</sourceRef>
        <targetRef>_3_ContentInput</targetRef>
      </dataInputAssociation>
      <dataInputAssociation>
        <targetRef>_3_CommentInput</targetRef>
        <assignment>
          <from xsi:type="tFormalExpression">You need to evaluate #{employee}.</from>
          <to xsi:type="tFormalExpression">_3_CommentInput</to>
        </assignment>
      </dataInputAssociation>
      <dataInputAssociation>
        <targetRef>_3_SkippableInput</targetRef>
        <assignment>
          <from xsi:type="tFormalExpression">false</from>
          <to xsi:type="tFormalExpression">_3_SkippableInput</to>
        </assignment>
      </dataInputAssociation>
      <dataInputAssociation>
        <targetRef>_3_TaskNameInput</targetRef>
        <assignment>
          <from xsi:type="tFormalExpression">Performance Evaluation PM</from>
          <to xsi:type="tFormalExpression">_3_TaskNameInput</to>
        </assignment>
      </dataInputAssociation>
      <potentialOwner>
        <resourceAssignmentExpression>
          <formalExpression>john</formalExpression>
        </resourceAssignmentExpression>
      </potentialOwner>
    </userTask>
    <userTask id="_4" name="HR Evaluation" >
      <extensionElements>
        <tns:onEntry-script scriptFormat="http://www.java.com/java">
          <script>java.util.Map contentParam = new java.util.HashMap();
contentParam.put("reason", reason);
contentParam.put("performance", performance);
kcontext.setVariable("content", contentParam);</script>
        </tns:onEntry-script>
      </extensionElements>
      <ioSpecification>
        <dataInput id="_4_ContentInput" name="Content" />
        <dataInput id="_4_CommentInput" name="Comment" />
        <dataInput id="_4_SkippableInput" name="Skippable" />
        <dataInput id="_4_TaskNameInput" name="TaskName" />
        <inputSet>
          <dataInputRefs>_4_ContentInput</dataInputRefs>
          <dataInputRefs>_4_CommentInput</dataInputRefs>
          <dataInputRefs>_4_SkippableInput</dataInputRefs>
          <dataInputRefs>_4_TaskNameInput</dataInputRefs>
        </inputSet>
        <outputSet>
        </outputSet>
      </ioSpecification>
      <dataInputAssociation>
        <sourceRef>content</sourceRef>
        <targetRef>_4_ContentInput</targetRef>
      </dataInputAssociation>
      <dataInputAssociation>
        <targetRef>_4_CommentInput</targetRef>
        <assignment>
          <from xsi:type="tFormalExpression">You need to evaluate #{employee}.</from>
          <to xsi:type="tFormalExpression">_4_CommentInput</to>
        </assignment>
      </dataInputAssociation>
      <dataInputAssociation>
        <targetRef>_4_SkippableInput</targetRef>
        <assignment>
          <from xsi:type="tFormalExpression">false</from>
          <to xsi:type="tFormalExpression">_4_SkippableInput</to>
        </assignment>
      </dataInputAssociation>
      <dataInputAssociation>
        <targetRef>_4_TaskNameInput</targetRef>
        <assignment>
          <from xsi:type="tFormalExpression">Performance Evaluation HR</from>
          <to xsi:type="tFormalExpression">_4_TaskNameInput</to>
        </assignment>
      </dataInputAssociation>
      <potentialOwner>
        <resourceAssignmentExpression>
          <formalExpression>mary</formalExpression>
        </resourceAssignmentExpression>
      </potentialOwner>
    </userTask>
    <parallelGateway id="_5" name="Gateway" gatewayDirection="Diverging" />
    <parallelGateway id="_6" name="Gateway" gatewayDirection="Converging" />
    <endEvent id="_7" name="End" >
        <terminateEventDefinition/>
    </endEvent>

    <!-- connections -->
    <sequenceFlow id="_1-_2" sourceRef="_1" targetRef="_2" />
    <sequenceFlow id="_5-_3" sourceRef="_5" targetRef="_3" />
    <sequenceFlow id="_5-_4" sourceRef="_5" targetRef="_4" />
    <sequenceFlow id="_2-_5" sourceRef="_2" targetRef="_5" />
    <sequenceFlow id="_4-_6" sourceRef="_4" targetRef="_6" />
    <sequenceFlow id="_3-_6" sourceRef="_3" targetRef="_6" />
    <sequenceFlow id="_6-_7" sourceRef="_6" targetRef="_7" />

  </process>

  <bpmndi:BPMNDiagram>
    <bpmndi:BPMNPlane bpmnElement="com.sample.evaluation" >
      <bpmndi:BPMNShape bpmnElement="_1" >
        <dc:Bounds x="16" y="56" width="48" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_2" >
        <dc:Bounds x="96" y="56" width="135" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_3" >
        <dc:Bounds x="344" y="96" width="136" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_4" >
        <dc:Bounds x="344" y="16" width="136" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_5" >
        <dc:Bounds x="263" y="56" width="48" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_6" >
        <dc:Bounds x="512" y="56" width="48" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_7" >
        <dc:Bounds x="593" y="56" width="48" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="_1-_2" >
        <di:waypoint x="40" y="80" />
        <di:waypoint x="163" y="80" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_5-_3" >
        <di:waypoint x="287" y="80" />
        <di:waypoint x="287" y="120" />
        <di:waypoint x="412" y="120" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_5-_4" >
        <di:waypoint x="287" y="80" />
        <di:waypoint x="287" y="40" />
        <di:waypoint x="412" y="40" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_2-_5" >
        <di:waypoint x="163" y="80" />
        <di:waypoint x="287" y="80" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_4-_6" >
        <di:waypoint x="412" y="40" />
        <di:waypoint x="536" y="40" />
        <di:waypoint x="536" y="80" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_3-_6" >
        <di:waypoint x="412" y="120" />
        <di:waypoint x="537" y="120" />
        <di:waypoint x="536" y="80" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_6-_7" >
        <di:waypoint x="536" y="80" />
        <di:waypoint x="617" y="80" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>

</definitions>


启动任务:StartProcess.java
package com.sample;

import java.util.HashMap;
import java.util.Map;

import org.drools.KnowledgeBase;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.io.ResourceFactory;
import org.drools.logger.KnowledgeRuntimeLogger;
import org.drools.logger.KnowledgeRuntimeLoggerFactory;
import org.drools.runtime.StatefulKnowledgeSession;
import org.jbpm.process.workitem.wsht.WSHumanTaskHandler;

/**
 * This is a sample file to launch a process.
 */
public class StartProcess {

	public static final void main(String[] args) {
		try {
			// load up the knowledge base
			KnowledgeBase kbase = readKnowledgeBase();
			StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
			KnowledgeRuntimeLogger logger = KnowledgeRuntimeLoggerFactory.newThreadedFileLogger(ksession, "test", 1000);
			ksession.getWorkItemManager().registerWorkItemHandler("Human Task", new WSHumanTaskHandler());
			// start a new process instance
			Map<String, Object> params = new HashMap<String, Object>();
			params.put("employee", "tony");
			params.put("reason", "全年业绩评价");
			ksession.startProcess("com.sample.evaluation", params);
			System.out.println("@流程已经启动。。。。。。");
			logger.close();
		} catch (Throwable t) {
			t.printStackTrace();
		}
	}

	private static KnowledgeBase readKnowledgeBase() throws Exception {
		KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
		kbuilder.add(ResourceFactory.newClassPathResource("Evaluation.bpmn"), ResourceType.BPMN2);
		return kbuilder.newKnowledgeBase();
	}

}



执行任务:TestProcess.java
package com.sample;

import java.io.IOException;
import java.util.List;

import org.drools.SystemEventListenerFactory;
import org.jbpm.task.query.TaskSummary;
import org.jbpm.task.service.TaskClient;
import org.jbpm.task.service.mina.MinaTaskClientConnector;
import org.jbpm.task.service.mina.MinaTaskClientHandler;
import org.jbpm.task.service.responsehandlers.BlockingTaskOperationResponseHandler;
import org.jbpm.task.service.responsehandlers.BlockingTaskSummaryResponseHandler;

public class TestProcess {
	public static void main(String[] args) throws IOException,
			InterruptedException {
		TaskClient tc = new TaskClient(new MinaTaskClientConnector("client 1",
				new MinaTaskClientHandler(
						SystemEventListenerFactory.getSystemEventListener())));
		tc.connect("127.0.0.1", 9123);
		System.out.println("@客户端理解服务器[127.0.0.1:9123]");
		BlockingTaskSummaryResponseHandler taskHandler = new BlockingTaskSummaryResponseHandler();
		BlockingTaskOperationResponseHandler responseHandler = new BlockingTaskOperationResponseHandler();
		long taskId = 0;
		
		
		tc.getTasksAssignedAsPotentialOwner("tony", "en-UK", taskHandler);
		List<TaskSummary> tasks = taskHandler.getResults();
		System.out.println("@tony获得任务列表, tony属于第?级节点");
		if (tasks != null && tasks.size() != 0) {
			System.out.println("-------@>tony 执行");
			taskId = tasks.get(0).getId();
			System.out.println("tony 执行 Task " + tasks.get(0).getName());
			System.out.println("@休息 2 秒钟");
			Thread.currentThread();
			Thread.sleep(2000);
			tc.start(taskId, "tony", responseHandler);
			responseHandler = new BlockingTaskOperationResponseHandler();
			tc.complete(taskId, "tony", null, responseHandler);
			System.out.println("@tony处理完成任务");
		}else{
			System.out.println("------->tony 不执行");
		}
		
		System.out.println("休息 1 秒钟");
		responseHandler = new BlockingTaskOperationResponseHandler();
		taskHandler = new BlockingTaskSummaryResponseHandler();
		Thread.currentThread();
		Thread.sleep(1000);

		tc.getTasksAssignedAsPotentialOwner("john", "en-UK", taskHandler);
		tasks = taskHandler.getResults();
		System.out.println("@john获得任务列表, john属于第二级节点,PM");
		if (tasks != null && tasks.size() != 0) {
			System.out.println("-------@>john 执行");
			taskId = tasks.get(0).getId();
			System.out.println("john 执行 Task " + tasks.get(0).getName());
			System.out.println("@休息 2 秒钟");
			Thread.currentThread();
			Thread.sleep(2000);
			tc.start(taskId, "john", responseHandler);
			responseHandler = new BlockingTaskOperationResponseHandler();
			tc.complete(taskId, "john", null, responseHandler);
			System.out.println("@john处理完成任务");
		}else{
			System.out.println("------->john 不执行");
		}
		

		responseHandler = new BlockingTaskOperationResponseHandler();
		taskHandler = new BlockingTaskSummaryResponseHandler();
		tc.getTasksAssignedAsPotentialOwner("mary", "en-UK", taskHandler);
		System.out.println("@mary获得任务列表, mary属于第二级节点,HR");
		tasks = taskHandler.getResults();
		if (tasks != null && tasks.size() != 0) {
			System.out.println("-------@>mary 执行");
			taskId = tasks.get(0).getId();
			System.out.println("mary 执行 Task " + tasks.get(0).getName());
			System.out.println("@休息 2 秒钟");
			Thread.currentThread();
			Thread.sleep(2000);
			tc.start(taskId, "mary", responseHandler);
			responseHandler = new BlockingTaskOperationResponseHandler();
			tc.complete(taskId, "mary", null, responseHandler);
			System.out.println("@mary处理完成......");
		}else{
			System.out.println("------->mary 不执行");
		}

		
		System.out.println("......Task被处理完成......");

	}
}

      
输出:
@客户端理解服务器[127.0.0.1:9123]
@tony获得任务列表, tony属于第?级节点
-------@>tony 执行
tony 执行 Task Performance Evaluation Self
@休息 2 秒钟
@tony处理完成任务
休息 1 秒钟
@john获得任务列表, john属于第二级节点,PM
-------@>john 执行
john 执行 Task Performance Evaluation PM
@休息 2 秒钟
@john处理完成任务
@mary获得任务列表, mary属于第二级节点,HR
-------@>mary 执行
mary 执行 Task Performance Evaluation HR
@休息 2 秒钟
@mary处理完成......
......Task被处理完成......
分享到:
评论
1 楼 z070204z 2013-07-30  
请问为什么我taskHandler.getResults()得到的是一个空数组??
我的启动顺序是DemoTaskService、StartProcess、TestProcess这样对吗??

相关推荐

    Spring 3.1.x + Hibernate 4.2.x+JBPM 5.2 + Ecache例子源码

    标题中的"Spring 3.1.x + Hibernate 4.2.x + JBPM 5.2 + Ecache例子源码"代表了一个集成开发环境,其中包含了四个关键的技术组件: 1. **Spring 3.1.x**:这是一个开源的应用框架,主要用于简化Java企业级应用的...

    Spring 3.1.x + Hibernate 4.2.x+JBPM 5.2 + Ecache例子

    标题 "Spring 3.1.x + Hibernate 4.2.x + JBPM 5.2 + Ecache 例子" 涉及的是一个集成多种技术的Java应用开发示例。这个项目可能是一个完整的业务流程管理系统,它整合了Spring、Hibernate、JBPM和Ecache等关键组件。...

    jBPM5.2API文档

    jbPM5.2是该系统的某一版本,提供了一整套API,使得开发者能够灵活地集成业务流程到Java应用程序中。API文档是开发人员理解和使用jbPM框架的关键资源,它详细解释了各种类、接口、方法和异常,以帮助开发者充分利用...

    jBPM5.2 eclipse 插件

    1. **流程建模**:插件提供了一个图形化的流程建模工具,支持BPMN 2.0标准,使得开发者可以直观地设计流程图,包括开始事件、结束事件、任务、网关、泳道等元素。 2. **流程部署**:用户可以直接从Eclipse中将流程...

    jbpm5.2学习1------安装与配置

    **jbpm5.2学习1——安装与配置** jbpm(Java Business Process Management)是一款开源的工作流管理系统,它提供了一套完整的解决方案,用于管理和执行业务流程。jbpm5.2是该系统的较早版本,但即便如此,它仍包含...

    jbpm4.4请假例子,eclipse工程

    1. **jbpm4.4**:jbpm4.4是jbpm的第4个主要版本,它提供了流程定义、流程实例管理和任务服务等功能。jbpm4.4支持BPMN 2.0标准,使得流程模型更接近业务人员的语言,同时也强化了对工作流的控制和监控。 2. **...

    jbpm5完整的例子

    【jbpm5完整的例子】是针对企业业务流程管理(Business Process Management, BPM)的一个实践教程,其中涵盖了jbpm5框架的多个核心功能和用法。jbPM是一个开源的工作流管理系统,它提供了强大的流程定义、执行和监控...

    jbpm5 web整合例子

    【jbpm5 web整合例子】是一个关于如何将JBPM5工作流引擎与Web应用程序整合的实践项目,适合那些想要在Web环境中使用业务流程管理(BPM)功能的开发者。JBPM5是一个开源的工作流和业务规则管理系统,它提供了一整套...

    jbpm4.0三个例子

    1. **LeaveJbpm**:这个示例可能是一个请假流程的应用,展示了如何在jbpm4中定义一个工作流,包括请假申请、审批等步骤。用户可以通过编写Java代码启动流程实例,同时也可以设置监听器来跟踪流程状态的变化,实现...

    JBPM例子

    【JBPM例子】是关于Java Business Process Management System (JBPM) 的一系列示例代码,用于展示如何在实际项目中使用这个工作流引擎。JBPM是一个开源的企业级平台,主要用于管理和执行业务流程。它提供了全面的...

    jbpm4web请假例子,eclipse项目,下卷。

    【jbpm4web请假例子】是一个基于JBPM4工作流引擎的示例应用,它演示了如何在Eclipse环境中开发和部署一个简单的请假流程。这个例子可以帮助开发者理解工作流管理系统的基本概念,以及如何将它们集成到实际的Web应用...

    JBPM工作流经典例子

    "JbpmTest"这个例子可能是对一个简单的JBPM流程的演示。通常,这会包含以下步骤: - 创建流程定义(Process Definition):使用JBPM的kie-workbench或Eclipse插件,设计流程图。 - 编译和部署流程:将流程模型编译为...

    JBPM5.4 工作流例子

    JBPM(JBoss Business Process Management)是一个开源的工作流管理系统,主要用于处理业务流程的自动化。JBPM5.4是该系统的一个版本,它在之前的基础上进行了优化和增强,提供了更为灵活和强大的流程管理功能。在本...

    JBPM4.4例子

    **JBPM4.4入门详解** JBPM,全称Java Business Process Management...通过实践这个简单的jsp+servlet例子,你将能够初步掌握如何在实际应用中使用JBPM。在学习过程中,不断探索和实践,逐步提升你的业务流程管理能力。

    一个JBPM工作流例子,JBPM

    JBPM(Java Business Process Management)是一个开源的工作流管理系统,它提供了一整套解决方案,用于设计、执行和管理业务流程。在本示例中,我们将深入探讨如何利用JBPM实现销售批复这一具体场景。 一、JBPM核心...

    jbpm3.2.2工作流入门例子

    jbpm3.2.2是jbpm的一个版本,它在当时提供了许多关键功能,如流程定义、流程实例管理、任务管理和事件处理等。本入门例子旨在帮助初学者快速理解和应用jbpm。 在jbpm3.2.2中,工作流引擎是核心组件,它负责解析流程...

    jbpm5第一个例子

    做好myeclipse集成后,按官方的文档做第一个例子即可。 http://www.mastertheboss.com/jbpm5/jbpm-5-tutorial-first-example 一般不会的问题,从官方找就行,但上面说的也不详细,结果搞了一天也没通,后来把条件...

    JBPM整合mysql小例子

    总结起来,"JBPM整合MySQL小例子"是一个演示如何将JBPM的工作流系统与MySQL数据库相结合的实际操作。通过这个过程,我们可以学习到JBPM的部署、配置、流程设计和监控等关键知识点,以及MySQL数据库的使用。这个例子...

    jbpm工作流程小例子

    3. **任务(Task)**:流程中的每个节点通常对应一个任务,可以是用户任务(需要人工处理)或服务任务(由系统自动执行)。 4. **工作流引擎(Workflow Engine)**:jbpm的核心组件,负责解析流程定义,管理流程...

    jbpm开发入门指南的例子

    总结,jbpm是一个功能强大的业务流程管理框架,通过理解基础概念、搭建开发环境、编写代码实现流程实例的启动和任务处理,可以逐步掌握jbpm的使用。"myjbpm"压缩包中的例子是一个很好的起点,通过实践加深理解,将...

Global site tag (gtag.js) - Google Analytics