编程知识 cdmana.com

Best practices of serverless in SaaS

With the Internet, the demographic dividend is weakening , Traffic based growth has slowed down , The Internet industry urgently needs to find a new blue ocean that can carry its own sustainable growth , Industrial Internet is the new trend under this grand background . We see the Internet wave sweeping traditional industries , Cloud computing 、 big data 、 AI began to integrate into finance on a large scale 、 manufacture 、 logistics 、 retail 、 recreational 、 education 、 In the production link of medical and other industries , This convergence is called the industrial Internet . And in the industrial Internet , One area that cannot be underestimated is SaaS field , It is ToB The backbone of the track , such as CRM、HRM、 Cost control system 、 Financial system 、 Collaborative office and so on .

SaaS System challenges

In the age of consumer Internet , Everybody is Search for What you want , Various manufacturers in Cloud Computing 、 big data 、 Based on artificial intelligence and other technologies, the service and ecology with maximum flow are established , Based on massive content distribution and traffic sharing, the system is built logically . And in the era of industrial Internet , Supply relations have changed , Everybody is customized What you want , Two way construction should be carried out from both sides of supply and demand , At this time, the flexibility and scalability of the system are facing unprecedented challenges , In especial ToB Of SaaS field .

Especially for the current economic environment ,SaaS Manufacturers should understand , You can't burn money anymore , Just focus on your own number of users , And more to think about how to help customers reduce costs 、 Increase efficiency , So we need to put more energy on the customization ability of our products .

How to deal with challenges

SaaS The best in the field Salesforce, take CRM The concept of Marketing、Sales、Service, Of these three areas, only Sales Specialized SaaS product , The other two areas are different ISV Industry solutions in different industries , By what ? Beyond all doubt , yes Salesforce Powerful aPaaS platform .ISV、 Internal implementation 、 Customers can pass the aPaaS Platform to build their own industry 、 In your field SaaS System , Establish a complete ecology . So in my opinion , current Salesforce Has been established by a SaaS The company is sublimated into one aPaaS Platform company . This evolution process also confirms the transformation logic of consumer Internet and industrial Internet, as well as the core demands of the latter .

But not all SaaS Companies have the money and time to incubate and polish their own aPaaS platform , But changes in the market 、 The demands of users are real . To survive , We need to change . The core of this change is to be able to make your current SaaS The system became flexible , Relatively difficult to build aPaaS platform , We can actually choose lightweight and effective ones Serverless Solutions to enhance the flexibility and scalability of existing systems , In order to achieve different customization needs of users .

Serverless workflow

In the last article 《 Resource cost double optimization ! see Serverless Innovative practice of subverting Programming Education 》 in , Have been to Serverless I have explained the concept of , And also introduced Serverless Function calculation (FC) The concept and practice of . This article introduces the core elements of building system flexibility service choreography ——Serverless workflow .

Serverless Workflow is a fully managed cloud service used to coordinate the execution of multiple distributed tasks . stay Serverless In workflow , You can use order 、 Branch 、 To arrange distributed tasks in parallel ,Serverless The workflow reliably coordinates task execution according to the set steps , Track state transitions for each task , And execute your defined retry logic if necessary , To ensure that the workflow is completed .Serverless Workflow monitors workflow execution by providing logging and auditing , You can easily diagnose and debug applications .

The picture below describes Serverless How workflow coordinates distributed tasks , These tasks can be functions 、 Integrated cloud services API、 A program running on a virtual machine or container .

After watching Serverless Introduction of Workflow , You may have some ideas . The core of system flexibility and scalability is service choreography , No matter it was before BPM Or now aPaaS. So based on the Serverless Workflow refactoring SaaS The core idea of system flexibility scheme , It is to sort out the functions that users most want to customize in the system 、 Split 、 Pull away , Coordination function calculation (FC) Provide stateless capabilities , adopt Serverless The workflow arranges these function points , So as to achieve different business processes .

Calculate by function FC and Serverless Flexible ordering module based on Workflow

