65.9K
CodeProject 正在变化。 阅读更多。
Home

实践中的货币 - OutSystems Fixer.io API 封装器

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2020年11月17日

CPOL

15分钟阅读

viewsIcon

5247

在本文中,我将提出一系列使用 Fixer API 的可能性,以支持一套支持多货币的场景。

大家好!我很高兴与大家分享与 OutSystems 平台进行的精彩集成。本次演示,我们将集成 Fixer,一个简单轻量的 API,用于获取当前和历史外汇汇率。

那么,实践中的货币,嗯?如果你在想,答案是肯定的:本文的标题灵感来自我个人喜欢的一本书《软件架构实践》。这是一本好书,你应该看看。它不是一本常规的“从头读到尾”的书,但包含关于质量属性和实现这些属性的策略的非常有趣的话题。如果你对软件架构感兴趣,我认为值得一看。

让我们回到主题。正如你可能已经知道的,OutSystems 支持多语言和多租户。但是……多货币呢?是的,OutSystems 不支持多货币,至少不是直接支持。明确地说,我们不会为你的多货币需求创建一刀切的解决方案。我将提出一系列使用 Fixer API 的可能性,以支持一套场景。

现在,至关重要的问题:什么场景?

在进入场景列表之前,我想对主题做一个简要介绍,并澄清一组现在将非常非常常见的术语。

那么,什么是货币?

根据“货币”一词最具体的用法(源自中古英语:curraunt,“流通中”,源自拉丁语:currens, -entis),指的是在实际使用或作为交易媒介流通中的任何形式的货币,特别是流通的纸币和硬币。更普遍的定义是,货币是一个货币体系(货币单位)在普遍使用,尤其是在一个国家。根据这个定义,美元、英镑、澳元、欧元和俄罗斯卢布都是货币的例子。这些不同的货币被认为是价值储存手段,并在国际外汇市场上进行交易,外汇市场决定了不同货币的相对价值。在此意义上的货币由政府定义,每种类型都有有限的接受范围。

以上摘录自Wikipedia,免费的百科全书。但是,我们大多数人都清楚什么是货币——因为我们几乎每天都在使用它——对吧?因此,相关的问题——也是绝大多数人可能不知道的问题——是:为什么货币的价值会发生变化?

根据 Investopedia 的说法,有六个主要因素影响货币的价值。而且,再次强调,货币的价值不是绝对值,而是总是相对于不同货币的相对价值。截至 2018 年 8 月 21 日,一 (1) 欧元价值一美元十五美分 (1.15)。绝对价值将始终为 1——一个欧元永远是一个欧元。

这些主要因素是

  • 通货膨胀差异
  • 利率差异
  • 经常账户赤字
  • 公共债务
  • 贸易条件
  • 政治稳定和经济表现

可以在以下文章中找到对这些因素的全面描述以及更多信息:影响汇率的 6 个因素,来自 Investopedia。

现在我们已经了解了影响货币价值的因素的冰山一角,让我们来讨论一些在本文其余部分将广泛使用的术语。

  • 符号:符号是给定货币的代码。EUR 是欧元的符号,USD 是美元的符号。
  • 基准:计算值的基准货币/符号。如果欧元基准下的美元价值为 1.20,则意味着 1 欧元将获得 1.2 美元。
  • EOD:日终。在本文的上下文中,EOD 值将是给定日期的参考值。

还记得那个至关重要的问题吗?所以:我们将每小时检索一组符号相对于一个基准的当前值。默认情况下,我们的基准是欧元。如果你需要另一个基准——比如你的业务以美元结算——我们可以

a) 从 API 检索新值

b) 使用通用基准计算值。

在可能的选项中,一个有潜在成本(API 限制),另一个……则不然。不仅如此,我们将通过提供一组函数来支持这两种情况:一个用于检索给定时间点的值——如果你处理的是多货币的金融交易,这很好——以及一个用于转换货币 A 为 B,使用基准 C——通常称为交叉汇率。

