Activiti7 的基本介紹

前言

最近接到上級(jí)的安排,要我為公司的某個(gè)系統(tǒng)設(shè)計(jì)一套工作流引擎。對(duì)于工作流引擎這一個(gè)主題,我查找了許多資料,在java方面用的比較多的是一個(gè)名叫Activiti的開(kāi)源工作流引擎,通過(guò)與同事們交流之后,決定了使用該開(kāi)源工作流引擎。在這段時(shí)間也是花了些時(shí)間來(lái)學(xué)習(xí)研究Activiti。網(wǎng)上的資料并不多,官方的文檔是全英文的,而且更新的貌似不是很及時(shí),所以準(zhǔn)備寫(xiě)這個(gè)系列的文檔來(lái)記錄我的學(xué)習(xí)過(guò)程。

官方地址

我的寫(xiě)作水平還有待提高,可能很多人更愿意直接去看官方的一些教程,所以我先把地址放上!

官方地址

官方最新用戶文檔-V6.0.0

碼云鏡像-activiti-7-developers-guide

關(guān)于BPMN

BPMN(Business Process Model AndNotation)- 業(yè)務(wù)流程模型和符號(hào) 是由BPMI(BusinessProcess Management Initiative)開(kāi)發(fā)的一套標(biāo)準(zhǔn)的業(yè)務(wù)流程建模符號(hào),使用BPMN提供的符號(hào)可以創(chuàng)建業(yè)務(wù)流程。2004年5月發(fā)布了BPMN1.0規(guī)范.BPMI于2005年9月并入OMG(The Object Management Group對(duì)象管理組織)組織OMG于2011年1月發(fā)布BPMN2.0的最終版本。

Activiti 就是使用 BPMN 2.0 進(jìn)行流程建模、流程執(zhí)行管理,它包括很多的建模符號(hào)。

image-20210116163216372

可以使用這些符號(hào)來(lái)繪制流程圖,例如下圖:

image-20210116165507302

Activiti也是通過(guò)將這些流程圖的BPMN文件部署到數(shù)據(jù)庫(kù)中,然后啟動(dòng)相應(yīng)的流程,來(lái)完成工作流的一個(gè)映射。這些節(jié)點(diǎn)可以指定一些參數(shù)、表達(dá)式、綁定事件或者綁定解析處理類,來(lái)實(shí)現(xiàn)對(duì)每個(gè)流程節(jié)點(diǎn)的處理。

對(duì)于BPMN流程圖的繪制方法這里就不做贅述,因?yàn)槲乙膊皇橇私夂芏?,網(wǎng)上有許多非常好的文章可供參考。

Activiti 的架構(gòu)

引擎API是與Activiti交互的最常見(jiàn)方式。中心起點(diǎn)是ProcessEngine,可以按照配置部分中所述的多種方式創(chuàng)建 。從ProcessEngine,您可以獲得包含工作流/ BPM方法的各種服務(wù)。ProcessEngine和服務(wù)對(duì)象是線程安全的。因此,您可以為整個(gè)服務(wù)器保留對(duì)其中之一的引用。

api.services

Activiti的工作流程是通過(guò)讀取一個(gè)配置文件,然后得到一個(gè)工作流引擎實(shí)例,通過(guò)這個(gè)引擎可以獲取多個(gè)不同模塊的Service,然后就可以使用這些Service去完成相應(yīng)的接口,比如部署會(huì)使用到RepositoryService,實(shí)例會(huì)用到RuntimeService等。(注意:FormServiceIdentityService已經(jīng)在新版本中刪除了)

// 獲取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 獲取Service
RuntimeService runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
ManagementService managementService = processEngine.getManagementService();
IdentityService identityService = processEngine.getIdentityService();
HistoryService historyService = processEngine.getHistoryService();
FormService formService = processEngine.getFormService();
DynamicBpmnService dynamicBpmnService = processEngine.getDynamicBpmnService();

Service總覽

Service名稱 作用
RepositoryService 資源管理服務(wù)
RuntimeService 流程運(yùn)行管理類
TaskService 任務(wù)管理類
HistoryService 歷史管理類
ManagerService 引擎管理類
Activiti API
activiti運(yùn)行流程