I believe we are not unfamiliar with the scene of ordering , Order takeout at home or order at a restaurant , It's all about this scene . At present, there are also many systems providing ordering SaaS Service provider , There are many good ones SaaS Ordering system . With the transformation of consumer Internet to industrial Internet , these SaaS The ordering system is facing more and more customized demands , One of the requirements is that different businesses will show different payment methods when paying , For instance from A Alipay will be shown after payment. 、 Wechat payment 、 UnionPay payment , from B Alipay will be shown after payment. 、 Jingdong payment . All of a sudden, meituan came out again and paid by meituan , here B Merchant receives meituan payment , So from B Alipay will be shown after payment. 、 Jingdong payment 、 Meituan payment . There is a growing demand for customization , these SaaS Product if not PaaS platform , Then you will be tired of constantly adding condition judgment through hard code to meet the needs of different businesses , This is obviously not a sustainable development model .

So let's take a look at functional computation FC and Serverless How can workflow solve this problem gracefully . Let's take a look at the ordering process :

adopt Serverless Workflow creation process

First of all, I need to transform the above user side process into the program side process , You need to use Serverless Workflow to take on this task .

open Serverless Console , Create ordering process , here Serverless Workflow uses process definition language FDL Create workflow , How to use FDL Please refer to the document for creating workflow . The flow chart is shown in the figure below :

FDL The code is :

version: v1beta1
type: flow
timeoutSeconds: 3600
steps:
  - type: task
    name: generateInfo
    timeoutSeconds: 300
    resourceArn: acs:mns:::/topics/generateInfo-fnf-demo-jiyuan/messages
    pattern: waitForCallback
    inputMappings:
      - target: taskToken
        source: $context.task.token
      - target: products
        source: $input.products
      - target: supplier
        source: $input.supplier
      - target: address
        source: $input.address
      - target: orderNum
        source: $input.orderNum
      - target: type
        source: $context.step.name 
    outputMappings:
      - target: paymentcombination
        source: $local.paymentcombination
      - target: orderNum
        source: $local.orderNum
    serviceParams:
      MessageBody: $
      Priority: 1
    catch:
      - errors:
          - FnF.TaskTimeout
        goto: orderCanceled
  - type: task
    name: payment
    timeoutSeconds: 300
    resourceArn: acs:mns:::/topics/payment-fnf-demo-jiyuan/messages
    pattern: waitForCallback
    inputMappings:
      - target: taskToken
        source: $context.task.token
      - target: orderNum
        source: $local.orderNum
      - target: paymentcombination
        source: $local.paymentcombination
      - target: type
        source: $context.step.name 
    outputMappings:
      - target: paymentMethod
        source: $local.paymentMethod
      - target: orderNum
        source: $local.orderNum
      - target: price
        source: $local.price
      - target: taskToken
        source: $input.taskToken
    serviceParams:
      MessageBody: $
      Priority: 1
    catch:
      - errors:
          - FnF.TaskTimeout
        goto: orderCanceled
  - type: choice
    name: paymentCombination
    inputMappings:
      - target: orderNum
        source: $local.orderNum
      - target: paymentMethod
        source: $local.paymentMethod
      - target: price
        source: $local.price
      - target: taskToken
        source: $local.taskToken
    choices:
      - condition: $.paymentMethod == "zhifubao"
        steps:
          - type: task
            name: zhifubao
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan/functions/zhifubao-fnf-demo
            inputMappings:
              - target: price
                source: $input.price             
              - target: orderNum
                source: $input.orderNum 
              - target: paymentMethod
                source: $input.paymentMethod
              - target: taskToken
                source: $input.taskToken
      - condition: $.paymentMethod == "weixin"
        steps:
          - type: task
            name: weixin
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/weixin-fnf-demo
            inputMappings:
            - target: price
              source: $input.price             
            - target: orderNum
              source: $input.orderNum
            - target: paymentMethod
              source: $input.paymentMethod
            - target: taskToken
              source: $input.taskToken
      - condition: $.paymentMethod == "unionpay"
        steps:
          - type: task
            name: unionpay
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/union-fnf-demo
            inputMappings:
            - target: price
              source: $input.price             
            - target: orderNum
              source: $input.orderNum 
            - target: paymentMethod
              source: $input.paymentMethod
            - target: taskToken
              source: $input.taskToken
    default:
      goto: orderCanceled
  - type: task
    name: orderCompleted
    resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/orderCompleted
    end: true
  - type: task
    name: orderCanceled
    resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/cancerOrder