我不会讨论如何获取 API 密钥,因为,嗯……在 Fixer 的网站上非常简单明了。一旦你有了 API 密钥,你就可以开始集成可用的端点。

现在,技术细节。

Fixer “默认”提供了六个不同的端点:一个用于支持的符号,五个用于货币值本身。在这五个与货币相关的端点中,我们有一个用于最新值,一个用于历史值,一个用于货币转换,一个用于两个日期之间的货币波动,还有一个用于时间序列值。

虽然前三个的目的似乎非常清楚,但我将对后两个端点做简要解释。以两个日期为例:1 月 1 日和 1 月 5 日。当你调用波动端点时,你将获得 1 月 5 日 EOD 值与 1 月 1 日 EOD 值之间的差值——所以,只有一个结果。如果你调用这两个日期的时序值,你将收到这五天内给定符号的每日 EOD 值——所以,有五个结果。当我们进行实现并查看响应时,这将更容易理解。

关于可用的端点,我想在此声明一个非常重要的事情:并非所有端点都包含在免费套餐中。免费套餐仅包含符号最新历史数据——这并非什么大问题,因为我们可以使用免费端点来获取与付费端点相关的数据,只要我们在免费套餐的限制内进行操作。这就是为什么当你查看 REST 集成时,你会看到两个“不同”的条目:一个名为 FixerFree,另一个名为 FixerPaid。它们各自包含相应的端点,而 BaseURL 属性是相同的。

集成

如前所述,我们将实现所有六个可用端点。所有端点的实现逻辑都将非常相似:我们获取完整的请求和响应,将其粘贴到 Service Studio,然后它就会自动创建所有结构的集成。我们将使用数据模型来存储所有信息来支持此集成。很简单,对吧?

嗯……不太是。让我解释一下,以符号端点为例。让我们向端点发出 GET 请求: http://data.fixer.io/api/symbols?access_key=<API KEY>。你会看到这样的结果