流程部署

在使用Activiti進(jìn)行流程管理之前,首先需要將建模工具繪制的業(yè)務(wù)流程圖部署到數(shù)據(jù)庫(kù)中,這個(gè)時(shí)候就需要使用RepositoryService,可以通過(guò)RepositoryService進(jìn)行流程部署、查詢流程定義、暫?;蚣せ畎l(fā)布的流程定義等。官方教程

部署流程

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
Deployment deploy = deploymentBuilder
                                .addClasspathResource("bpmn/stadiumapplication.bpmn")
                .name("球場(chǎng)申請(qǐng)流程")
                .deploy();

部署方式

ReposityService主要就是依靠調(diào)用DeploymentBuilder的接口來(lái)進(jìn)行流程定義的部署的。DeploymentBuilder支持多種方式的部署。

public interface DeploymentBuilder {
    // 文件流方式部署
  DeploymentBuilder addInputStream(String resourceName, InputStream inputStream);
  DeploymentBuilder addInputStream(String resourceName,Resource resource);
    // 資源文件方式部署
  DeploymentBuilder addClasspathResource(String resource);
    // 字符串內(nèi)容部署,一般是bpmn的xml內(nèi)容字符串部署
  DeploymentBuilder addString(String resourceName, String text);
    // 字節(jié)數(shù)組部署
  DeploymentBuilder addBytes(String resourceName, byte[] bytes);
  // Zip壓縮包部署
  DeploymentBuilder addZipInputStream(ZipInputStream zipInputStream);
  // Bpmn模型部署,可動(dòng)態(tài)生成BPMN model進(jìn)行部署。
  DeploymentBuilder addBpmnModel(String resourceName, BpmnModel bpmnModel);

  DeploymentBuilder setProjectManifest(ProjectManifest projectManifest);

  DeploymentBuilder setEnforcedAppVersion(Integer enforcedAppVersion);
  
  /**
   *如果調(diào)用,則不會(huì)對(duì)bpmn2.0xsd進(jìn)行XML模式驗(yàn)證。 
   *一般不推薦
   */
  DeploymentBuilder disableSchemaValidation();

  /**
   * 如果調(diào)用,則不會(huì)對(duì)流程定義進(jìn)行驗(yàn)證,以確定流程定義在引擎上是可執(zhí)行的。
   * 一般不推薦使用。
   */
  DeploymentBuilder disableBpmnValidation();

  /**
   * 為部署指定名字
   */
  DeploymentBuilder name(String name);

  /**
   * 為部署指定種類
   */
  DeploymentBuilder category(String category);

  /**
   * 為部署指定Key,該屬性默認(rèn)是bpmn的id
   */
  DeploymentBuilder key(String key);

  /**
   * 為部署指定租戶ID,沒(méi)有用到過(guò)。。。
   */
  DeploymentBuilder tenantId(String tenantId);

  /**
   * 如果已設(shè)置,則此部署將與以前的任何部署進(jìn)行比較。這意味著每個(gè)(未生成的)資源都將與此部署提供的資源進(jìn)行比較。
   */
  DeploymentBuilder enableDuplicateFiltering();
  
  /**
   * 設(shè)置激活此部署中包含的流程定義的日期。這意味著所有流程定義都將像往常一樣部署,但它們將從在給定的激活日期之前開(kāi)始。
   */
  DeploymentBuilder activateProcessDefinitionsOn(Date date);
  
  /**
   * 允許將影響部署的屬性添加到實(shí)例中
   */
  DeploymentBuilder deploymentProperty(String propertyKey, Object propertyValue);
  
  /**
   * 將所有提供的源部署到Activiti引擎。
   */
  Deployment deploy();

}

涉及數(shù)據(jù)庫(kù)表