Before parsing the entire process , The first thing I want to make clear is that , We didn't pass it completely Serverless Function calculation and Serverless Workflow to build the ordering module , Just use it to solve the problem of flexibility , So the main application of this example is Java Compiling , And then combined Serverless Function calculation and Serverless workflow . Now let's analyze this process in detail .

Start process

As a rule , When you start ordering, the process should start , So in this example , My design is when we've finished choosing products and businesses 、 Start the process after filling in the address :

Here we go through Serverless Workflow provides OpenAPI To start the process .

Java Start process

I use this example Serverless Workflow Java SDK, First, in the POM Add dependencies to the file :

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-core</artifactId>
    <version>[4.3.2,5.0.0)</version>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-fnf</artifactId>
    <version>[1.0.0,5.0.0)</version>
</dependency>

Then create the initialization Java SDK Of Config class :

@Configuration
public class FNFConfig {

    @Bean
    public IAcsClient createDefaultAcsClient(){
        DefaultProfile profile = DefaultProfile.getProfile(
                "cn-xxx",          //  regional ID
                "ak",      // RAM  Account number AccessKey ID
                "sk"); // RAM  account number Access Key Secret
        IAcsClient client = new DefaultAcsClient(profile);
        return client;
    }

}

Look again. Controller Medium startFNF Method , This method is exposed GET The interface of the way , Pass in three parameters :

  1. fnfname: The name of the process to start .
  2. execuname: The name of the process instance after the process is started .
  3. input: Start input parameters , For example, business parameters .
@GetMapping("/startFNF/{fnfname}/{execuname}/{input}")
    public StartExecutionResponse startFNF(@PathVariable("fnfname") String fnfName,
                                           @PathVariable("execuname") String execuName,
                                           @PathVariable("input") String inputStr) throws ClientException {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("fnfname", fnfName);
        jsonObject.put("execuname", execuName);
        jsonObject.put("input", inputStr);
        return fnfService.startFNF(jsonObject);
    }

Look again. Service Medium startFNF Method , The method is divided into two parts , The first part is to start the process , The second part is to create the order object , And simulate the storage ( In the example, the Map In the ):

@Override
    public StartExecutionResponse startFNF(JSONObject jsonObject) throws ClientException {
        StartExecutionRequest request = new StartExecutionRequest();
        String orderNum = jsonObject.getString("execuname");
        request.setFlowName(jsonObject.getString("fnfname"));
        request.setExecutionName(orderNum);
        request.setInput(jsonObject.getString("input"));

        JSONObject inputObj = jsonObject.getJSONObject("input");
        Order order = new Order();
        order.setOrderNum(orderNum);
        order.setAddress(inputObj.getString("address"));
        order.setProducts(inputObj.getString("products"));
        order.setSupplier(inputObj.getString("supplier"));
        orderMap.put(orderNum, order);

        return iAcsClient.getAcsResponse(request);
    }

When starting the process , The process name and the name of the start process instance are the parameters that need to be passed in , Here I use the order number of each time as the instance name of the start process . as for Input, It can be constructed according to requirements JSON String input . Here I will merchandise 、 merchants 、 Address 、 The order number is constructed JSON The string is passed into the process when the process starts .

in addition , Created the Order example , And exist Map in , Simulated stock in , In subsequent phases, the order instance will be queried to update the order properties .

VUE Choose products / Merchant page

Front end I use VUE build , When you click the next step on the select products and merchants page , adopt GET Way to call HTTP Interface to protocol /startFNF/{fnfname}/{execuname}/{input}. And the above Java Methods corresponding to the .

  1. fnfname: The name of the process to start .
  2. execuname: Random generation uuid, As the number of the order , Also as the name of the initiating process instance .
  3. input: Put the goods 、 merchants 、 The order number 、 Address constructed as JSON String transfer process .
submitOrder(){
                const orderNum = uuid.v1()
                this.$axios.$get('/startFNF/OrderDemo-Jiyuan/'+orderNum+'/{\n' +
                    '  "products": "'+this.products+'",\n' +
                    '  "supplier": "'+this.supplier+'",\n' +
                    '  "orderNum": "'+orderNum+'",\n' +
                    '  "address": "'+this.address+'"\n' +
                    '}' ).then((response) => {
                    console.log(response)
                    if(response.message == "success"){
                        this.$router.push('/orderdemo/' + orderNum)
                    }
                })
            }

