工作流之前一直有在了解,也做過預(yù)研,算是參加工作后第一個(gè)預(yù)研的項(xiàng)目吧,以前的工作流對(duì)數(shù)據(jù)和流程控制的耦合太嚴(yán)重蚀之,對(duì)于系統(tǒng)之間的集成較困難,所以當(dāng)時(shí)預(yù)研的一個(gè)結(jié)論就是要把數(shù)據(jù)和流程控制分開捷泞,也做了些DEMO足删,最后進(jìn)去另外項(xiàng)目組了,此事就沒有進(jìn)行下去了锁右。
現(xiàn)在工作中需要使用工作流失受,選型用Activiti讶泰,可能是Actitivi比較簡單,在國內(nèi)受歡迎程度較高吧拂到,用的人比較多痪署。但是文檔相對(duì)較少,而且貌似大家都在使用較老的版本兄旬,目前Activiti 7.0.0是已經(jīng)有的狼犯,Activiti 6.0.0在2017年發(fā)布,應(yīng)該出來時(shí)間也算比較久了领铐,但是在一些技術(shù)交流群里面大家還是在用5.22.0版本悯森。 目前網(wǎng)上對(duì)于Activiti 5.x 的DEMO 還是比較多的,如使用自定義數(shù)據(jù)源绪撵,整合自有業(yè)務(wù)用戶角色關(guān)系呐馆,整合建模工具等等。Activiti 6.0.0 相對(duì)于 5.x包結(jié)構(gòu)和類都做了一些優(yōu)化莲兢,整合一起遇到了一些困難,記錄一下分享到給各位伙伴续膳。
此處 有Activiti 5.x 遷移到 6.0.0的主要變化改艇,遇到問題可以參考。
目錄 (貌似導(dǎo)航無用哦 應(yīng)該是簡書的問題)
Spring boot 1.5.6 + Activiti 6.0.0
先給出項(xiàng)目中的最后的POM文件:
<?xml version="1.0" encoding="UTF-8"?>
<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>workflow</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<properties>
<activiti.version>6.0.0</activiti.version>
<activiti-exp.version>5.22.0</activiti-exp.version>
</properties>
<dependencies>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.197</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.driver.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-rest-api</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-actuator</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-jpa</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-explorer</artifactId>
<version>${activiti-exp.version}</version>
</dependency>
<!--<dependency> 這個(gè)包看起來沒什么用坟岔,一旦加入的話由于他依賴于5.22的activiti-engine包谒兄,所以不能兼容6.0.0
<groupId>org.activiti</groupId>
<artifactId>activiti-diagram-rest</artifactId>
<version>${activiti-exp.version}</version>
</dependency>-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-simple-workflow</artifactId>
<version>${activiti-exp.version}</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-codec</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-css</artifactId>
<version>1.7</version>
</dependency>
<!-- Testing -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.18.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.subethamail</groupId>
<artifactId>subethasmtp-wiser</artifactId>
<version>1.2</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
配置Activiti數(shù)據(jù)源
一般會(huì)有兩個(gè)數(shù)據(jù)源,業(yè)務(wù)數(shù)據(jù)源+Activiti(自帶)數(shù)據(jù)庫, 業(yè)務(wù)數(shù)據(jù)源的配置這里不提社付,大家應(yīng)該個(gè)有自己的配置方式承疲,簡單的直接使用spring boot默認(rèn)的屬性配置方式即可,這里重點(diǎn)列出配置Activiti自帶數(shù)據(jù)源(自定義節(jié)點(diǎn)spring.activiti.datasource來存儲(chǔ)activiti數(shù)據(jù)源配置):
spring:
activiti:
async-executor-activate: true
mail-server-use-ssl: true
#自動(dòng)更新數(shù)據(jù)庫
database-schema-update: true
#校驗(yàn)流程文件鸥咖,默認(rèn)校驗(yàn)resources下的processes文件夾里的流程文件
#check-process-definitions: false
#restApiEnabled: false
datasource:
driverClassName: com.mysql.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://127.0.0.1:3306/activiti6ui?useUnicode=true&characterEncoding=UTF-8
username: root
password: 123456
hikari:
maximum-pool-size: 30
idle-timeout: 30000
connection-test-query: select 1 from DUAL
auto-commit: true
minimum-idle: 5
connection-timeout: 30000
pool-name: activiti-datasource-pool
這里使用的屬性是spring.activiti.datasource 屬于自定義屬性燕鸽,所以需要如下配置:
@Configuration
@EnableTransactionManagement//開啟事物管理
@EnableJpaRepositories(//自定義數(shù)據(jù)管理的配置
entityManagerFactoryRef = "activitiEntityManagerFactory", //指定EntityManager的創(chuàng)建工廠Bean
transactionManagerRef = "activitiTransactionManager",//指定事物管理的Bean
basePackages = {"com.fintech.insurance.micro.workflow.persist.entity"})//指定管理的實(shí)體位置
public class ActivitiConfiguration extends AbstractProcessEngineAutoConfiguration {
@Bean("activitiDataSourceProperties")
@ConfigurationProperties(prefix = "spring.activiti.datasource")
public DataSourceProperties activitiDataSourceProperties() {
return new DataSourceProperties();
}
@Bean("activitiDataSource")
public HikariDataSource activitiDataSource() {
DataSourceProperties activitiDataSourceProperties = activitiDataSourceProperties();
return (HikariDataSource)activitiDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
@Primary
@Bean("insuranceActivitiConfig")
public SpringProcessEngineConfiguration insuranceActivitiConfig(PlatformTransactionManager transactionManager,
SpringAsyncExecutor springAsyncExecutor) throws IOException {
SpringProcessEngineConfiguration springProcessEngineConfiguration = baseSpringProcessEngineConfiguration(
activitiDataSource(),
transactionManager,
springAsyncExecutor);
return springProcessEngineConfiguration;
}
}
這里使用spring boot的配置重載了SpringProcessEngineConfiguration, 使得springProcessEngineConfiguration能讀取activiti自有數(shù)據(jù)源。
配置自定義用戶-組/角色管理
Activiti有自己的用戶啼辣、角色管理功能以及數(shù)據(jù)庫表啊研,用于給工作流中的工作項(xiàng)分配、指派任務(wù)鸥拧,Activiti中與此相關(guān)的表主要有:
身份相關(guān)的表:
act_id_user:用戶表
act_id_info:用戶信息表
act_id_group:組(或角色)
act_id_memership:用戶與組的關(guān)系 党远,查詢?nèi)蝿?wù)獲選人需關(guān)聯(lián)的表
自己的業(yè)務(wù)系統(tǒng)一般也有自己的用戶-角色(組)管理模塊,可參考鏈接 富弦,上面簡要說了幾種解決方案沟娱,可以根據(jù)自己的現(xiàn)實(shí)情況進(jìn)行決策。我這邊是重新自己實(shí)現(xiàn)了UserEntityManager腕柜, GroupEntityManager接口济似,把自己業(yè)務(wù)中的用戶-角色(組管理)注入到Activiti引擎之中矫废,這里只重寫一兩個(gè)比較重要的方法,大部分方法不需要實(shí)現(xiàn)碱屁,基本能滿足需求磷脯。這部分跟Activiti 5.x的整合不一樣,網(wǎng)上至今未看到例子娩脾。
- 需要自定義ActivitiUser類赵誓,實(shí)現(xiàn)org.activiti.engine.impl.persistence.entity.UserEntity接口
- 需要自定義ActivitiRole類,實(shí)現(xiàn)org.activiti.engine.impl.persistence.entity.GroupEntity接口
- 下面代碼中ActivitiUserGroupFeign是自有業(yè)務(wù)系統(tǒng)的用戶角色(組)服務(wù)
import org.activiti.engine.identity.Group;
import org.activiti.engine.identity.Picture;
import org.activiti.engine.identity.User;
import org.activiti.engine.identity.UserQuery;
import org.activiti.engine.impl.Page;
import org.activiti.engine.impl.UserQueryImpl;
import org.activiti.engine.impl.interceptor.Session;
import org.activiti.engine.impl.persistence.entity.UserEntity;
import org.activiti.engine.impl.persistence.entity.UserEntityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @Description: (some words)
* @Author: Yong Li
* @Date: 2018/8/8 16:52
*/
@Component
public class CustomUserEntityManager implements UserEntityManager, Session {
@Autowired
private ActivitiUserGroupFeign activitiUserGroupFeign;
// session interface
@Override
public void flush() {
// do nothing
}
// session interface
@Override
public void close() {
// do nothing
}
@Override
public User createNewUser(String userId) {
return new ActivitiUser();
}
@Override
public void updateUser(User updatedUser) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public List<User> findUserByQueryCriteria(UserQueryImpl query, Page page) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public long findUserCountByQueryCriteria(UserQueryImpl query) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public List<Group> findGroupsByUser(String userId) {
List<ActivitiRole> roles = activitiUserGroupFeign.listRolesByUserMobile(userId).getDataNoPatience();
List<Group> result = new ArrayList<>();
if (null != roles) {
for (ActivitiRole r : roles) {
result.add(r);
}
}
return result;
}
@Override
public UserQuery createNewUserQuery() {
return new UserQueryImpl();
}
@Override
public Boolean checkPassword(String userId, String password) {
ActivitiUser user = activitiUserGroupFeign.findUserByMobile(userId).getDataNoPatience();
return null != user && user.getPassword().equals(password);
}
@Override
public List<User> findUsersByNativeQuery(Map<String, Object> parameterMap, int firstResult, int maxResults) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public long findUserCountByNativeQuery(Map<String, Object> parameterMap) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public boolean isNewUser(User user) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public Picture getUserPicture(String userId) {
ActivitiUser user = activitiUserGroupFeign.findUserByMobile(userId).getDataNoPatience();
return user.getPicture();
}
@Override
public void setUserPicture(String userId, Picture picture) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public void deletePicture(User user) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public UserEntity create() {
return new ActivitiUser();
}
@Override
public UserEntity findById(String entityId) {
ActivitiUser user = activitiUserGroupFeign.findUserByMobile(entityId).getDataNoPatience();
return user;
}
@Override
public void insert(UserEntity entity) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public void insert(UserEntity entity, boolean fireCreateEvent) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public UserEntity update(UserEntity entity) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public UserEntity update(UserEntity entity, boolean fireUpdateEvent) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public void delete(String id) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public void delete(UserEntity entity) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public void delete(UserEntity entity, boolean fireDeleteEvent) {
throw BaseExceptionFactory.buildBaseException(201001);
}
}
import org.activiti.engine.identity.*;
import org.activiti.engine.impl.GroupQueryImpl;
import org.activiti.engine.impl.Page;
import org.activiti.engine.impl.interceptor.Session;
import org.activiti.engine.impl.persistence.entity.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @Description: (some words)
* @Author: Yong Li
* @Date: 2018/8/8 17:53
*/
@Component
public class CustomGroupEntityManager implements GroupEntityManager, Session {
@Autowired
private ActivitiUserGroupFeign activitiUserGroupFeign;
// session interface
@Override
public void flush() {
// do nothing
}
// session interface
@Override
public void close() {
// do nothing
}
@Override
public Group createNewGroup(String groupId) {
return new ActivitiRole();
}
@Override
public GroupQuery createNewGroupQuery() {
return new GroupQueryImpl();
}
@Override
public List<Group> findGroupByQueryCriteria(GroupQueryImpl query, Page page) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public long findGroupCountByQueryCriteria(GroupQueryImpl query) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public List<Group> findGroupsByUser(String userId) {
List<ActivitiRole> roles = activitiUserGroupFeign.listRolesByUserMobile(userId).getDataNoPatience();
List<Group> result = new ArrayList<>();
if (null != roles) {
for (ActivitiRole r : roles) {
result.add(r);
}
}
return result;
}
@Override
public List<Group> findGroupsByNativeQuery(Map<String, Object> parameterMap, int firstResult, int maxResults) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public long findGroupCountByNativeQuery(Map<String, Object> parameterMap) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public boolean isNewGroup(Group group) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public GroupEntity create() {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public GroupEntity findById(String entityId) {
return activitiUserGroupFeign.findRoleByCode(entityId).getDataNoPatience();
}
@Override
public void insert(GroupEntity entity) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public void insert(GroupEntity entity, boolean fireCreateEvent) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public GroupEntity update(GroupEntity entity) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public GroupEntity update(GroupEntity entity, boolean fireUpdateEvent) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public void delete(String id) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public void delete(GroupEntity entity) {
throw BaseExceptionFactory.buildBaseException(201001);
}
@Override
public void delete(GroupEntity entity, boolean fireDeleteEvent) {
throw BaseExceptionFactory.buildBaseException(201001);
}
}
再分別實(shí)現(xiàn)CustomUserEntityManagerFactory, CustomGroupEntityManagerFactory
@Service
public class CustomUserEntityManagerFactory implements SessionFactory {
// 使用自定義的User管理類
@Autowired
private CustomUserEntityManager customUserEntityManager;
@Autowired
public Class<?> getSessionType() {
//注意此處也必須為Activiti原生類
return UserEntityManager.class;
}
@Override
public Session openSession(CommandContext commandContext) {
return customUserEntityManager;
}
}
@Service
public class CustomGroupEntityManagerFactory implements SessionFactory {
@Autowired
private CustomGroupEntityManager customGroupEntityManager;
@Override
public Class<?> getSessionType() {
//注意此處必須為Activiti原生的類柿赊,否則自定義類不會(huì)生效
return GroupEntityManager.class;
}
@Override
public Session openSession(CommandContext commandContext) {
return customGroupEntityManager;
}
}
最后把自定義的用戶-組管理注入到Activiti配置中:
@Primary
@Bean("insuranceActivitiConfig")
public SpringProcessEngineConfiguration insuranceActivitiConfig(PlatformTransactionManager transactionManager,
SpringAsyncExecutor springAsyncExecutor) throws IOException {
SpringProcessEngineConfiguration springProcessEngineConfiguration = baseSpringProcessEngineConfiguration(
activitiDataSource(),
transactionManager,
springAsyncExecutor);
// 配置自定義的用戶和組管理
springProcessEngineConfiguration.setUserEntityManager(customUserEntityManager);
springProcessEngineConfiguration.setGroupEntityManager(customGroupEntityManager);
List<SessionFactory> customSessionFactories = new ArrayList<>();
customSessionFactories.add(customUserEntityManagerFactory);
customSessionFactories.add(customGroupEntityManagerFactory);
springProcessEngineConfiguration.setCustomSessionFactories(customSessionFactories);
return springProcessEngineConfiguration;
}
自定義用戶-角色(組)配置完成俩功。
流程測試用例編寫
流程設(shè)計(jì)完之后,需要對(duì)流程的過程進(jìn)行測試碰声,特別對(duì)于剛?cè)腴T的新人诡蜓,不熟悉BPMN每個(gè)元素的作用,需要通過實(shí)驗(yàn)或測試觀察結(jié)果是否與期望的結(jié)果一致胰挑。如果直接使用spring boot環(huán)境蔓罚,或spring cloud,在多個(gè)微服務(wù)集成的環(huán)境中進(jìn)行測試瞻颂,前期特別耗費(fèi)時(shí)間豺谈,所以建議可以用如下的測試方法: 數(shù)據(jù)源使用h2內(nèi)存數(shù)據(jù)源, 在test/resources下面準(zhǔn)備文件activiti.cfg.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration">
<property name="jdbcUrl" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
<property name="jdbcDriver" value="org.h2.Driver" />
<property name="jdbcUsername" value="sa" />
<property name="jdbcPassword" value="" />
<!-- Database configurations -->
<property name="databaseSchemaUpdate" value="drop-create" />
<!-- job executor configurations 如果有定時(shí)任務(wù)贡这,比如 Time Boundoury Activiti茬末, 需要開啟下面配置-->
<property name="asyncExecutorActivate" value="true" />
<!-- mail server configurations -->
<property name="mailServerPort" value="5025" />
</bean>
</beans>
測試用例例子
public class RequisitionFlowTest extends PluggableActivitiTestCase {
protected static final long REQUISITION_CANCEL_TIMEOUT_SECONDES = 4;
protected static final long REQUISITION_PAYMENT_TIMEOUT_SECONDES = 4;
protected Map<String, Object> processVars;
@Override
protected void setUp() throws Exception {
super.setUp();
processVars = new HashMap<>();
processVars.put("requisitionFlowService", new RequisitionFlowService());
processVars.put("requisitionNumber", "test_requisition");
String cancelTime = DateCommonUtils.formatDateISO8601(DateCommonUtils.getAfterSeconds(new Date(), (int)REQUISITION_CANCEL_TIMEOUT_SECONDES));
System.out.println("=====cancel time expression:" + cancelTime);
processVars.put("confirmTimeoutTime", cancelTime);
}
@Override
public void tearDown() throws Exception {
super.tearDown();
}
@Test
@Deployment(resources = {"processes/Insurance_Requisition.bpmn20.xml"})
public void testCustomerTimeoutForConfirm() throws InterruptedException {
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("requisitionV1", processVars);
// 檢查流程是否未結(jié)束
ProcessInstance qpi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstance.getId()).singleResult();
Assert.assertNotNull(qpi);
Task task1 = taskService.createTaskQuery().processDefinitionKey(processInstance.getProcessDefinitionKey()).taskDefinitionKey("customerConfirmTask").singleResult();
Assert.assertNotNull(task1);
// 等待用戶任務(wù)完成
Thread.sleep((REQUISITION_CANCEL_TIMEOUT_SECONDES + 12) * 1000); //定時(shí)任務(wù)貌似每隔10秒重新掃一次
// 檢查歷史流程實(shí)例是否已經(jīng)結(jié)束
Assert.assertNull(runtimeService.createProcessInstanceQuery().processInstanceId(processInstance.getId()).singleResult());
}
protected String generatePayDueDateVariable() {
return DateCommonUtils.formatDateISO8601(DateCommonUtils.getAfterSeconds(new Date(), (int)REQUISITION_PAYMENT_TIMEOUT_SECONDES));
}
}
使用這種測試用例進(jìn)行測試時(shí),基本可以完成所有與用戶無關(guān)的活動(dòng)連接測試盖矫,比如從節(jié)點(diǎn)A到節(jié)點(diǎn)F的邏輯丽惭,對(duì)于初步的練習(xí)和測試來說是非常高效的。
整合Activiti-Explorer
Activiti 建模工具提供有兩種: 一種是eclipse plugins, 一種的自帶的web 版的辈双,可以從官方網(wǎng)站上下載责掏,在activiti-6.0.0 下面wars/activiti-app.war。我比較喜歡后者湃望,如果能在自己的業(yè)務(wù)系統(tǒng)中提供一個(gè)如下的工作流流程設(shè)計(jì)器給運(yùn)營或者產(chǎn)品經(jīng)理拷橘,是一個(gè)不錯(cuò)的選擇。
遺憾的是activiti 6.0.0并未提供流程設(shè)計(jì)器依賴的jar包(也許是我沒有找到)喜爷,所以我這還是Activiti engine 6.0.0 + Activiti Explorer 5.22.0冗疮。
我這里參考github 項(xiàng)目 , 只需要把pom.xml文件依賴文件換成最開始提到的pom就好了。
最終檩帐,就能在自己的項(xiàng)目里面嵌入了流程設(shè)計(jì)器术幔,并且能在上面編輯/創(chuàng)建流程, 并對(duì)流程進(jìn)行在線發(fā)布。
由于版本的不匹配湃密,是否會(huì)對(duì)流程設(shè)計(jì)器最終設(shè)計(jì)的圖在engine6.0上有什么樣的問題诅挑,目前還未知四敞。