表名 作用
ACT_RE_DEPLOYMENT 流程定義部署表:每部署一次增加一條記錄
ACT_RE_PROCDEF 流程定義表:部署每個(gè)新的流程都會(huì)在這張表上增加一條記錄
ACT_GE_BYTEARRAY 流程資源表:存儲(chǔ)xml和png內(nèi)容的表
  • ACT_GE_BYTEARRAY

    該表記錄了流程的資源信息,包括bpmn文件的xml內(nèi)容和png圖片的二進(jìn)制內(nèi)容,RepositoryService也提供了相應(yīng)的接口去獲取這些資源。

    SELECT * FROM ACT_GE_BYTEARRAY;
    
    ID_ REV_ NAME_ DEPLOYMENT_ID_ BYTES_ GENERATED_
    2 1 bpmn/stadiumapplication.bpmn 1 (BLOB)4.43
  • ACT_RE_DEPLOYMENT

    SELECT * FROM ACT_RE_DEPLOYMENT;
    
    ID_ NAME_ CATEGORY_ KEY_ TENANT_ID_ DEPLOY_TIME_ ENGINE_VERSION_
    1 球場(chǎng)申請(qǐng)流程 2021-01-17 08:59:05
  • ACT_RE_PROCDEF

    SELECT * FROM ACT_RE_PROCDEF;
    
    ID_ REV_ CATEGORY_ NAME_ KEY_ VERSION_ DEPLOYMENT_ID_ RESOURCE_NAME_ DGRM_RESOURCE_NAME_ DESCRIPTION_ HAS_START_FORM_KEY_ HAS_GRAPHICAL_NOTATION_ SUSPENSION_STATE_ TENANT_ID_ ENGINE_VERSION_
    ballrepally:1:3 1 http://www.activiti.org/testm1574124674914 球場(chǎng)申請(qǐng) ballrepally 1 1 bpmn/stadiumapplication.bpmn 0 1 1

    KEY_ 這個(gè)字段是用來(lái)唯一識(shí)別不同流程的關(guān)鍵字。

    ACT_RE_DEPLOYMENTACT_RE_PROCDEF是一對(duì)多的關(guān)系,即:一次部署可以部署多個(gè)流程定義,而只會(huì)在流程部署表(ACT_RE_DEPLOYMENT)生成一條記錄。

    一般情況下建議一次部署只部署一個(gè)流程。

查詢流程信息

  • 查詢流程部署信息

    // 獲取流程部署查詢器
    DeploymentQuery deploymentQuery =  repositoryService.createDeploymentQuery();
    // 查詢列表。
    List<Deployment> deployments = deploymentQuery.list();
    
  • 查詢流程定義信息

    // 獲取流程定義查詢器
    ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
    // 查詢器構(gòu)建查詢條件,最終返回列表。
    List<ProcessDefinition> processDefinitionList = processDefinitionQuery
                                                                  .orderByProcessDefinitionVersion()
                                                                  .asc()
                                                                  .list();
    
  • 查詢部署資源

    public interface RepositoryService {
      
      /**
       * 通過(guò)部署ID和資源名稱獲取資源流。 
       * @param deploymentId 部署ID
       * @param resourceName 資源名稱 
       */
      InputStream getResourceAsStream(String deploymentId, String resourceName);
      
      /**
       * 檢索給定部署的部署資源列表,按字母順序排列。 
       * @param deploymentId 部署ID
       */
      List<String> getDeploymentResourceNames(String deploymentId);
      
      /**
       * 允許通過(guò)字節(jié)流訪問(wèn)已部署的流程模型,例如BPMN2.0XML文件。
       * @param processDefinitionId 流程定義ID
       */
      InputStream getProcessModel(String processDefinitionId);
      
      /**
       * 返回流程定義信息
       * @param processDefinitionId 流程定義ID
       */
      ProcessDefinition getProcessDefinition(String processDefinitionId);
       
    
      /**
       * 返回與具有提供的進(jìn)程定義id的進(jìn)程定義對(duì)應(yīng)的BPMN 模型。
       * @param processDefinitionId 流程定義ID
       */
      BpmnModel getBpmnModel(String processDefinitionId);
      
    }
    

掛起流程

public interface RepositoryService {
    
   /**
   * 掛起給定id的流程定義。  
   */
  void suspendProcessDefinitionById(String processDefinitionId);
  