generateInfo node

First node generateInfo, First look at it. FDL The meaning of :

- type: task
    name: generateInfo
    timeoutSeconds: 300
    resourceArn: acs:mns:::/topics/generateInfo-fnf-demo-jiyuan/messages
    pattern: waitForCallback
    inputMappings:
      - target: taskToken
        source: $context.task.token
      - target: products
        source: $input.products
      - target: supplier
        source: $input.supplier
      - target: address
        source: $input.address
      - target: orderNum
        source: $input.orderNum
      - target: type
        source: $context.step.name 
    outputMappings:
      - target: paymentcombination
        source: $local.paymentcombination
      - target: orderNum
        source: $local.orderNum
    serviceParams:
      MessageBody: $
      Priority: 1
    catch:
      - errors:
          - FnF.TaskTimeout
        goto: orderCanceled
  1. name: The name of the node .
  2. timeoutSeconds: Timeout time . The length of time that the node waits , It will jump to the goto Branch directed orderCanceled node .
  3. pattern: Set to waitForCallback, Indicates that you need to wait for confirmation .inputMappings: This node is a parameter .
  • taskToken:Serverless Workflow automatically generated Token.
  • products: Selected products .
  • supplier: Selected businesses .
  • address: Delivery address .
  • orderNum: The order number .
  1. outputMappings: The output parameter of the node .
  • paymentcombination: Payment method supported by the merchant .
  • orderNum: The order number .
  1. catch: Capture exception , Jump to other branches .

here resourceArn and serviceParams It needs to be explained separately .Serverless Workflow supports integration with multiple cloud services , Other services are used as execution units of task steps . Service integration mode is provided by FDL Language expression , In the task step , have access to resourceArn To define the target services for integration , Use pattern Defining integration patterns . So you can see in resourceArn Middle configuration acs:mns:::/topics/generateInfo-fnf-demo-jiyuan/messages Information , That is to say generateInfo Integrated in the node MNS Message Queuing service , When generateInfo When the node is triggered, the generateInfo-fnf-demo-jiyuanTopic Send a message in . The message body and parameters are displayed in the serviceParams Object .MessageBody Is the message body , To configure $ Represents mapping through input inputMappings Generate message body .

Look at the example of the first node , You can see , stay Serverless In workflow , Information transfer between nodes can be realized through integration MNS Send a message to deliver , It is also one of the more widely used ways .

generateInfo-fnf-demo function

towards generateInfo-fnf-demo-jiyuanTopic The message sent in contains product information 、 Merchant information 、 Address 、 The order number , Indicates the beginning of an order placing process , Since there is a message , Then, there must be a receiving message for subsequent processing . So open the function calculation console , Create services , Create a service named generateInfo-fnf-demo Event trigger function for , Choose here Python Runtime:

establish MNS trigger , Choose monitor generateInfo-fnf-demo-jiyuanTopic.

Open message service MNS Console , establish generateInfo-fnf-demo-jiyuanTopic:

Prepare functions well , Let's start writing code :

# -*- coding: utf-8 -*-
import logging
import json
import time
import requests
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.acs_exception.exceptions import ServerException
from aliyunsdkfnf.request.v20190315 import ReportTaskSucceededRequest
from aliyunsdkfnf.request.v20190315 import ReportTaskFailedRequest


def handler(event, context):
    # 1.  structure Serverless workflow Client
    region = "cn-hangzhou"
    account_id = "XXXX"
    ak_id = "XXX"
    ak_secret = "XXX"
    fnf_client = AcsClient(
        ak_id,
        ak_secret,
        region
    )
    logger = logging.getLogger()
    # 2. event The information in the database is received Topic generateInfo-fnf-demo-jiyuan Message content in , Convert it to Json object 
    bodyJson = json.loads(event)
    logger.info("products:" + bodyJson["products"])
    logger.info("supplier:" + bodyJson["supplier"])
    logger.info("address:" + bodyJson["address"])
    logger.info("taskToken:" + bodyJson["taskToken"])
    supplier = bodyJson["supplier"]
    taskToken = bodyJson["taskToken"]
    orderNum = bodyJson["orderNum"]
    # 3.  Determine which businesses use what payment combination , The example here is simple , Under normal circumstances , It should be obtained by metadata configuration 
    paymentcombination = ""
    if supplier == "haidilao":
        paymentcombination = "zhifubao,weixin"
    else:
        paymentcombination = "zhifubao,weixin,unionpay"

    # 4.  call Java Service exposed interface , Update order information , Mainly to update the payment method 
    url = "http://xx.xx.xx.xx:8080/setPaymentCombination/" + orderNum + "/" + paymentcombination + "/0"
    x = requests.get(url)

    # 5.  To give generateInfo Node response , And return the data , The order number and payment method are returned here 
    output = "{\"orderNum\": \"%s\", \"paymentcombination\":\"%s\" " \
                         "}" % (orderNum, paymentcombination)
    request = ReportTaskSucceededRequest.ReportTaskSucceededRequest()
    request.set_Output(output)
    request.set_TaskToken(taskToken)
    resp = fnf_client.do_action_with_exception(request)
    return 'hello world'