{
    "success": true,
    "symbols": {
        "AED": "United Arab Emirates Dirham",
        "AFN": "Afghan Afghani",
        "ALL": "Albanian Lek",
        "AMD": "Armenian Dram",
        "ANG": "Netherlands Antillean Guilder",
        "AOA": "Angolan Kwanza",
        "ARS": "Argentine Peso",
        "AUD": "Australian Dollar",
        "AWG": "Aruban Florin",
        "AZN": "Azerbaijani Manat",
        "BAM": "Bosnia-Herzegovina Convertible Mark",
        "BBD": "Barbadian Dollar",
        "BDT": "Bangladeshi Taka",
        "BGN": "Bulgarian Lev",
        "BHD": "Bahraini Dinar",
        "BIF": "Burundian Franc",
        "BMD": "Bermudan Dollar",
        "BND": "Brunei Dollar",
        "BOB": "Bolivian Boliviano",
        "BRL": "Brazilian Real",
        "BSD": "Bahamian Dollar",
        "BTC": "Bitcoin",
        "BTN": "Bhutanese Ngultrum",
        "BWP": "Botswanan Pula",
        "BYN": "New Belarusian Ruble",
        "BYR": "Belarusian Ruble",
        "BZD": "Belize Dollar",
        "CAD": "Canadian Dollar",
        "CDF": "Congolese Franc",
        "CHF": "Swiss Franc",
        "CLF": "Chilean Unit of Account (UF)",
        "CLP": "Chilean Peso",
        "CNY": "Chinese Yuan",
        "COP": "Colombian Peso",
        "CRC": "Costa Rican Colón",
        "CUC": "Cuban Convertible Peso",
        "CUP": "Cuban Peso",
        "CVE": "Cape Verdean Escudo",
        "CZK": "Czech Republic Koruna",
        "DJF": "Djiboutian Franc",
        "DKK": "Danish Krone",
        "DOP": "Dominican Peso",
        "DZD": "Algerian Dinar",
        "EGP": "Egyptian Pound",
        "ERN": "Eritrean Nakfa",
        "ETB": "Ethiopian Birr",
        "EUR": "Euro",
        "FJD": "Fijian Dollar",
        "FKP": "Falkland Islands Pound",
        "GBP": "British Pound Sterling",
        "GEL": "Georgian Lari",
        "GGP": "Guernsey Pound",
        "GHS": "Ghanaian Cedi",
        "GIP": "Gibraltar Pound",
        "GMD": "Gambian Dalasi",
        "GNF": "Guinean Franc",
        "GTQ": "Guatemalan Quetzal",
        "GYD": "Guyanaese Dollar",
        "HKD": "Hong Kong Dollar",
        "HNL": "Honduran Lempira",
        "HRK": "Croatian Kuna",
        "HTG": "Haitian Gourde",
        "HUF": "Hungarian Forint",
        "IDR": "Indonesian Rupiah",
        "ILS": "Israeli New Sheqel",
        "IMP": "Manx pound",
        "INR": "Indian Rupee",
        "IQD": "Iraqi Dinar",
        "IRR": "Iranian Rial",
        "ISK": "Icelandic Króna",
        "JEP": "Jersey Pound",
        "JMD": "Jamaican Dollar",
        "JOD": "Jordanian Dinar",
        "JPY": "Japanese Yen",
        "KES": "Kenyan Shilling",
        "KGS": "Kyrgystani Som",
        "KHR": "Cambodian Riel",
        "KMF": "Comorian Franc",
        "KPW": "North Korean Won",
        "KRW": "South Korean Won",
        "KWD": "Kuwaiti Dinar",
        "KYD": "Cayman Islands Dollar",
        "KZT": "Kazakhstani Tenge",
        "LAK": "Laotian Kip",
        "LBP": "Lebanese Pound",
        "LKR": "Sri Lankan Rupee",
        "LRD": "Liberian Dollar",
        "LSL": "Lesotho Loti",
        "LTL": "Lithuanian Litas",
        "LVL": "Latvian Lats",
        "LYD": "Libyan Dinar",
        "MAD": "Moroccan Dirham",
        "MDL": "Moldovan Leu",
        "MGA": "Malagasy Ariary",
        "MKD": "Macedonian Denar",
        "MMK": "Myanma Kyat",
        "MNT": "Mongolian Tugrik",
        "MOP": "Macanese Pataca",
        "MRO": "Mauritanian Ouguiya",
        "MUR": "Mauritian Rupee",
        "MVR": "Maldivian Rufiyaa",
        "MWK": "Malawian Kwacha",
        "MXN": "Mexican Peso",
        "MYR": "Malaysian Ringgit",
        "MZN": "Mozambican Metical",
        "NAD": "Namibian Dollar",
        "NGN": "Nigerian Naira",
        "NIO": "Nicaraguan Córdoba",
        "NOK": "Norwegian Krone",
        "NPR": "Nepalese Rupee",
        "NZD": "New Zealand Dollar",
        "OMR": "Omani Rial",
        "PAB": "Panamanian Balboa",
        "PEN": "Peruvian Nuevo Sol",
        "PGK": "Papua New Guinean Kina",
        "PHP": "Philippine Peso",
        "PKR": "Pakistani Rupee",
        "PLN": "Polish Zloty",
        "PYG": "Paraguayan Guarani",
        "QAR": "Qatari Rial",
        "RON": "Romanian Leu",
        "RSD": "Serbian Dinar",
        "RUB": "Russian Ruble",
        "RWF": "Rwandan Franc",
        "SAR": "Saudi Riyal",
        "SBD": "Solomon Islands Dollar",
        "SCR": "Seychellois Rupee",
        "SDG": "Sudanese Pound",
        "SEK": "Swedish Krona",
        "SGD": "Singapore Dollar",
        "SHP": "Saint Helena Pound",
        "SLL": "Sierra Leonean Leone",
        "SOS": "Somali Shilling",
        "SRD": "Surinamese Dollar",
        "STD": "São Tomé and Príncipe Dobra",
        "SVC": "Salvadoran Colón",
        "SYP": "Syrian Pound",
        "SZL": "Swazi Lilangeni",
        "THB": "Thai Baht",
        "TJS": "Tajikistani Somoni",
        "TMT": "Turkmenistani Manat",
        "TND": "Tunisian Dinar",
        "TOP": "Tongan Paʻanga",
        "TRY": "Turkish Lira",
        "TTD": "Trinidad and Tobago Dollar",
        "TWD": "New Taiwan Dollar",
        "TZS": "Tanzanian Shilling",
        "UAH": "Ukrainian Hryvnia",
        "UGX": "Ugandan Shilling",
        "USD": "United States Dollar",
        "UYU": "Uruguayan Peso",
        "UZS": "Uzbekistan Som",
        "VEF": "Venezuelan Bolívar Fuerte",
        "VND": "Vietnamese Dong",
        "VUV": "Vanuatu Vatu",
        "WST": "Samoan Tala",
        "XAF": "CFA Franc BEAC",
        "XAG": "Silver (troy ounce)",
        "XAU": "Gold (troy ounce)",
        "XCD": "East Caribbean Dollar",
        "XDR": "Special Drawing Rights",
        "XOF": "CFA Franc BCEAO",
        "XPF": "CFP Franc",
        "YER": "Yemeni Rial",
        "ZAR": "South African Rand",
        "ZMK": "Zambian Kwacha (pre-2013)",
        "ZMW": "Zambian Kwacha",
        "ZWL": "Zimbabwean Dollar"
    }
}
图 1 — 符号端点输出