    /**
   * 掛起給定id的流程定義。  
   *
   * @param suspendProcessInstances 是否掛起流程實(shí)例
   *    如果為true,則提供的流程定義的所有流程實(shí)例也將掛起
   * @param suspensionDate 掛起日期
   *        進(jìn)程定義將被掛起的日期。如果為null,進(jìn)程定義將立即掛起。注意:作業(yè)執(zhí)行器需要處于活動(dòng)狀態(tài)才能使用此命令!
   */
  void suspendProcessDefinitionById(String processDefinitionId, 
                                    boolean suspendProcessInstances, 
                                    Date suspensionDate);
  
  /**
   *  掛起給定流程定義Key的流程
   */
  void suspendProcessDefinitionByKey(String processDefinitionKey);
  
  /** 
   *  掛起給定流程定義Key的流程
   * @param suspendProcessInstances 是否掛起流程實(shí)例
   *    如果為true,則提供的流程定義的所有流程實(shí)例也將掛起
   * @param suspensionDate 掛起日期
   *        進(jìn)程定義將被掛起的日期。如果為null,進(jìn)程定義將立即掛起。注意:作業(yè)執(zhí)行器需要處于活動(dòng)狀態(tài)才能使用此命令!
   */
  void suspendProcessDefinitionByKey(String processDefinitionKey, 
                                     boolean suspendProcessInstances,
                                     Date suspensionDate); 
  
  /**
   * 掛起給定流程定義Key和租戶Id的流程。
   */
  void suspendProcessDefinitionByKey(String processDefinitionKey, String tenantId);
  
  ...
}

激活流程

public interface RepositoryService {
    /**
   *  激活指定流程定義ID的流程
   */
  void activateProcessDefinitionById(String processDefinitionId); 
  
  /**
   * 激活指定流程定義ID的流程
   *
   * @param suspendProcessInstances 是否掛起流程實(shí)例
   *    如果為true,則提供的流程定義的所有流程實(shí)例也將激活
   * @param suspensionDate 掛起日期
   *        進(jìn)程定義將被激活的日期。如果為null,進(jìn)程定義將立即激活。注意:作業(yè)執(zhí)行器需要處于掛起狀態(tài)才能使用此命令!
   */
  void activateProcessDefinitionById(String processDefinitionId, 
                                     boolean activateProcessInstances, 
                                     Date activationDate);
  
  /**
   * 激活給定流程定義Key(BPMN圖中的ID)的流程 
   */
  void activateProcessDefinitionByKey(String processDefinitionKey);
  
  /**
   * 激活指定流程定義Key的流程
   *
   * @param suspendProcessInstances 是否掛起流程實(shí)例
   *    如果為true,則提供的流程定義的所有流程實(shí)例也將激活
   * @param suspensionDate 掛起日期
   *        進(jìn)程定義將被激活的日期。如果為null,進(jìn)程定義將立即激活。注意:作業(yè)執(zhí)行器需要處于掛起狀態(tài)才能使用此命令!
   */
  void activateProcessDefinitionByKey(String processDefinitionKey, 
                                      boolean activateProcessInstances, 
                                      Date activationDate);
  
   /**
   *  激活給定流程定義Key和租戶Id的流程
   */
  void activateProcessDefinitionByKey(String processDefinitionKey, String tenantId);
  
  ...
}

刪除流程

public interface RepositoryService {
 
  /**
   * 刪除指定流程. 
   * @param deploymentId 流程部署ID
   */
  void deleteDeployment(String deploymentId);
  
  /**
   * 刪除給定的部署和級(jí)聯(lián)刪除到流程實(shí)例、歷史流程實(shí)例和作業(yè)。
   *
   * @param deploymentId 流程部署ID
   * @param cascade          是否級(jí)聯(lián)刪除
   */
  void deleteDeployment(String deploymentId, boolean cascade);
  
}

流程實(shí)例

當(dāng)我們將流程部署完成之后,先要使用他就得先開(kāi)始一個(gè)流程實(shí)例。所謂流程實(shí)例,即比如我們部署了一個(gè)請(qǐng)假流程,小李的請(qǐng)假流程就是一個(gè)實(shí)例,小王的請(qǐng)假流程也是一個(gè)實(shí)例。所以我們的流程實(shí)例也是基于流程部署來(lái)實(shí)現(xiàn)的。