because generateInfo-fnf-demo Function is configured MNS trigger , So when TopicgenerateInfo-fnf-demo-jiyuan Execution is triggered when there is a message generateInfo-fnf-demo function .

The whole code is divided into five parts :

  1. structure Serverless workflow Client.
  2. event The information in the database is received TopicgenerateInfo-fnf-demo-jiyuan Message content in , Convert it to Json object .
  3. Determine which businesses use what payment combination , The example here is simple , Under normal circumstances , It should be obtained by metadata configuration . For example, there is the configuration function of business information in the system , By configuring which payment methods the merchant supports on the interface , Form metadata configuration information , Provide query interface , Query here .
  4. call Java Service exposed interface , Update order information , Mainly to update the payment method .
  5. To give generateInfo Node response , And return the data , The order number and payment method are returned here . Because of the pattern yes waitForCallback, So you need to wait for the response .

payment node

Let's look at the second node payment, First look FDL Code :

- type: task
    name: payment
    timeoutSeconds: 300
    resourceArn: acs:mns:::/topics/payment-fnf-demo-jiyuan/messages
    pattern: waitForCallback
    inputMappings:
      - target: taskToken
        source: $context.task.token
      - target: orderNum
        source: $local.orderNum
      - target: paymentcombination
        source: $local.paymentcombination
      - target: type
        source: $context.step.name 
    outputMappings:
      - target: paymentMethod
        source: $local.paymentMethod
      - target: orderNum
        source: $local.orderNum
      - target: price
        source: $local.price
      - target: taskToken
        source: $input.taskToken
    serviceParams:
      MessageBody: $
      Priority: 1
    catch:
      - errors:
          - FnF.TaskTimeout
        goto: orderCanceled

When the process flows to payment After node , It means that the user enters the payment page .

At this time payment The node will MNS Of Topicpayment-fnf-demo-jiyuan Send a message , Will trigger payment-fnf-demo function .

payment-fnf-demo function

payment-fnf-demo Function creation and generateInfo-fnf-demo Function similar to , There's no more encumbrance here . Let's go straight to the code :

# -*- coding: utf-8 -*-
import logging
import json
import os
import time
import logging
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.acs_exception.exceptions import ServerException
from aliyunsdkcore.client import AcsClient
from aliyunsdkfnf.request.v20190315 import ReportTaskSucceededRequest
from aliyunsdkfnf.request.v20190315 import ReportTaskFailedRequest
from mns.account import Account  # pip install aliyun-mns
from mns.queue import *


def handler(event, context):
    logger = logging.getLogger()
    region = "xxx"
    account_id = "xxx"
    ak_id = "xxx"
    ak_secret = "xxx"
    mns_endpoint = "http://your_account_id.mns.cn-hangzhou.aliyuncs.com/"
    queue_name = "payment-queue-fnf-demo"
    my_account = Account(mns_endpoint, ak_id, ak_secret)
    my_queue = my_account.get_queue(queue_name)
    # my_queue.set_encoding(False)
    fnf_client = AcsClient(
        ak_id,
        ak_secret,
        region
    )
    eventJson = json.loads(event)

    isLoop = True
    while isLoop:
        try:
            recv_msg = my_queue.receive_message(30)
            isLoop = False
            # body = json.loads(recv_msg.message_body)
            logger.info("recv_msg.message_body:======================" + recv_msg.message_body)
            msgJson = json.loads(recv_msg.message_body)
            my_queue.delete_message(recv_msg.receipt_handle)
            # orderCode = int(time.time())
            task_token = eventJson["taskToken"]
            orderNum = eventJson["orderNum"]
            output = "{\"orderNum\": \"%s\", \"paymentMethod\": \"%s\", \"price\": \"%s\" " \
                         "}" % (orderNum, msgJson["paymentMethod"], msgJson["price"])
            request = ReportTaskSucceededRequest.ReportTaskSucceededRequest()
            request.set_Output(output)
            request.set_TaskToken(task_token)
            resp = fnf_client.do_action_with_exception(request)
        except Exception as e:
            logger.info("new loop")
    return 'hello world'

