runopencode / exchange-rate

用于获取、存储和操作货币汇率的库。

3.0.3 2022-11-01 17:32 UTC

This package is auto-updated.

Last update: 2024-08-29 03:58:07 UTC


README

在您的应用程序中获取、存储和使用货币汇率

Packagist Scrutinizer Code Quality Code Coverage Build Status Build Status

SensioLabsInsight

关于在您的网店/会计应用程序或类似应用中处理汇率时遇到域名问题。

如果您正在构建任何类型的会计应用程序或处理各种货币汇率的网店,您可能必须处理汇率。

汇率计算器很简单,然而,获取适当的汇率来工作可能很麻烦,特别是所有汇率都由外部第三方通过某些API提供。这意味着您的应用程序依赖于第三方API的可用性。

现在,考虑到(至少在商业理论中)汇率每天下午2点由国家银行为第二天(周五为周六和周日)确定,并且在工作日(至少不是在商品和服务市场上)汇率保持不变,您应该只能提前一次获取汇率,并使用您自己的本地源中的这些汇率,而无需从外部源获取当天的汇率。商业银行遵循类似的惯例,国家银行将确定买卖的上下限,商业银行可以在该范围内确定其价格(毕竟,货币交易是贸易业务的基础)。

总的来说,您的任务是在预先获取与您的应用程序一起工作的汇率,将它们存储在某些本地存储库中,并从那一刻起使用这些汇率。

这个库通过提供必要的类和工具来解决这个问题,这些工具将帮助您处理汇率。

请注意,这个库不是汇率计算器。

系统架构

系统定义了6个核心组件

  • ,实现RunOpenCode\ExchangeRate\Contract\SourceInterface,其唯一目的是从您的第三方API提供者(例如,您的银行或您所在国家的国家银行)为您提供汇率。每个源实现必须有一个独特的名称。您可以使用您的银行名称和国家名称构建您的源名称,例如。名称应包含小写字母,用下划线分隔。请注意,这个库不提供您任何源,源可以在单独的库中使用,您可以根据您的需求在项目中使用它们,或者您可以开发自己的。
  • 汇率,实现RunOpenCode\ExchangeRate\Contract\RateInterface,表示一个单独的汇率值,由源提供给您。汇率将被持久化在您的本地存储系统中,由您选择。您当然提供了这个接口的默认实现。
  • 存储库,实现RunOpenCode\ExchangeRate\Contract\RepositoryInterface负责在本地存储系统中持久化汇率。这个库提供了RunOpenCode\ExchangeRate\Repository\FileRepository的实现,该实现将汇率保存到本地纯文本文件。考虑到一年只有365天,这是一个很好的存储系统,尤其是如果您用某种缓存机制包装它。当然,如果您想通过Doctrine Dbal库将汇率存储在关系型数据库(如MySQL)中,您也可以使用RunOpenCode\ExchangeRate\Repository\DoctrineDbalRepository
  • 处理器,实现了 RunOpenCode\ExchangeRate\Contract\ProcessorInterface 接口,用于在从源获取汇率后对其进行处理和修改。库提供了一些处理器实现,主要负责验证获取的汇率。然而,在持久化之前,您可以随意修改汇率。例如,您的客户可能要求您使用他们自己的汇率,这些汇率与某些基准汇率的价格不同。处理器还为您提供了在获取基准汇率后添加您自己的汇率的可能性。
  • 配置 是一个实体,您可以在其中配置系统,以确定您感兴趣的汇率。源将为您提供第三方API提供的汇率,但并非所有汇率都将存储到本地存储库中。通过配置,您可以确定您希望系统中有哪些汇率。
  • 管理器 是一个中心组件,它将汇率系统暴露给开发者。

汇率的结构

让我们考虑汇率

  • 汇率由源提供(例如,“塞尔维亚国家银行”)
  • 汇率有其类型(例如,“中间汇率”)。汇率类型并不是一个有限集,每个银行都可以处理不同的汇率类型。通常,有三种常用的汇率类型:“中间”、“买入”和“卖出”。然而,上述国家银行,塞尔维亚国家银行,提供了5种不同的汇率。
  • 汇率有其基准货币(这通常是银行所在国家的货币)
  • 货币代码和值,当然,用于兑换,以及该汇率有效的日期。