流程實(shí)例的管理底層使用的是RuntimeService,而最新提供的ProcessRuntime對(duì)流程進(jìn)行了二次封裝,簡(jiǎn)便了api的調(diào)用。

啟動(dòng)流程實(shí)例

  ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  RuntimeService runtimeService = processEngine.getRuntimeService();
public interface RuntimeService {
    
    /**
   * 啟動(dòng)先前創(chuàng)建的流程實(shí)例。 
   * @param createdProcessInstance 已經(jīng)創(chuàng)建的流程實(shí)例 
   */
  ProcessInstance startCreatedProcessInstance(ProcessInstance createdProcessInstance, Map<String, Object> variables);
    
 /**
   * 開(kāi)啟指定流程定義Key的最新版本 
   * @param processDefinitionKey 流程定義的Key
   */
  ProcessInstance startProcessInstanceByKey(String processDefinitionKey);

  /**
   * 開(kāi)啟給定流程定義Key的最新版本,并指定業(yè)務(wù)流程Key   
   * @param processDefinitionKey 流程定義Key
   * @param businessKey 業(yè)務(wù)流程Key
   */
  ProcessInstance startProcessInstanceByKey(String processDefinitionKey, String businessKey);

  /**
   * 開(kāi)啟指定流程定義Key的最新版本
   *
   * @param processDefinitionKey 流程定義的Key
   * @param variables 流程的參數(shù),比如uel參數(shù) 
   */
  ProcessInstance startProcessInstanceByKey(String processDefinitionKey, Map<String, Object> variables);

  /**
   * 開(kāi)啟給定流程定義Key的最新版本,并指定業(yè)務(wù)流程Key 
   * @param processDefinitionKey 流程定義Key
   * @param variables 參數(shù)
   * @param businessKey 業(yè)務(wù)Key
   */
  ProcessInstance startProcessInstanceByKey(String processDefinitionKey, String businessKey, Map<String, Object> variables);

  /**
   * 開(kāi)啟指定流程定義Id的最新版本. 
   * @param processDefinitionId 流程定義Id 
   */
  ProcessInstance startProcessInstanceById(String processDefinitionId);

  /**
   * 開(kāi)啟給定流程定義Id的最新版本,并指定業(yè)務(wù)流程Key
   * @param processDefinitionId 流程定義ID
   */
  ProcessInstance startProcessInstanceById(String processDefinitionId, String businessKey);

  /**
   * 開(kāi)啟給定流程定義Id的最新版本
   * @param processDefinitionId 流程定義Id
   * @param variables 參數(shù)
   */
  ProcessInstance startProcessInstanceById(String processDefinitionId, Map<String, Object> variables);

  /**
   * 開(kāi)啟給定流程定義Id的最新版本 
   * @param processDefinitionId 流程定義Id
   * @param variables 參數(shù)
   */
  ProcessInstance startProcessInstanceById(String processDefinitionId, String businessKey, Map<String, Object> variables);

}

