Slickflow.NET核心开源工作流引擎

Slickflow是一款基于.NET/.NET Core的开源工作流引擎,支持SQLSERVER,ORACLE,MySQL等多种数据库。文章通过简单顺序、并行分支和或分支的代码示例,介绍了如何使用Slickflow进行流程开发和测试,包括流程图创建、启动、撤回、发送回等操作,提供Slickflow设计器和WebTest工具辅助开发。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

介绍 (Introduction)

For developers eager to understand the engine function, after downloading the version, they want to try to write code to complete the development and testing of a process. This article attempts to illustrate how to quickly understand engine code writing from a simple process.

对于渴望了解引擎功能的开发人员,在下载版本后,他们希望尝试编写代码以完成流程的开发和测试。 本文试图说明如何通过一个简单的过程快速理解引擎代码编写。

背景 (Background)

.NET/.NET Core Workflow Engine With Full Source Code

带有完整源代码的.NET / .NET Core工作流引擎

Github: https://github.com/besley/Slickflow

GitHubhttps : //github.com/besley/Slickflow

  1. .NET, .NET CORE version both supported: Slickflow is an open source project based on .NET Framework 4.5, .NET CORE 2; It's easy to use engine product into cross platform application.

    都支持.NET和.NET CORE版本 :Slickflow是一个基于.NET Framework 4.5,.NET CORE 2的开源项目。 易于将引擎产品用于跨平台应用程序。

  2. BPMN graphic style process diagram: Slickflow is using BPMN notation to descript process diagram, the Slickflow designer is HTML5 graph editor and user friendly to business process communication and business analysis.

    BPMN图形样式流程图 :Slickflow使用BPMN表示法来描述流程图,Slickflow设计器是HTML5图形编辑器,对业务流程通信和业务分析用户友好。

  3. High performance with Dapper.NET library: Dapper is a simple object mapper for .NET and own the title of King of Micro ORM in terms of speed and is virtually as fast as using a raw ADO.NET data reader. An ORM is an Object Relational Mapper, which is responsible for mapping between database and programming language. (Reference: https://dapper-tutorial.net/dapper)

    Dapper.NET库具有高性能 :Dapper是.NET的简单对象映射器,就速度而言,它拥有Micro ORM之王的称号,实际上与使用原始ADO.NET数据读取器一样快。 ORM是对象关系映射器,它负责数据库和编程语言之间的映射。 ( 参考https : //dapper-tutorial.net/dapper )

  4. Multiple database supported: Slickflow supports SQLSERVER, ORACLE, MySQL and other database, it implemented by Dapper.NET extension library. The .NET Core version using EF core to support different database products.

    支持多个数据库 :Slickflow支持SQLSERVER,ORACLE,MySQL等数据库,它由Dapper.NET扩展库实现。 使用EF核心支持不同数据库产品的.NET核心版本。

  5. Workflow patterns supported:

    支持的工作流程模式

    Wokflow Pattern

    1. Sequence: The most frequently process pattern

      顺序 :最常用的处理模式

    2. Split/Merge: Support and/or gateway such as and/or split, and/or join, together with condition variables on the transition

      拆分/合并 :支持和/或网关,例如和/或拆分和/或联接 ,以及过渡时的条件变量

    3. Sub-process: In the main process, a sub process node can start a new process life cycle.

      子流程 :在主流程中,子流程节点可以启动新的流程生命周期。

    4. Multi-instance: Multiple performers processing a task together by multiple task instances. All performers both compete their task, then the process can be continued on. There are sequence and parallel pattern, and the percentage or count parameters can be set on it to ensure when you can go to the next step.

      多实例 :多个执行者通过多个任务实例一起处理一个任务。 所有表演者都在竞争他们的任务,然后可以继续进行该过程。 有顺序并行模式,可以在其上设置百分比计数参数,以确保何时可以进行下一步。

      Muliple Instance Pattern

    5. Event interoperation: Process instance and activity instance event delegation service, such as process/activity start, execute and complete. The event is also supported to call external method through the configuration of the node on the property page. such as WebApi, SQL, StoreProcedure and Python script.

      事件互操作 :流程实例和活动实例事件委托服务,例如流程/活动的启动,执行和完成。 还支持通过属性页面上节点的配置来调用外部方法的事件。 例如WebApi,SQL,StoreProcedure和Python脚本。

    6. Timer: Integrated with HangFire library, and with CRON expression supported

      计时器 :与HangFire库集成,并支持CRON表达式

    7. Email: To do or overdue tasks email notification

      电子邮件 :待办事项或过期任务的电子邮件通知

    8. Withdraw: Withdraw the task after just being sent out to next step users.

      提现 :仅在发送给下一步用户后才提现任务。

    9. Sendback: Send back to previous step user, because there are some exceptions.

      Sendback :发送回上一步用户,因为有一些例外。

    10. Resend: Combined after sendback and re-send the task to original sendback users.

      重新发送 :在回发后合并,然后将任务重新发送给原始回发用户。

    11. Reverse: Reverse the process instance alive when completed doing them.

      反向 :完成该过程后,还可以对流程实例进行实时反向。

    12. Jump: Jump the process over by several steps forward or backward.

      跳转 :向前或向后跳过几个步骤。

  6. Process Version: The process has version property to upgrade a new definition due to business process changed.

    流程版本 :由于业务流程已更改,流程具有版本属性以升级新定义。

  7. XML Cache: The runtime instance uses cache to keep XML process diagram by an expired duration.

    XML缓存 :运行时实例使用缓存来使XML流程图保持过期时间。

使用代码 (Using the Code)

I:简单顺序过程编码示例 (I: Simple Sequential Process Coding Example)

Foreword: For developers eager to understand the engine function, after downloading the version, they want to try to write code to complete the development and testing of a process.This article attempts to illustrate how to quickly understand engine code writing from a simple process.

前言 :对于渴望了解引擎功能的开发人员,下载版本后,他们想要尝试编写代码以完成流程的开发和测试。本文试图说明如何从一个简单的流程快速理解引擎代码编写。

Version: .NET Core 2.1

版本:.NET Core 2.1

1.创建流程图 (1. Create Flow Chart)

Sequence is used to create sequential processes, and nodes are generated sequentially.

序列用于创建顺序过程,节点是顺序生成的。

var pmb = ProcessModelBuilder.CreateProcess("BookSeller Process", "BookSeller Process Code", "1");
var process = pmb.Start("Start")
      .Task("Package Books", "PB001")
      .Task("Deliver Books", "DR001")
      .End("End")
      .Store();

The above code creates a simple serial process with four nodes, the start node, two task nodes and the Store() method is used for database storage process. Examples of flow charts are as follows:

上面的代码创建了一个简单的串行过程,该过程具有四个节点,即起始节点,两个任务节点,并且Store()方法用于数据库存储过程。 流程图示例如下:

Sequence Flow Chart
2.流程启动和操作 (2. Process Startup and Operation)

Starting and running processes are the two most commonly used API interfaces.

启动和运行过程是两个最常用的API接口。

2.1流程启动 (2.1 Process Startup)

Starting needs to deal with the creation of process instances, as well as the creation of task nodes after the start node. The sample code is as follows:

启动需要处理流程实例的创建,以及启动节点之后的任务节点的创建。 示例代码如下:

IWorkflowService wfService = new WorkflowService( );
var wfResult = wfService.CreateRunner("10", "jack")
             .UseApp("BS-100", "Delivery-Books", "BS-100-LX")
             .UseProcess("BookSellerProcessCode ")
             .Start( );

Process Record List:

流程记录清单:

Sequence Activity Instance
2.2流程运行 (2.2 Process Running)

Process running is a process that starts with the current to-do task and runs to the next step. When parsing a process, there may be multiple processing steps in the next step, requiring the user to explicitly select a list of steps (usually specified by the front-end user when the next step list pops up). Here, an example can be used to run as a simple one-step instance.

流程运行是从当前待办任务开始并运行到下一步的流程。 解析过程时,下一步可能会有多个处理步骤,要求用户明确选择步骤列表(通常在弹出下一步骤列表时由前端用户指定)。 在这里,可以使用一个示例作为一个简单的单步实例运行。

IWorkflowService wfService = new WorkflowService( );
var wfResult = wfService.CreateRunner("10", "jack")
        .UseApp("BS-100", "Delivery-Books", "BS-100-LX")
        .UseProcess("BookSellerProcessCode ")
        .OnTask(8027) // TaskID
        .NextStepInt("20", "Alice")
        .Run( );

Process Record List:

流程记录清单:

Sequence Activity Instance After Running
3.处理撤回和发送回 (3. Process Withdraw and SendBack)
3.1流程撤回 (3.1 Process Withdraw)

If the user finds an error message when he completes his to-do task and sends it to the next dealer, he can initiate the revocation by himself and revoke the current process back. Revoking internal processing logic is equivalent to returning processing, except that the sponsors are in different positions. The code example is as follows:

如果用户在完成待办任务并将其发送给下一个经销商时发现错误消息,则他可以自己发起吊销并撤回当前过程。 撤消内部处理逻辑等效于返回处理,除了发起人位于不同的位置。 代码示例如下:

IWorkflowService wfService = new WorkflowService( );
var wfResult = wfService.CreateRunner("20", "Alice")
           .UseApp("BS-100", "Delivery-Books", "BS-100-LX")
           .UseProcess("BookSellerProcessCode ")
           .OnTask(8027) // TaskID
           .Withdraw( );

Process Record List:

流程记录清单:

Sequence Activity Instance After Withdraw
3.2处理SendBack (3.2 Process SendBack)

The process return is initiated by the current to-do task handler and returns to the previous step of the process. When parsing a process, the previous step may have multiple processing steps, requiring the user to explicitly select a list of steps (usually specified by the front-end user when the previous step list pops up), and the PrevStepInt () method is used to simplify processing for only one of the processing steps that are returned.

流程返回由当前的待办任务处理程序启动,并返回到流程的上一步。 解析流程时,上一步可能包含多个处理步骤,要求用户显式选择一个步骤列表(通常在弹出上一个步骤列表时由前端用户指定),并使用PrevStepInt ()方法以简化仅返回的处理步骤之一的处理。

IWorkflowService wfService = new WorkflowService( );
var wfResult = wfService.CreateRunner("20", "Alice")
            .UseApp("BS-100", "Delivery-Books", "BS-100-LX")
            .UseProcess("BookSellerProcessCode ")
            .PrevStepInt()
            .OnTask(8030) // TaskID
            .SendBack( );

Process Record List:

流程记录清单:

Sequence Activity Instance After SendBack

II:简单的并行分支处理代码编写示例 (II: Simple Parallel Branch Process Code Writing Example)

Foreword: For developers eager to understand the engine function, after downloading the version, they want to try to write code to complete the development and testing of a process. This article attempts to illustrate how to quickly understand engine code writing from a simplest parallel branching process.

前言 :对于渴望了解引擎功能的开发人员,下载版本后,他们希望尝试编写代码以完成流程的开发和测试。 本文试图说明如何通过最简单的并行分支过程快速理解引擎代码编写。

Version: .NET Core 2.1

版本:.NET Core 2.1

1.创建分支流程图 (1. Create Branch Flow Chart)

Branch process is a common decision-making process. In this paper, a car order process is taken as an example to demonstrate the process of parallel branch process. Parallel branches are usually used in scenarios where multiple departments can perform tasks at the same time, and each individual branch can also be considered as a serial sequence fragment. Finally, multiple branches are merged through the sink node.

分支过程是一个常见的决策过程。 本文以汽车订购过程为例,说明并行分支过程。 并行分支通常用于多个部门可以同时执行任务的场景,每个单独的分支也可以视为串行序列片段。 最后,多个分支通过接收器节点合并。

var pmb = ProcessModelBuilder.CreateProcess(" LargeOrderProcess ", " LargeOrderProcessCode ");
var process = pmb.Start("Start")
           .Task("Large Order Received", "LOR001")
           .AndSplit("AndSplit")
           .Parallels(
               () => pmb.Branch (
                    () => pmb.Task("Engineering Review", "ER001")
                  )
                  , ( ) => pmb.Branch (
                    () => pmb.Task("Design Review", "DR001")
                  )
                )
           .AndJoin("AndJoin")
           .Task("Management Approve", "MA001")
           .End("End")
           .Store(); 

The above code creates a parallel branching process with two branches, and the branch is AndSplit-AndJoin. The attribute types of the nodes in the graph are set by direct assignment of the code. Examples of flow charts are as follows:

上面的代码创建了一个带有两个分支的并行分支过程,该分支是AndSplit - AndJoin 。 通过直接分配代码来设置图中节点的属性类型。 流程图示例如下:

parallel flowchart
2.流程启动和操作 (2. Process Startup and Operation)

Starting and running processes are the two most commonly used API interfaces.

启动和运行过程是两个最常用的API接口。

2.1。 流程启动 (2.1. Process Startup)

Starting needs to deal with the creation of process instances, as well as the creation of task nodes after the start node and the start node. The sample code is as follows:

启动需要处理流程实例的创建,以及启动节点和启动节点之后的任务节点的创建。 示例代码如下:

IWorkflowService wfService = new WorkflowService( );
var wfResult = wfService.CreateRunner("10", "jack")
           .UseApp("PS-100", "Large-Car-Order", "PS-100-LX")
           .UseProcess("LargeOrderProcessCode")
           .Start( );

The activity instance record table is as follows:

活动实例记录表如下:

Parallel Activity Instance After Startup
2.2。 流程运行 (2.2. Process Running)

Process running is a process that starts with the current to-do task and runs to the next step. Parallel branches indicate that multiple branches are triggered simultaneously, as illustrated in the flow chart above: Design Review and Engineering Review are triggered simultaneously by parallel gateway nodes (AndSplit), so two new activity records are generated at one time in the activity instance table.

流程运行是从当前待办任务开始并运行到下一步的流程。 并行分支表示已同时触发多个分支,如上面的流程图所示:并行网关节点( AndSplit )同时触发了Design Review和Engineering Review,因此在活动实例表中一次生成了两个新的活动记录。

IWorkflowService wfService = new WorkflowService( );
var wfResult = wfService.CreateRunner("10", "jack")
         .UseApp("PS-100", "Large-Car-Order", "PS-100-LX")
         .UseProcess("LargeOrderProcessCode ")
         .OnTask( 8033)
         .NextStepInt("20", "Alice")
         .Run( );

The activity instance record table is as follows:

活动实例记录表如下:

Parallel Activity Instance After Running
3.提现并发回 (3. Withdraw and SendBack)
3.1流程撤回 (3.1 Process Withdraw)

If the user finds an error message when he completes his to-do task and sends it to the next dealer, he can initiate the revocation by himself and revoke the current process back. When the parallel branch is withdrawn, the two parallel branches are also set back and revoked at the same time, indicating that the branch is two activities of strong correlation type.

如果用户在完成待办任务并将其发送给下一个经销商时发现错误消息,则他可以自己发起吊销并撤回当前过程。 撤消并行分支时,两个并行分支也同时撤回并撤销,表明该分支是两个具有强相关性的活动。

IWorkflowService wfService = new WorkflowService( );
var wfResult = wfService.CreateRunner("10", "Jack")
         .UseApp("PS-100", "Large-Car-Order", "PS-100-LX")
         .UseProcess("LargeOrderProcessCode ")
         .OnTask(8033) // TaskID
         .Withdraw( );

   The activity instance record table is as follows:

活动实例记录表如下:

Parallel Activity Instance After Withdraw
3.2处理SendBack (3.2 Process SendBack)

The process return is initiated by the current to-do task handler and returns to the previous step of the process. If one branch of a parallel branch is returned, then the default is to return only the task node before the current branch to the gateway, without affecting the other branch.

流程返回由当前的待办任务处理程序启动,并返回到流程的上一步。 如果返回并行分支的一个分支,则默认值为仅将当前分支之前的任务节点返回到网关,而不会影响另一个分支。

IWorkflowService wfService = new WorkflowService( );
var wfResult = wfService.CreateRunner("20", "Alice")
          .UseApp("PS-100", "Large-Car-Order", "PS-100-LX")
          .UseProcess("LargeOrderProcessCode ")
          .PrevStepInt()
          .OnTask( 8038) // TaskID
          .SendBack( );

The activity instance record table is as follows:

活动实例记录表如下:

Parallel Activity Instance After SendBack

Revocation and fallback of parallel branches are special in the engine because of the processing of adjacent branches. Here, only the result records after the return are described, and the internal processing logic of the return will be arranged for another article to be introduced specifically in the future.

由于处理相邻分支,因此并行分支的撤消和回退在引擎中很特殊。 这里,仅描述退货后的结果记录,并且将安排退货的内部处理逻辑以供将来要专门介绍的另一篇文章。

III:简单或分支过程编码示例 (III: Simple or Branched Process Coding Example)

Foreword: For developers eager to understand the engine function, after downloading the version, they want to try to write code to complete the development and testing of a process.This paper attempts to quickly understand the engine code writing from the leave process or branch mode.

前言 :对于渴望了解引擎功能的开发人员,下载版本后,他们想尝试编写代码以完成流程的开发和测试。本文试图从离开流程或分支模式快速了解引擎代码的编写。 。

Version: .NET CORE 2.1

版本:.NET CORE 2.1

1.创建或分支流程图 (1. Create or Branch Flow Charts)

Or branching process is a common decision-making process, which is used to deal with business processing in different decision scenarios, in which each branch can be regarded as a fragment of a serial process. The following is the creation code of the leave process:

Or分支过程是一个常见的决策过程,用于处理不同决策场景中的业务处理,其中每个分支都可以视为串行过程的一部分。 以下是leave流程的创建代码:

var pmb = ProcessModelBuilder.CreateProcess(" LeaveRequest ", " LeaveRequestCode ");
var process = pmb.Start("Start")
         .Task("Fill Leave Days", "FLD001")
         .OrSplit("OrSplit")
         .Parallels(
                  () => pmb.Branch(
                     () => pmb.Task(
                              VertexBuilder.CreateTask("CEO Evaluate", "CEOE001"),
                              LinkBuilder.CreateTransition("days>=3")
                                 .AddCondition( ConditionTypeEnum.Expression , "Days>=3")
                              )
                      )
                 , () => pmb.Branch(
                     () => pmb.Task(
                              VertexBuilder.CreateTask("Manager Evaluate", "ME001"),
                              LinkBuilder.CreateTransition("days<3")
                                 .AddCondition(ConditionTypeEnum.Expression, "Days<=3")
                              )
                      )
                  )
          .OrJoin("OrJoin")
          .Task("the Notify HR", "HRN001")
          .End("End")
          .Store( );

The above code creates an OrSplit-OrJoin process with two branches representing the leave process in the actual business process. An example of the completed flow chart is as follows:

上面的代码创建一个OrSplit - OrJoin流程,其中两个分支代表实际业务流程中的请假流程。 完整流程图的示例如下:

Decision Flowchart

Or the branch is a transition of two conditional expressions. In the case of a branch gateway node, the process will decide which branch to take according to the value of the incoming conditional variable days. This process can be regarded as the branch selection of the number of days off in the leave process. For example, if the number of days off is less than 3 days, the Department Manager will approve it. If the number of days off exceeds (including) 3 days, the general manager will be required to approve it. Approval decision-making can be realized by means of branch mode or branch mode.

或者分支是两个条件表达式的过渡。 对于分支网关节点,该过程将根据传入的条件变量days的值来决定采用哪个分支。 此过程可以视为休假过程中休假天数的分支选择。 例如,如果请假天数少于3天,则部门经理将批准它。 如果请假天数超过(包括)3天,则总经理将要求批准。 批准决策可以通过分支模式或分支模式来实现。

2.流程启动和操作 (2. Process Startup and Operation)

Starting and running processes are the two most commonly used API interfaces.

启动和运行过程是两个最常用的API接口。

2.1。 流程启动 (2.1. Process Startup)

Starting needs to deal with the creation of process instances, as well as the creation of task nodes after the start node and the start node. The sample code is as follows:

启动需要处理流程实例的创建,以及启动节点和启动节点之后的任务节点的创建。 示例代码如下:

IWorkflowService wfService = new WorkflowService( );
var wfResult = wfService.CreateRunner("10", "jack")
          .UseApp("DS-100", "Leave-Request", "DS-100-LX")
          .UseProcess("LeaveRequestCode ")
          .Start( );

The activity instance record table is as follows:

活动实例记录表如下:

Decision Activity Instance After Startup

Similarly, according to the leave process as an example, the first task node can be regarded as "submitting leave form", and when the employee completes the leave form, it can be regarded as starting the process.

同样,以leave流程为例,可以将第一个任务节点视为“提交请假表格”,当员工填写请假表格时,可以将其视为启动流程。

2.2流程运行 (2.2 Process Running)

Process running is a process that starts with the current to-do task and runs to the next step. Because it's a branch gateway or a branch gateway, it's necessary to specify the name and value of the condition variable to determine the next branch path. Here, the days of leave as a conditional variable need to be passed in. When the days of leave are 3 days, the gateway decides to the CEO approval node, so only one of the branches will be selected for routing. The code example is as follows:

流程运行是从当前待办任务开始并运行到下一步的流程。 因为它是分支网关还是分支网关,所以必须指定条件变量的名称和值来确定下一个分支路径。 在此,需要传递作为条件变量的休假天数。当休假天数为3天时,网关将决定执行长批准节点,因此将仅选择一个分支进行路由。 代码示例如下:

IWorkflowService wfService = new WorkflowService( );
var wfResult = wfService.CreateRunner("10", "jack")
      .UseApp("DS-100", "Leave-Request", "DS-100-LX")
      .UseProcess("LeaveRequestCode ")
      .OnTask( 8017)
      .IfCondition("Days", "3")
      .NextStepInt("20", "Alice")
      .Run( );

The activity instance record table is as follows:

活动实例记录表如下:

Decison Activity Instance After Running
3.撤回和发送回流程 (3. Withdraw and SendBack of Process)
3.1。 流程提现 (3.1. Process Withdraw)

If the user finds an error message when he completes his to-do task and sends it to the next dealer, he can initiate the revocation by himself and revoke the current process back.

如果用户在完成待办任务并将其发送给下一个经销商时发现错误消息,则他可以自己发起吊销并撤回当前流程。

IWorkflowService wfService = new WorkflowService( );
var wfResult = wfService.CreateRunner("10", "Jack")
         .UseApp("DS-100", "Leave-Request", "DS-100-LX")
         .UseProcess("LeaveRequestCode ")
         .OnTask(8017) // TaskID
         .Withdraw ( );

   The activity instance record table is as follows:

活动实例记录表如下:

Decision Activity Instance After Withdraw
3.2处理SendBack (3.2 Process SendBack)

The process return is initiated by the current to-do task handler and returns to the previous step of the process. If one branch of a parallel branch is returned, then the default is to return only the task node before the current branch to the gateway, without affecting the other branch.

流程返回由当前的待办任务处理程序启动,并返回到流程的上一步。 如果返回并行分支的一个分支,则默认值为仅将当前分支之前的任务节点返回到网关,而不会影响另一个分支。

IWorkflowService wfService = new WorkflowService( );
var wfResult = wfService.CreateRunner("20", "Alice")
      .UseApp("DS-100", "Leave-Request", "DS-100-LX")
      .UseProcess("LeaveRequestCode ")
      .PrevStepInt()
      .OnTask(8020) // TaskID
      .SendBack( );

The activity instance record table is as follows:

活动实例记录表如下:

Decision Activity Instance After SendBack

开发工具 (Development Tools)

1)Slickflow流程设计器 (1) Slickflow Process Designer)

2)Slickflow WebTest工具 (2) Slickflow WebTest Tools)

GitHub项目 (GitHub Project)

摘要 (Summary)

  1. The above code can help developers quickly familiarize themselves with the interface of engine components and the practical functions of simple or branch. The complete functions need to be acquired in the enterprise version or above.

    上面的代码可以帮助开发人员快速熟悉引擎组件的界面以及simple或branch的实用功能。 需要在企业版或更高版本中获取完整功能。
  2. Process Designer and Web Testing Tool in the Assistant Development Tool, which can give users intuitive experience operation, but also suitable for different types of business users;

    助手开发工具中的Process Designer和Web测试工具,可以为用户提供直观的体验操作,也适合不同类型的业务用户;
  3. Code creation process and test process are convenient for developers to learn and start quickly, which is an efficient way to improve work efficiency.

    代码创建过程和测试过程方便开发人员学习和快速入门,这是提高工作效率的有效途径。

历史 (History)

  • 21st September, 2019: Initial version

    2019年9月21 :初始版本

翻译自: https://www.codeproject.com/Articles/5246528/Slickflow-NET-Core-Open-Source-Workflow-Engine

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值