图 2 — 生成的结构。

看起来输出很简单,对吧?错了!当你使用 OutSystems 的默认功能导入 REST 方法/端点时,你会得到图 2 所示的内容。比如,一个结构中有 168 个属性。正如 Jesse Ventura 所说,我没有时间去遍历一个我必须手动迭代的结构的 168 个属性。另外,在有人提到“也许 ardoJSON 可以解决问题”之前,我要说一次,而且只说一次

图 3 — 但这是另一种爱,伙计!

**是的,我百分之百确定 Jesse Ventura 曾这么说过

那么,我做了什么?好吧,除了 Convert 端点之外,所有端点的输出都是纯文本。然后,我将使用一个很酷的(自定义)C# 扩展来解析输出,并为 OutSystems 提供一个“可扩展”的响应版本。尽管看到了低代码平台的种种好处,我仍然喜欢“敲几行代码”——灵感来自 Carlos Carvalhal 的“把所有的肉都放到烧烤架上”。这可能也是我准备了新的和旧的阅读材料以备将来几天的原因——尽管这些书都不是 OutSystems 人“推荐”阅读的

图 4 — 阅读材料。是的,那些是球罐。关于最后一个,我相信我的数据结构与算法老师会感到骄傲。

由于我不想让本文过长,我将快速总结开发过程

  • 我们将从 Fixer 检索完整的 JSON 输出
  • 我们将使用一个工具从 JSON 输出生成 C# 类。(相信我,这很有趣)
  • 我们将使用 Integration Studio 来实现这些方法,以便我们可以在 OutSystems 中接收数据。

我不会深入探讨“如何将 REST 端点导入 OutSystems”。互联网上有很多资源,而且事实上,这非常简单,你应该先尝试自己动手。

到目前为止,我们已经将端点集成到了 Service Studio。因此,让我们进入有趣的部分:在 OutSystems 扩展中处理响应。如上所述,我使用了一个工具从 JSON 输出生成 C# 类。如果你在 Google 上搜索json to c# class,你看到的第一个结果可能是 http://json2csharp.com。虽然这完全没问题,但底部有一个名为 Quicktype 的选项。我强烈推荐使用 Quicktype。为什么?