涉及數(shù)據(jù)庫(kù)表

  • ACT_RU_EXECTION

    流程實(shí)例執(zhí)行表,記錄當(dāng)前流程實(shí)例的執(zhí)行情況。流程實(shí)例執(zhí)行,如果當(dāng)前只有一個(gè)分支時(shí),一個(gè)流程實(shí)例只有一條記錄且執(zhí)行表的主鍵id和流程實(shí)例id相同,如果當(dāng)前有多個(gè)分支正在運(yùn)行則該執(zhí)行表中有多條記錄,存在執(zhí)行表的主鍵和流程實(shí)例id不相同的記錄。

    不論當(dāng)前有幾個(gè)分支總會(huì)有一條記錄的執(zhí)行表的主鍵和流程實(shí)例id相同

    一個(gè)流程實(shí)例運(yùn)行完成,此表中與流程實(shí)例相關(guān)的記錄刪除。

  • ACT_RU_TASK

    任務(wù)執(zhí)行表,記錄當(dāng)前執(zhí)行的任務(wù)。啟動(dòng)流程實(shí)例,流程當(dāng)前執(zhí)行到第一個(gè)任務(wù)結(jié)點(diǎn),此表會(huì)插入一條記錄表示當(dāng)前任務(wù)的執(zhí)行情況,如果任務(wù)完成則記錄刪除。

  • ACT_RU_IDENTITYLINK

    任務(wù)參與者,記錄當(dāng)前參與任務(wù)的用戶或組。

  • ACT_HI_PROCINST

    流程實(shí)例歷史表,流程實(shí)例啟動(dòng),會(huì)在此表插入一條記錄,流程實(shí)例運(yùn)行完成記錄也不會(huì)刪除。

  • ACT_HI_TASKINST

    任務(wù)歷史表,記錄所有任務(wù),開(kāi)始一個(gè)任務(wù),不僅在act_ru_task表插入記錄,也會(huì)在歷史任務(wù)表插入一條記錄,任務(wù)歷史表的主鍵就是任務(wù)id,任務(wù)完成此表記錄不刪除。

  • ACT_HI_ACTINST

    活動(dòng)歷史表,記錄所有活動(dòng),活動(dòng)包括任務(wù),所以此表中不僅記錄了任務(wù),還記錄了流程執(zhí)行過(guò)程的其它活動(dòng),比如開(kāi)始事件、結(jié)束事件。

    掛起流程實(shí)例

/**
  * 掛起指定流程Id的流程實(shí)例 
  */
void suspendProcessInstanceById(String processInstanceId);

激活流程實(shí)例

/**
  * 激活指定流程Id的流程實(shí)例 
  */
void activateProcessInstanceById(String processInstanceId);

刪除流程實(shí)例

/**
 * 刪除實(shí)例 
 * @param processInstanceId 流程實(shí)例Id
 * @param deleteReason 刪除原因
 */
void deleteProcessInstance(String processInstanceId, String deleteReason);

獲取傳入?yún)?shù)

/**
 * 給定執(zhí)行作用域(包括父作用域)中可見(jiàn)的所有變量。
 * @param executionId 啟動(dòng)的實(shí)例的ID
 */
Map<String, Object> getVariables(String executionId);

任務(wù)管理

任務(wù)管理的接口是通過(guò)TaskService來(lái)實(shí)現(xiàn)的。而最新的是使用TaskRuntime接口來(lái)實(shí)現(xiàn)的,當(dāng)然TaskRuntime底層也是通過(guò)TaskService來(lái)實(shí)現(xiàn)的。

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = processEngine.getTaskService();

獲取我的任務(wù)

// 獲取任務(wù)查詢器
TaskQuery taskQuery = taskService.createTaskQuery();
// 查詢器獲取列表
List<Task> tasks = taskQuery.taskAssignee("you ID").list();

拾取任務(wù)

有的時(shí)候,某個(gè)任務(wù)節(jié)點(diǎn)可能沒(méi)有直接指定責(zé)任人,但是指定了候選人列表或者候選組。這個(gè)時(shí)候你還是可以將這個(gè)任務(wù)查詢出來(lái)的,但是需要主動(dòng)拾取任務(wù)。

TaskQuery taskQuery = taskService.createTaskQuery();
Task task = taskQuery.processInstanceBusinessKey("businessKey").singleResult();
// 拾取任務(wù)
taskService.claim(task.getId(),"userId");

解開(kāi)任務(wù)

如果拾取任務(wù)錯(cuò)了或者想將任務(wù)轉(zhuǎn)給別人,可以調(diào)用解開(kāi)任務(wù)的接口,其實(shí)也就是將責(zé)任人置空

taskService.unclaim(String taskId);

完成任務(wù)

/**
  * 在任務(wù)成功執(zhí)行時(shí)調(diào)用,并且所需的任務(wù)參數(shù)由最終用戶給定。
  * @param taskId 任務(wù)ID
  * @param variables 參數(shù)對(duì)象
  */
void complete(String taskId, Map<String, Object> variables);
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容