The core idea of this function is to wait for the user to select a payment method on the payment page to confirm the payment . So it's used here MNS To simulate waiting . Loop wait for receive queue payment-queue-fnf-demo The messages in the , After receiving the message, return the order number and the specific payment method and amount selected by the user to payment node .

VUE Select payment method page

Because after generateInfo After node , The payment method information for this order is available , So for users , When the item is completed 、 merchants 、 After the address , The page you jump to is the confirmation payment page , It also includes the payment methods supported by the merchant .

After entering the page , Will ask Java Service exposed interface , Get order information , Different payment methods are displayed on the page according to the payment method . The code snippet is as follows :

When the user selects a payment method and clicks the submit order button , towards payment-queue-fnf-demo Queue to send messages , Notice payment-fnf-demo The function continues the subsequent logic .

Here I use a HTTP Function of trigger type , To implement MNS The logic of sending messages ,paymentMethod-fnf-demo The function code is as follows .

# -*- coding: utf-8 -*-

import logging
import urllib.parse
import json
from mns.account import Account  # pip install aliyun-mns
from mns.queue import *
HELLO_WORLD = b'Hello world!\n'

def handler(environ, start_response):
    logger = logging.getLogger()  
    context = environ['fc.context']
    request_uri = environ['fc.request_uri']
    for k, v in environ.items():
      if k.startswith('HTTP_'):
        # process custom request headers
        pass
    try:        
        request_body_size = int(environ.get('CONTENT_LENGTH', 0))    
    except (ValueError):        
        request_body_size = 0   
    request_body = environ['wsgi.input'].read(request_body_size)  
    paymentMethod = urllib.parse.unquote(request_body.decode("GBK"))
    logger.info(paymentMethod)
    paymentMethodJson = json.loads(paymentMethod)

    region = "cn-xxx"
    account_id = "xxx"
    ak_id = "xxx"
    ak_secret = "xxx"
    mns_endpoint = "http://your_account_id.mns.cn-hangzhou.aliyuncs.com/"
    queue_name = "payment-queue-fnf-demo"
    my_account = Account(mns_endpoint, ak_id, ak_secret)
    my_queue = my_account.get_queue(queue_name)
    output = "{\"paymentMethod\": \"%s\", \"price\":\"%s\" " \
                         "}" % (paymentMethodJson["paymentMethod"], paymentMethodJson["price"])
    msg = Message(output)
    my_queue.send_message(msg)
    
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    return [HELLO_WORLD]

The logic of this function is simple , That is to say MNS Queues payment-queue-fnf-demo Send the payment method and amount selected by the user .

VUE The code snippet is as follows :

paymentCombination node

paymentCombination A node is a routing node , By judging a parameter to route to different nodes , It's natural to use here paymentMethod As a condition of judgment .FDL The code is as follows :

- type: choice
    name: paymentCombination
    inputMappings:
      - target: orderNum
        source: $local.orderNum
      - target: paymentMethod
        source: $local.paymentMethod
      - target: price
        source: $local.price
      - target: taskToken
        source: $local.taskToken
    choices:
      - condition: $.paymentMethod == "zhifubao"
        steps:
          - type: task
            name: zhifubao
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan/functions/zhifubao-fnf-demo
            inputMappings:
              - target: price
                source: $input.price             
              - target: orderNum
                source: $input.orderNum 
              - target: paymentMethod
                source: $input.paymentMethod
              - target: taskToken
                source: $input.taskToken
      - condition: $.paymentMethod == "weixin"
        steps:
          - type: task
            name: weixin
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/weixin-fnf-demo
            inputMappings:
            - target: price
              source: $input.price             
            - target: orderNum
              source: $input.orderNum
            - target: paymentMethod
              source: $input.paymentMethod
            - target: taskToken
              source: $input.taskToken
      - condition: $.paymentMethod == "unionpay"
        steps:
          - type: task
            name: unionpay
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/union-fnf-demo
            inputMappings:
            - target: price
              source: $input.price             
            - target: orderNum
              source: $input.orderNum 
            - target: paymentMethod
              source: $input.paymentMethod
            - target: taskToken
              source: $input.taskToken
    default:
      goto: orderCanceled

