- 03-5820-1777平日10:00〜18:00
- お問い合わせ
今回はAWSのStep FunctionsというAWS LambdaやAWSのサービスなどをフローチャートにより組み合わせて機能を作成する事が出来るローコードビジュアル開発ツールを使用してローコード開発を実践してみたいと思います。
簡単な商品見積機能を作成します。
商品情報を入力し、商品コードから商品を検索し価格を取得し、入力された荷姿の算出方法に従って原価算出と輸送費・関税の算出する機能を作成します。
今回はDBを使用せずに、必要なデータはLambda内に持っています。
配送費は商品又はケースの体積と数量を計算し、40'コンテナと20'コンテナがいくつ必要かを計算し、その金額をし出力する。
完全は商品単価総計に関税率を乗算した金額を出力する。
Lambda関数は次の5つを用意する。
入力、出力ともJSON形式で行う。出力はPayloadの中にstatusCodeとbodyが入っている形式です。
statusCodeは正常終了又はエラー情報、bodyは処理結果が格納されています。
名称 | 説明 | 入力 | 出力(body) |
---|---|---|---|
GetGoodsByCode | 渡された商品コードから商品情報を 取得するLambda関数です。 | input.code:商品コード | code:商品コード name:商品名 innerCount:入数 volume:体積 price:単価 caseVolume:ケース体積 |
EstimateHomeGoodsPrice | 国内の商品単価算出をするLambda関数 | input.count:数量 input.price:単価(円) input.innerCount:入 | totalPrice:商品単価合計 |
EstimateForeignGoodsPrice | 海外の商品単価算出をするLambda関数 | input.count:数量 input.price:単価($) input.innerCount:入数 | totalPrice:商品単価合計 |
CalcTransportationCost | 輸送費を算出する Lambda関数 | input.volume:体積 input.count:数量 | twentyFeetVolumeCount:40’コンテナ個数 fouthFeetVolumeCount:20'コンテナ個数 totalAmount:輸送費合計 |
CalcImportDuty | 関税を算出する Lambda関数 | totalAmount:商品単価合計 | importDuty:関税 |
入力項目
入力コードで該当する商品情報を取得する。
GetGoodsByCode関数を使用する。
入力項目
出力項目
商品情報の値に応じて計算方法を識別する。
判定条件
エラー情報を出力して処理を終了する。
個別の国内商品原価計算を行う。
EstimateHomeGoodsPrice関数を使用する。
(ケースの国内商品原価計算のLambdaと同じものを使用する)
入力項目
出力項目
ケースの国内商品原価計算を行う。
EstimateHomeGoodsPrice関数を使用する。
(個別の国内商品原価計算のLambdaと同じものを使用する)
入力項目
出力項目
個別の海外商品原価計算を行う。
EstimateForeignGoodsPrice関数を使用する。
(ケースの海外商品原価計算のLambdaと同じものを使用する)
入力項目
出力項目
輸送費の計算を行う。
CalcTransportationCost関数を使用する。
(ケースの輸送費計算のLambdaと同じものを使用する)
入力項目
出力項目
ケースの海外商品原価計算を行う。
EstimateForeignGoodsPrice関数を使用する。
(個別の海外商品原価計算のLambdaと同じものを使用する)
入力項目
出力項目
輸送費の計算を行う。
CalcTransportationCost関数を使用する。
(ケースの輸送費計算のLambdaと同じものを使用する)
入力項目
出力項目
商品単価に対する関税額を計算する。
CalcCostDuty関数を使用する。
入力項目
出力項目
次はワークフローの定義を具体的に見ていきます。
次の図は上記の図と同様ですが、それぞれのワークの定義を具体的に見ていきます。
ステートマシン起動時にはステートマシン詳細の画面で実行する方法や、Api Gatewayから呼び出す方法などありますが、呼び出す際にJSON形式で必要なパラメータを渡す必要があります。
パラメータの形式を以下に示します。
{
"code": "G0001", //存在する商品コードを指定
"count": 10, //数量を指定
"pack":"piece", //pieceかcaseを指定
"country":"国内" //国内か中国を指定
}
設定タブ
出力タブ
5つの方向に分岐するので分岐条件をそれぞれ指定しています。
Default ruleは全ての分岐条件に当てはまらない時の指定です。
ErrorとCauseにエラーが起きた際に出力される適当なメッセージを入れて置きました。
設定タブ
出力タブ
設定タブ
出力タブ
設定タブ
出力タブ
設定タブ
出力タブ
設定タブ
出力タブ
設定タブ
出力タブ
設定タブ
出力タブ
上記のワークフローを定義したJSONファイルを下記に記述します。
AWS上でインポートして使用する事が可能です。(XXXXXXXの部分は自分の環境の数値に置き換えて下さい)
{
"Comment": "A description of my state machine",
"StartAt": "商品情報取得",
"States": {
"商品情報取得": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "arn:aws:lambda:ap-northeast-1:XXXXXXXXX:function:GetGoodsByCode:$LATEST",
"Payload": {
"input.code.$": "$.code"
}
},
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException",
"Lambda.SdkClientException",
"Lambda.TooManyRequestsException"
],
"IntervalSeconds": 2,
"MaxAttempts": 6,
"BackoffRate": 2
}
],
"Next": "計算方法識別",
"ResultPath": "$.GetGoodsResult",
"ResultSelector": {
"body.$": "States.StringToJson($.Payload.body)",
"statusCode.$": "$.Payload.statusCode"
}
},
"計算方法識別": {
"Type": "Choice",
"Choices": [
{
"And": [
{
"Variable": "$.pack",
"StringEquals": "piece"
},
{
"Variable": "$.country",
"StringEquals": "国内"
},
{
"Variable": "$.GetGoodsResult.statusCode",
"NumericEquals": 200
}
],
"Next": "国内商品原価計算(個別)"
},
{
"And": [
{
"Variable": "$.pack",
"StringEquals": "case"
},
{
"Variable": "$.country",
"StringEquals": "国内"
},
{
"Variable": "$.GetGoodsResult.statusCode",
"NumericEquals": 200
}
],
"Next": "国内商品原価計算(ケース)"
},
{
"And": [
{
"Variable": "$.pack",
"StringEquals": "piece"
},
{
"Not": {
"Variable": "$.country",
"StringEquals": "国内"
}
},
{
"Variable": "$.GetGoodsResult.statusCode",
"NumericEquals": 200
}
],
"Next": "海外商品原価計算(個別)"
},
{
"And": [
{
"Variable": "$.pack",
"StringEquals": "case"
},
{
"Not": {
"Variable": "$.country",
"StringEquals": "国内"
}
},
{
"Variable": "$.GetGoodsResult.statusCode",
"NumericEquals": 200
}
],
"Next": "海外商品原価計算(ケース)"
}
],
"Default": "Fail"
},
"Fail": {
"Type": "Fail",
"Error": "NotExistGoodsCode",
"Cause": "NotExistGoodsCode"
},
"国内商品原価計算(個別)": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "arn:aws:lambda:ap-northeast-1:XXXXXXX:function:EstimateHomeGoodsPrice:$LATEST",
"Payload": {
"input.count.$": "$.count",
"input.price.$": "$.GetGoodsResult.body.price",
"input.innerCount": 1
}
},
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException",
"Lambda.SdkClientException",
"Lambda.TooManyRequestsException"
],
"IntervalSeconds": 2,
"MaxAttempts": 6,
"BackoffRate": 2
}
],
"End": true,
"ResultSelector": {
"body.$": "States.StringToJson($.Payload.body)"
},
"ResultPath": "$.CalcEstimateResult"
},
"国内商品原価計算(ケース)": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "arn:aws:lambda:ap-northeast-1:XXXXXXX:function:EstimateHomeGoodsPrice:$LATEST",
"Payload": {
"input.count.$": "$.count",
"input.price.$": "$.GetGoodsResult.body.price",
"input.innerCount.$": "$.GetGoodsResult.body.innerCount"
}
},
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException",
"Lambda.SdkClientException",
"Lambda.TooManyRequestsException"
],
"IntervalSeconds": 2,
"MaxAttempts": 6,
"BackoffRate": 2
}
],
"ResultPath": "$.CalcEstimateResult",
"ResultSelector": {
"body.$": "States.StringToJson($.Payload.body)"
},
"End": true
},
"海外商品原価計算(個別)": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "arn:aws:lambda:ap-northeast-1:XXXXXXX:function:EstimateForeignGoodsPrice:$LATEST",
"Payload": {
"input.count.$": "$.count",
"input.price.$": "$.GetGoodsResult.body.priceDoll",
"input.innerCount": 1
}
},
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException",
"Lambda.SdkClientException",
"Lambda.TooManyRequestsException"
],
"IntervalSeconds": 2,
"MaxAttempts": 6,
"BackoffRate": 2
}
],
"ResultSelector": {
"body.$": "States.StringToJson($.Payload.body)"
},
"ResultPath": "$.CalcEstimateResult",
"Next": "輸送費計算(個別)"
},
"輸送費計算(個別)": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"Payload": {
"input.count.$": "$.count",
"input.volume.$": "$.GetGoodsResult.body.volume"
},
"FunctionName": "arn:aws:lambda:ap-northeast-1:XXXXXXX:function:CalcTranspotationCost:$LATEST"
},
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException",
"Lambda.SdkClientException",
"Lambda.TooManyRequestsException"
],
"IntervalSeconds": 2,
"MaxAttempts": 6,
"BackoffRate": 2
}
],
"Next": "関税計算",
"ResultPath": "$.CalcTransportationCostResult",
"ResultSelector": {
"body.$": "States.StringToJson($.Payload.body)"
}
},
"関税計算": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "arn:aws:lambda:ap-northeast-1:XXXXXXX:function:CalcImportDuty:$LATEST",
"Payload": {
"input.totalPrice.$": "$.CalcEstimateResult.body.totalPrice"
}
},
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException",
"Lambda.SdkClientException",
"Lambda.TooManyRequestsException"
],
"IntervalSeconds": 2,
"MaxAttempts": 6,
"BackoffRate": 2
}
],
"End": true,
"ResultPath": "$.CalcImportDutyResult",
"ResultSelector": {
"body.$": "States.StringToJson($.Payload.body)"
}
},
"海外商品原価計算(ケース)": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "arn:aws:lambda:ap-northeast-1:XXXXXXX:function:EstimateForeignGoodsPrice:$LATEST",
"Payload": {
"input.count.$": "$.count",
"input.price.$": "$.GetGoodsResult.body.priceDoll",
"input.innerCount.$": "$.GetGoodsResult.body.innerCount"
}
},
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException",
"Lambda.SdkClientException",
"Lambda.TooManyRequestsException"
],
"IntervalSeconds": 2,
"MaxAttempts": 6,
"BackoffRate": 2
}
],
"Next": "輸送費計算(ケース)",
"ResultPath": "$.CalcEstimateResult",
"ResultSelector": {
"body.$": "States.StringToJson($.Payload.body)"
}
},
"輸送費計算(ケース)": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "arn:aws:lambda:ap-northeast-1:XXXXXXX:function:CalcTranspotationCost:$LATEST",
"Payload": {
"input.count.$": "$.count",
"input.volume.$": "$.GetGoodsResult.body.caseVolume"
}
},
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException",
"Lambda.SdkClientException",
"Lambda.TooManyRequestsException"
],
"IntervalSeconds": 2,
"MaxAttempts": 6,
"BackoffRate": 2
}
],
"Next": "関税計算",
"ResultPath": "$.CalcTransportationCostResult",
"ResultSelector": {
"body.$": "States.StringToJson($.Payload.body)"
}
}
}
}
import json
def lambda_handler(event, context):
# ここは本来はDBから取得するロジックを実装する、ディクショナリで疑似的なDBを作成
goods1 = {'code' : 'G0001', 'name' : '商品A', 'innerCount': 80, 'volume' : 1, 'price' : 12000, 'caseVolume' : 10, 'priceDoll':90}
goods2 = {'code' : 'G0002', 'name' : '商品B', 'innerCount': 120, 'volume' : 1, 'price' : 10000, 'caseVolume' : 10, 'priceDoll':80}
goodsDic = { 'G0001' : goods1, 'G0002' : goods2 }
code = event['input.code']
goods = goodsDic.get( code )
if goods is None:
statusCode = 400
goods = ""
else:
statusCode = 200
return {
'statusCode': statusCode,
'body': json.dumps(goods, ensure_ascii=False)
}
import json
def lambda_handler(event, context):
count = event['input.count']
price = event['input.price']
innerCount = event['input.innerCount']
totalPrice = price * innerCount * count
body = {"totalPrice" : totalPrice}
return {
'statusCode': 200,
'body': json.dumps( body )
}
import json
def lambda_handler(event, context):
count = event['input.count']
price = event['input.price']
innerCount = event['input.innerCount']
# 通常はDBから取得するが今回はレートは130に固定にしておく
rate = 130
totalPrice = price * rate * innerCount * count
body = {"totalPrice" : totalPrice}
return {
'statusCode': 200,
'body': json.dumps( body )
}
import json
def lambda_handler(event, context):
# コンテナ容量を指定、通常はDBから取得するが今回は実装内に固定
fouthFeetVolume = 60
twentyFeetVolume = 30
# コンテナ使用料を指定、通常はDBから取得するが今回は実装内に固定
fouthFeetAmount = 750
twentyFeetAmount = 430
# 通常はDBから取得するが今回はレートは130に固定にしておく
rate = 130
volume = event['input.volume']
count = event['input.count']
totalVolume = volume * count
fouthFeetVolumeCount = 0
twentyFeetVolumeCount = 0
if totalVolume <= twentyFeetVolume:
twentyFeetVolumeCount+=1
else:
if totalVolume <= fouthFeetVolume:
fouthFeetVolumeCount+=1
else:
d = totalVolume // fouthFeetVolume
fouthFeetVolumeCount += d
d = totalVolume % fouthFeetVolume
if d > 0:
if d <= twentyFeetVolume:
twentyFeetVolumeCount+=1
else:
fouthFeetVolumeCount+=1
totalAmount = twentyFeetVolumeCount * twentyFeetAmount * rate
totalAmount += fouthFeetVolumeCount * fouthFeetAmount * rate
returnValue = {'twentyFeetVolumeCount' : twentyFeetVolumeCount, 'fouthFeetVolumeCount' : fouthFeetVolumeCount, 'totalAmount' : totalAmount}
return {
'statusCode': 200,
'body': json.dumps(returnValue)
}
import json
def lambda_handler(event, context):
# 関税率を指定、通常はDBから取得するが今回は実装内に固定
importDutyRate = 0.028
totalPrice = event['input.totalPrice']
importDuty = totalPrice * importDutyRate
importDuty = round(importDuty, 2)
returnValue = {'importDuty': importDuty}
return {
'statusCode': 200,
'body': json.dumps(returnValue)
}
海外・個別のケースを実行してみます。
{
"code": "G0002",
"count": 100,
"pack":"piece",
"country":"中国"
}
{
"output": {
"code": "G0002",
"count": 100,
"pack": "piece",
"country": "中国",
"GetGoodsResult": {
"body": {
"code": "G0002",
"name": "商品B",
"innerCount": 120,
"volume": 1,
"price": 10000,
"caseVolume": 10,
"priceDoll": 80
},
"statusCode": 200
},
"CalcEstimateResult": {
"body": {
"totalPrice": 1040000
}
},
"CalcTransportationCostResult": {
"body": {
"twentyFeetVolumeCount": 0,
"fouthFeetVolumeCount": 2,
"totalAmount": 195000
}
},
"CalcImportDutyResult": {
"body": {
"importDuty": 29120
}
}
},
"outputDetails": {
"truncated": false
}
}