管理器

管理器遵循汇率结构,并暴露API,允许您查询所需的汇率

  • Manager::get($sourceName, $currencyCode, \DateTime $date, $rateType) 将为您提供特定来源、提供日期和提供类型的汇率。
  • Manager::latest($sourceName, $currencyCode, $rateType) 将为您提供最新的可用汇率。
  • Manager::today($sourceName, $currencyCode, $rateType) 将为您提供应在当前系统日期使用的汇率。这意味着如果它是星期六或星期日,并且汇率不可用,则将使用上一个星期五的汇率。
  • Manager::historical($sourceName, $currencyCode, \DateTime $date, $rateType) 将为您提供应在给定日期使用的汇率。这意味着如果它是星期六或星期日,并且汇率不可用,则将使用给定日期前一个星期五的汇率。

系统定义了“中间”汇率,假设每个源都将至少为您提供一种汇率类型,这通常是一个中间汇率。每个实现都应该声明一个可用的汇率类型作为默认值。建议将“中间”汇率类型作为默认值。

管理器的引导

以下示例中提供了管理器初始化的示例,您可以在您的应用程序中使用它。

    // We are using Composer for autoloading
    include 'vendor/autoload.php';
    
    use RunOpenCode\ExchangeRate\Configuration;
    use RunOpenCode\ExchangeRate\Manager;
    use RunOpenCode\ExchangeRate\Processor\BaseCurrencyValidator;
    use RunOpenCode\ExchangeRate\Processor\UniqueRatesValidator;
    use RunOpenCode\ExchangeRate\Registry\ProcessorsRegistry;
    use RunOpenCode\ExchangeRate\Registry\RatesConfigurationRegistry;
    use RunOpenCode\ExchangeRate\Registry\SourcesRegistry;
    use RunOpenCode\ExchangeRate\Repository\FileRepository;
    
    // Create sources registry
    $sourcesRegistry = new SourcesRegistry();
    
    // And register your sources. You can make your own sources, or find them on packagist, this is just example.
    $sourcesRegistry->add(new MySource());
    $sourcesRegistry->add(new MyOtherSource());
    
    // Create your configuration, register which rates you would like to fetch
    $configurationsRegistry = new RatesConfigurationRegistry(array(
        new Configuration('EUR', 'median', 'my_source'),
        new Configuration('CHF', 'median', 'my_source'),
        new Configuration('USD', 'median', 'my_other_source'),
    ));
    
    // Register your processors
    $processorsRegistry = new ProcessorsRegistry(array(
        new BaseCurrencyValidator(),
        new UniqueRatesValidator()
    ));
    
    // Provide repository
    $repository = new FileRepository('path/to/file/rates.db');
    
    // ... and initialize manager.
    $manager = new Manager('RSD', $repository, $sourcesRegistry, $processorsRegistry, $configurationsRegistry);

定时任务和获取汇率

如上例所示,您可以创建类似的脚本来每天由 crontab 执行以获取新鲜汇率。

    $manager = new Manager('RSD', $repository, $sourcesRegistry, $processorsRegistry, $configurationsRegistry);
    $manager->fetch();

一些设计注意事项和指南

当您设计一个使用此库中汇率的系统时,即使您使用数据库作为汇率持久化的存储,也不要与汇率建立关系。这是一种非常糟糕的做法!例如,如果您有一个外币发票,请从汇率复制所有数据到您的发票模型,不要通过外键或任何其他方法引用汇率。

这样做的原因是为了保持数据的一致性和灵活性

  1. 您的客户可以同意与买家/卖家特定的汇率。这是相当可能且合法的,如果使用关系,则无法支持此类请求。
  2. 如果在汇率值中发现错误并且存在关联关系,则无法更正,因为所有引用该汇率的已发行和已支付发票都会受到影响。这意味着这些发票必须存储并重新发行,系统用户将被迫向其客户进行额外解释。

然而——如果存在数据冗余,即您从汇率复制所有值到您的模型,则可以避免上述所有问题的发生。

已知源实现

以下是本库已知汇率源实现列表,您可以在您的项目中使用

已知网关

以下是已知PHP框架网关的列表

  • Symfony Bundle,可通过packagist获取:runopencode/exchange-rate-bundle,源代码通过Github获取,RunOpenCode提供。