The process here is , After the user chooses the payment method , Send by message to payment-fnf-demo function , The payment method is then returned , So it was transferred to paymentCombination The node flows to the node and function that processes the payment logic by judging the payment method .

zhifubao node

Let's take a concrete look at one zhifubao node :

choices:
      - condition: $.paymentMethod == "zhifubao"
        steps:
          - type: task
            name: zhifubao
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan/functions/zhifubao-fnf-demo
            inputMappings:
              - target: price
                source: $input.price             
              - target: orderNum
                source: $input.orderNum 
              - target: paymentMethod
                source: $input.paymentMethod
              - target: taskToken
                source: $input.taskToken

Of this node resourceArn Different from the previous two nodes , The configuration here is for the function in function calculation ARN, That is to say, when the process flows to this node, it will trigger zhifubao-fnf-demo function , This function is an event trigger function , But you don't need to create any triggers . The process changes the order amount 、 The order number 、 Payment method passed to zhifubao-fnf-demo function .

zhifubao-fnf-demo function

Now let's look at zhifubao-fnf-demo Function code :

# -*- coding: utf-8 -*-
import logging
import json
import requests
import urllib.parse
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.acs_exception.exceptions import ServerException
from aliyunsdkfnf.request.v20190315 import ReportTaskSucceededRequest
from aliyunsdkfnf.request.v20190315 import ReportTaskFailedRequest


def handler(event, context):
  region = "cn-xxx"
  account_id = "xxx"
  ak_id = "xxx"
  ak_secret = "xxx"
  fnf_client = AcsClient(
    ak_id,
    ak_secret,
    region
  )
  logger = logging.getLogger()
  logger.info(event)
  bodyJson = json.loads(event)
  price = bodyJson["price"]
  taskToken = bodyJson["taskToken"]
  orderNum = bodyJson["orderNum"]
  paymentMethod = bodyJson["paymentMethod"]
  logger.info("price:" + price)
  newPrice = int(price) * 0.8
  logger.info("newPrice:" + str(newPrice))
  url = "http://xx.xx.xx.xx:8080/setPaymentCombination/" + orderNum + "/" + paymentMethod + "/" + str(newPrice)
  x = requests.get(url)

  return {"Status":"ok"}

The code logic in the example is simple , After receiving the amount , Mark the amount 8 fold , The price is then updated back to the order . The nodes and functions of other payment methods are as follows , Change the implementation logic . In this example , Wechat payment 5 fold , UnionPay payment 7 fold .

Complete process

In the process orderCompleted and orderCanceled Nodes don't do much logic , You can play by yourself , The idea is the same as the previous node . So the whole process is like this :

from Serverless The flow of nodes seen in the workflow is like this :

summary

Here we are , We are based on Serverless Workflow and Serverless The order module example built by function calculation is completed , In the example , There are two points that need your attention :

  1. Metadata rules for configuring merchants and payment methods .
  2. Confirm metadata rules of payment page .

Because in actual production , We need to abstract the customizable parts as metadata descriptions , It is necessary to have a configuration interface to formulate the payment method of merchants, that is, to update metadata rules , Then the front page displays the corresponding content based on the metadata information .

So if you need to access other payment methods later , Just in paymentCombination Determine the routing rules in the routing node , Then add the corresponding payment method function . Configure items by adding metadata , The new payment method can be displayed on the page , And route to the function that processes the new payment method .

author : Alicloud Solution Architect Counting margin

 

Link to the original text

This article is the original content of Alibaba cloud , No reprint without permission .

版权声明
本文为[Alicloud technology blog]所创,转载请带上原文链接,感谢
https://cdmana.com/2020/12/20201224162433982Y.html

Scroll to Top