我将引导你完成我的旅程,并使用 json2csharp 从 JSON 对象生成 C# 类。对于 Symbols 端点,我们得到这个

public class Symbols
{
    public string AED { get; set; }
    public string AFN { get; set; }
    public string ALL { get; set; }
    public string AMD { get; set; }
    public string ANG { get; set; }
    public string AOA { get; set; }
    public string ARS { get; set; }
    public string AUD { get; set; }
    public string AWG { get; set; }
    public string AZN { get; set; }
    public string BAM { get; set; }
    public string BBD { get; set; }
    public string BDT { get; set; }
    public string BGN { get; set; }
    public string BHD { get; set; }
    public string BIF { get; set; }
    public string BMD { get; set; }
    public string BND { get; set; }
    public string BOB { get; set; }
    public string BRL { get; set; }
    public string BSD { get; set; }
    public string BTC { get; set; }
    public string BTN { get; set; }
    public string BWP { get; set; }
    public string BYN { get; set; }
    public string BYR { get; set; }
    public string BZD { get; set; }
    public string CAD { get; set; }
    public string CDF { get; set; }
    public string CHF { get; set; }
    public string CLF { get; set; }
    public string CLP { get; set; }
    public string CNY { get; set; }
    public string COP { get; set; }
    public string CRC { get; set; }
    public string CUC { get; set; }
    public string CUP { get; set; }
    public string CVE { get; set; }
    public string CZK { get; set; }
    public string DJF { get; set; }
    public string DKK { get; set; }
    public string DOP { get; set; }
    public string DZD { get; set; }
    public string EGP { get; set; }
    public string ERN { get; set; }
    public string ETB { get; set; }
    public string EUR { get; set; }
    public string FJD { get; set; }
    public string FKP { get; set; }
    public string GBP { get; set; }
    public string GEL { get; set; }
    public string GGP { get; set; }
    public string GHS { get; set; }
    public string GIP { get; set; }
    public string GMD { get; set; }
    public string GNF { get; set; }
    public string GTQ { get; set; }
    public string GYD { get; set; }
    public string HKD { get; set; }
    public string HNL { get; set; }
    public string HRK { get; set; }
    public string HTG { get; set; }
    public string HUF { get; set; }
    public string IDR { get; set; }
    public string ILS { get; set; }
    public string IMP { get; set; }
    public string INR { get; set; }
    public string IQD { get; set; }
    public string IRR { get; set; }
    public string ISK { get; set; }
    public string JEP { get; set; }
    public string JMD { get; set; }
    public string JOD { get; set; }
    public string JPY { get; set; }
    public string KES { get; set; }
    public string KGS { get; set; }
    public string KHR { get; set; }
    public string KMF { get; set; }
    public string KPW { get; set; }
    public string KRW { get; set; }
    public string KWD { get; set; }
    public string KYD { get; set; }
    public string KZT { get; set; }
    public string LAK { get; set; }
    public string LBP { get; set; }
    public string LKR { get; set; }
    public string LRD { get; set; }
    public string LSL { get; set; }
    public string LTL { get; set; }
    public string LVL { get; set; }
    public string LYD { get; set; }
    public string MAD { get; set; }
    public string MDL { get; set; }
    public string MGA { get; set; }
    public string MKD { get; set; }
    public string MMK { get; set; }
    public string MNT { get; set; }
    public string MOP { get; set; }
    public string MRO { get; set; }
    public string MUR { get; set; }
    public string MVR { get; set; }
    public string MWK { get; set; }
    public string MXN { get; set; }
    public string MYR { get; set; }
    public string MZN { get; set; }
    public string NAD { get; set; }
    public string NGN { get; set; }
    public string NIO { get; set; }
    public string NOK { get; set; }
    public string NPR { get; set; }
    public string NZD { get; set; }
    public string OMR { get; set; }
    public string PAB { get; set; }
    public string PEN { get; set; }
    public string PGK { get; set; }
    public string PHP { get; set; }
    public string PKR { get; set; }
    public string PLN { get; set; }
    public string PYG { get; set; }
    public string QAR { get; set; }
    public string RON { get; set; }
    public string RSD { get; set; }
    public string RUB { get; set; }
    public string RWF { get; set; }
    public string SAR { get; set; }
    public string SBD { get; set; }
    public string SCR { get; set; }
    public string SDG { get; set; }
    public string SEK { get; set; }
    public string SGD { get; set; }
    public string SHP { get; set; }
    public string SLL { get; set; }
    public string SOS { get; set; }
    public string SRD { get; set; }
    public string STD { get; set; }
    public string SVC { get; set; }
    public string SYP { get; set; }
    public string SZL { get; set; }
    public string THB { get; set; }
    public string TJS { get; set; }
    public string TMT { get; set; }
    public string TND { get; set; }
    public string TOP { get; set; }
    public string TRY { get; set; }
    public string TTD { get; set; }
    public string TWD { get; set; }
    public string TZS { get; set; }
    public string UAH { get; set; }
    public string UGX { get; set; }
    public string USD { get; set; }
    public string UYU { get; set; }
    public string UZS { get; set; }
    public string VEF { get; set; }
    public string VND { get; set; }
    public string VUV { get; set; }
    public string WST { get; set; }
    public string XAF { get; set; }
    public string XAG { get; set; }
    public string XAU { get; set; }
    public string XCD { get; set; }
    public string XDR { get; set; }
    public string XOF { get; set; }
    public string XPF { get; set; }
    public string YER { get; set; }
    public string ZAR { get; set; }
    public string ZMK { get; set; }
    public string ZMW { get; set; }
    public string ZWL { get; set; }
}

public class RootObject
{
    public bool success { get; set; }
    public Symbols symbols { get; set; }
}
图 5 — json2csharp 输出。

看到结果了吗?我一点也不懂 C#,但我很想知道:如何将这一组元素转换为键值对列表,知道键就是字段本身的名称?

你好,你听说过反射吗? 对于不了解的人来说,反射对程序员来说就像是鬼怪对孩子一样。我可能有点夸张——除非你尝试用 CLOS 进行元编程:那样的话,鬼怪就像饼干怪兽一样邪恶。

反射很棒,但也有问题。使用反射的性能成本不可忽视。幸运的是,使用 Quicktype,我得到了这个精巧的版本

// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do:
//
//    using OutSystems.NssFixerParser.Integrations;
//
//    var getSymbols = GetSymbols.FromJson(jsonString);

namespace OutSystems.NssFixerParser.Integrations
{
    using System;
    using System.Collections.Generic;

    using System.Globalization;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Converters;

    public partial class GetSymbols
    {
        [JsonProperty("success")]
        public bool Success { get; set; }

        [JsonProperty("symbols")]
        public Dictionary<string, string> Symbols { get; set; }
    }

    public partial class GetSymbols
    {
        public static GetSymbols FromJson(string json)
        {
            return JsonConvert.DeserializeObject<GetSymbols>(json, OutSystems.NssFixerParser.Integrations.Converter.Settings);
        }
    }

    public static class Serialize
    {
        public static string ToJson(this GetSymbols self)
        {
            return JsonConvert.SerializeObject(self, OutSystems.NssFixerParser.Integrations.Converter.Settings);
        }
    }

    internal static class Converter
    {
        public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
        {
            MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
            DateParseHandling = DateParseHandling.None,
            Converters = {
                new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
            },
        };
    }
}
图 6 — Quicktype 输出

重要提示:当使用 Quicktype 从 JSON 对象生成 C# 类以供 OutSystems 使用时,请确保选择 C# 版本为 V5。默认情况下,选择 V6,它将无法编译。注意:在与其他 MVP 的一次聊天中,这可能与 MSBuild 有关。我将进一步研究并相应更新帖子。

有些相关的注释:如果你使用 Quicktype 生成的多个类,其中包含公共元素(如 Error 结构),只需将这些类移动到单独的文件中。然后可以从生成代码中删除它们,只要你已正确引用所有内容,就不会出现错误。

图 7 — 扩展结构。

根据上面的注释,我将 internal static class Converter, public static class SerializeError 类(图 6 未显示)移到了单独的文件中,最终结果如图 7 所示。这样做,我们将有四个类用于五个端点——Convert 端点不在此方法考虑范围内——即 Converter、Serialize 和 Error 类(通用类)。如果你擅长数学,你可能会想:等等,什么?五个端点对应四个类?

是的,这是因为历史汇率和最新汇率具有相同的输出结构。那么,我们能做什么?没错,我们可以为它们使用相同的代码。这样就节省了工作。

图 8 — 我们都很擅长数学。来源:Amazon.ca。

现在,让我们进入实际操作。在 Integration Studio 中,我们将创建一个具有图 9 所示配置的扩展。

图 9 — 扩展配置。

注意:在有人询问之前,目前没有 Java 版本。未来可能会有,但可能性很小。

然后,我们将定义一个操作,并具有以下参数——如图 10 所示

  • JSONString(输入)
  • Success(输出)
  • Symbols — 键值对列表(输出)
  • Error — 通用错误结构(输出)

图 10 — Integration Studio 中打开的 Parse_GetSymbols。

现在我们已经定义了操作并更新了扩展代码,是时候使用我们生成的类了

/// <summary>
/// 
/// </summary>
/// <param name="ssJSONString"></param>
/// <param name="ssSuccess"></param>
/// <param name="ssSymbols"></param>
/// <param name="ssError"></param>
public void MssParse_GetSymbols(string ssJSONString, out bool ssSuccess, 
                                out RLKeyPairStructureRecordList ssSymbols, 
                                out RCErrorRecord ssError)
{
        ssSuccess = false;
        ssSymbols = new RLKeyPairStructureRecordList();
        ssError = new RCErrorRecord(null);

        GetSymbols getSymbols = GetSymbols.FromJson(ssJSONString);
        if (getSymbols.Success)
        {
                foreach (String ISOCode in getSymbols.Symbols.Keys)
                {
                        RCKeyPairStructureRecord rec = new RCKeyPairStructureRecord();
                        rec.ssSTKeyPairStructure.ssKey = ISOCode;
                        rec.ssSTKeyPairStructure.ssValue = getSymbols.Symbols[ISOCode];
                        ssSymbols.Append(rec);
                }
                ssSuccess = true;
        }
        else
        {
                ssSuccess = false;
                ssError.ssSTError.ssCode = getSymbols.Error.Code.ToString();
                ssError.ssSTError.ssInfo = getSymbols.Error.Info;
                ssError.ssSTError.ssType = getSymbols.Error.Type;
        }
} // MssParse_GetSymbols
图 11 — Parse_GetSymbols 方法体。

上面可以看到我们在做什么。这是所有操作的通用过程。不同之处在于复杂性或字段的数量。如果你愿意,可以随意查看扩展的源代码,并告诉我是否有任何问题、疑虑或建议。再次强调,分享即关怀。

因此,除了常规的变量初始化之外,我们将 JSON 字符串反序列化为 GetSymbols 对象(第 16 行),并验证请求是否成功。如果成功,我们将遍历字典并创建一个 KeyPairStructureRecord——我不知道为什么完整名称是 RC KeyPairStructureRecord,因为最佳实践告诉我们记录名称应以 RC 开头——然后将其添加到列表中。

如果请求不成功,我们将填充我们的错误结构。简单。

我在这里加一句个人评论:当我开始为这个组件工作时,我非常想写一篇关于 C# 反射的文章,这样你们就会认为我是一个超级厉害的 C# 程序员。但这就是软件开发的酷之处:你认为的事情,它们改变得如此之快和容易(#Agile)。而这,我的朋友们,就是我们工作的魅力——如果我以后转行去做电视新闻主播,那将是我的结尾语。

但是……我们该如何使用它呢?

创建一个组件并写一篇关于它的文章,如果不展示它的工作方式,那就没有意义了。所以,如果你还记得以前的文章,我们已经有一个控制面板,它将是这个组件的绝佳载体。

总而言之——并创建一个场景——我们已经有一个控制面板,显示我们网关处理的最新付款。这些最新付款可能有不同的货币,但我们的业务只以欧元结算。

在引用的文章的图 17(本文图 12)中,你有以下图片

图 12 — Adyen 控制面板

在右上角,我们有一个 CardSimple,显示“过去 30 天内的热门支付”。虽然有点模糊,但我们可以看到有欧元、日元和其他货币的支付。现在,让我们修改这个 CardSimple,确保我们显示的是交易发生当日的转换值。原始值将以欧元为基准进行转换。这有一个“许可”原因,我稍后会解释。

为了简单起见,我将使用一个可用的函数——CalculateConversion。对于 ListRecords 小部件中的每个项目,我将调用该函数,并计算结果。该函数行为如图 13 所示。

图 13 — CalculateConversion 函数。

此函数接收以下输入参数:BaseCurrency(文本)、DestinationCurrency(文本)、Amount(货币)、Date(日期)和一个输出参数:Value(货币)。现在,一步一步来

  • 我们检查在给定日期是否有可用的货币值(GetConversion 聚合)。如果存在,我们返回该值。
  • 如果不存在,我们尝试进行交叉货币转换。如果两个值在提到的日期都可用,我们将返回计算后的值。计算方式如下——为便于说明,元素已缩短。

Amount / (GetSourceCurrency.Value / GetDestinationCurrency.Value)

  • 如果值不可用,我们将执行 API 请求,如果成功,将返回汇率。由于我们只请求一个值,我们可以立即将其映射到输出。

然后,在 Card 小部件中的 ListRecords 中,我们将这个添加到我们的表达式中——如图 14 所示。由于使用了 FormatCurrency 函数来格式化转换后的金额,所以它有点“臃肿”。另外,由于我们以欧元为基准,我们不需要计算小数位数。请参考上一篇文章以理解这个概念。

图 14 — ListRecords 表达式。

我们有了第一个 FormatCurrency ,现在我们添加了文本部分“ (EUR: XXX)”,其中 XXX 将使用 CalculateConversion 函数计算。只是一个重要说明:请注意,在调用 CalculateConversion 函数时,我没有使用 DestinationCurrency 参数?这是因为我们将 DestinationCurrency 参数映射到了服务调用中的 Base 参数。由于我们使用免费套餐,因此无法在 API 调用中使用 Base 参数,默认情况下为 EUR。如果你至少拥有基础会员资格,就可以使用它。

这是最终结果——与谷歌在相应日期的值完全匹配

图 15 — UI 上的最终结果。

就这样,各位!该组件已在 OutSystems Forge 上提供,网址为 https://www.outsystems.com/forge/component/4122/fixer-io-api/

在结束之前,我将尝试一些新的事情。我正在研究发布赞助文章的可能性。如果你(或你的公司)有一些闲钱,并希望赞助任何文章或关于特定主题的文章,请通过 hello@armandogom.es 给我留言。明确说明:我真的很喜欢写作和创作新内容,所以我不会把钱留给自己。

因此,任何赞助文章的所有利润都将捐赠给慈善机构。

有关此事的更多详情,请通过上述电子邮件、TwitterLinkedIn 与我联系。

结束语 - 就是这样,各位!组件已在 OutSystems Forge 上提供,网址为 https://www.outsystems.com/forge/component/4122/fixer-io-api/

© . All rights reserved.