ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

InvenTree 货币汇率插件(InvenTree Currency Exchange):默认汇率提供器的实现与使用详解

InvenTree 货币汇率插件(InvenTree Currency Exchange):默认汇率提供器的实现与使用详解 InvenTree 货币汇率插件InvenTree Currency Exchange默认汇率提供器的实现与使用详解【免费下载链接】InvenTreeOpen Source Inventory Management System项目地址: https://gitcode.com/GitHub_Trending/in/InvenTree导读InvenTree Currency Exchange是 InvenTree 开源库存管理系统内置的默认货币汇率插件负责从外部汇率 APIfrankfurter.app拉取实时汇率数据供系统在用户界面中跨币种显示价格。本文以该插件的官方文档为主线结合仓库源码深入讲解其激活机制、核心实现原理、后台更新调度、相关全局设置以及如何编写和配置替代的汇率插件帮助你全面掌握 InvenTree 货币汇率体系的运作方式。插件定位InvenTree 的默认汇率提供器InvenTreeCurrencyExchange插件是 InvenTree 插件体系中CurrencyExchangeMixin货币汇率 Mixin的默认实现。它的作用是当 InvenTree 需要在不同货币之间换算价格时从外部 API 获取最新的汇率数据并写入系统数据库。该插件的源码位于 src/backend/InvenTree/plugin/builtin/integration/currency_exchange.py核心类定义如下class InvenTreeCurrencyExchange(APICallMixin, CurrencyExchangeMixin, InvenTreePlugin): Default InvenTree plugin for currency exchange rates. Fetches exchange rate information from frankfurter.app NAME InvenTreeCurrencyExchange SLUG inventreecurrencyexchange AUTHOR _(InvenTree contributors) TITLE _(InvenTree Currency Exchange) DESCRIPTION _(Default currency exchange integration) VERSION 1.0.0从源码可以看出该插件同时继承了APICallMixin提供 API 调用能力与CurrencyExchangeMixin声明汇率更新能力并使用SLUG inventreecurrencyexchange作为唯一标识——这个 SLUG 也是全局设置CURRENCY_UPDATE_PLUGIN的默认值。汇率数据来源frankfurter.app API插件通过api_url属性指定数据源然后调用update_exchange_rates方法发起请求property def api_url(self): Return the API URL for this plugin. return https://api.frankfurter.app def update_exchange_rates(self, base_currency: str, symbols: list[str]) - dict: Request exchange rate data from external API. response self.api_call( latest, url_args{from: [base_currency], to: symbols}, simple_responseFalse, ) if response.status_code 200: rates response.json().get(rates, {}) rates[base_currency] 1.00 return rates logger.warning( Failed to update exchange rates from %s: Server returned status %s, self.api_url, response.status_code, ) return {}请求逻辑的关键点请求端点https://api.frankfurter.app/latest参数from指定基准货币、to指定目标货币列表返回值若 HTTP 状态码为 200解析响应的rates字段并强制把基准货币自身的汇率设为1.00保证自兑汇率为 1失败处理非 200 状态码时仅记录 warning 日志并返回空字典{}由上层逻辑决定是否保留旧汇率。货币汇率 Mixin 契约所有汇率插件都必须继承 src/backend/InvenTree/plugin/base/integration/CurrencyExchangeMixin.py 中的CurrencyExchangeMixin其职责是在插件注册时向系统声明“本插件支持汇率更新”能力class CurrencyExchangeMixin: class MixinMeta: MIXIN_NAME CurrentExchange def __init__(self): super().__init__() self.add_mixin(PluginMixinEnum.CURRENCY_EXCHANGE, True, __class__) def update_exchange_rates(self, base_currency: str, symbols: list[str]) - dict: Update currency exchange rates. This method *must* be implemented by the plugin class. raise MixinNotImplementedError( Plugin must implement update_exchange_rates method )Mixin 通过add_mixin(PluginMixinEnum.CURRENCY_EXCHANGE, True, __class__)把插件注册进“活跃汇率插件”集合并在基类中声明了必须由子类实现的update_exchange_rates(base_currency, symbols)方法契约base_currency基准货币代码如USDsymbols需要获取汇率的目标货币代码列表返回值{货币代码: 汇率值}字典更新失败时返回空值。激活机制强制启用的内置插件官方文档明确指出该插件是强制启用的mandatory plugin始终处于激活状态且没有可配置的设置项。也就是说无论 InvenTree 实例中CURRENCY_UPDATE_PLUGIN全局设置指向哪个自定义汇率插件InvenTreeCurrencyExchange始终会被注册为活跃的汇率插件可以作为备选方案随时接管汇率更新任务。这一点在 InvenTree/exchange.py 的后端选择逻辑中得到印证见下文“汇率更新后端”一节。由于该插件不暴露任何插件级设置Plugin Settings它的所有行为都由 InvenTree 全局设置与任务调度机制统一驱动不存在需要用户单独配置的参数。使用方式从汇率拉取到价格换算的完整链路文档说明该插件是 InvenTree 的默认汇率提供器用于在用户界面展示价格时进行货币转换并且可作为备选提供器。整个链路可拆解为以下环节1. 汇率更新后端InvenTreeExchangesrc/backend/InvenTree/InvenTree/exchange.py 中的InvenTreeExchange类继承自djmoney的SimpleExchangeBackend是连接“插件系统”与“数据库汇率存储”的桥梁def get_rates(self, **kwargs) - dict: from plugin import PluginMixinEnum, registry base_currency kwargs.get(base_currency, currency_code_default()) symbols kwargs.get(symbols, currency_codes()) # Find the selected exchange rate plugin slug get_global_setting(CURRENCY_UPDATE_PLUGIN, createFalse) plugin registry.get_plugin(slug, activeTrue) if slug else None if not plugin: # Find the first active currency exchange plugin plugins registry.with_mixin(PluginMixinEnum.CURRENCY_EXCHANGE, activeTrue) if len(plugins) 0: plugin plugins[0] if not plugin: logger.warning(No active currency exchange plugins found - skipping update) return {} try: rates plugin.update_exchange_rates(base_currency, symbols) except Exception as exc: logger.exception(Exchange rate update failed: %s, exc) return {} ...插件选择优先级这段代码正是文档中“可配置替代提供器、本插件始终作为备选”的源码实现读取全局设置CURRENCY_UPDATE_PLUGIN指定的插件 SLUG若存在且处于激活状态则使用该插件若未配置或插件不可用则从所有激活的CURRENCY_EXCHANGEMixin 插件中取第一个若仍无可用插件记录 warning 并跳过本次更新。由于内置插件inventreecurrencyexchange是强制激活的在任何情况下它都能兜底完成汇率获取。获取到汇率字典后后端在事务中完成数据落库update_rates方法先update_or_create汇率后端记录随后clear_rates()清空旧数据再通过Rate.objects.bulk_create(...)批量写入新汇率atomic def update_rates(self, base_currencyNone, **kwargs): backend, _ ExchangeBackend.objects.update_or_create( nameself.name, defaults{base_currency: base_currency} ) ... rates self.get_rates(base_currencybase_currency, symbolssymbols) if rates: backend.clear_rates() Rate.objects.bulk_create([ Rate(currencycurrency, valueamount, backendbackend) for currency, amount in rates.items() ])这一设计保证若插件返回空数据或调用异常旧汇率不会被破坏系统仍可使用上次成功的汇率数据。2. 后台任务调度每日自动更新汇率更新由 InvenTree 后台任务系统驱动定义在 InvenTree/tasks.pytracer.start_as_current_span(update_exchange_rates) scheduled_task(ScheduledTask.DAILY) def update_exchange_rates(force: bool False): ... if not force: interval int(get_global_setting(CURRENCY_UPDATE_INTERVAL, 1, cacheFalse)) if not check_daily_holdoff(update_exchange_rates, interval): logger.info(Skipping exchange rate update (interval not reached)) return backend InvenTreeExchange() base currency_code_default() backend.update_rates(base_currencybase) # Remove any exchange rates which are not in the provided currencies Rate.objects.filter(backendInvenTreeExchange).exclude( currency__incurrency_codes() ).delete()要点解析该任务被标记为每日调度任务ScheduledTask.DAILY实际执行频率受全局设置CURRENCY_UPDATE_INTERVAL单位天控制通过check_daily_holdoff判断是否达到更新间隔间隔设为 0 即可禁用自动更新更新完成后会清理数据库中不属于当前“受支持货币列表”的汇率记录避免数据冗余。3. 手动触发与价格重算除自动调度外系统提供多种手动触发路径API 触发common/api.py 中的CurrencyViewSet支持 POST 请求强制更新汇率update_exchange_rates(forceTrue)同时 GET 请求可查询当前基准货币、汇率及缓存时间设置变更触发当修改INVENTREE_DEFAULT_CURRENCY或CURRENCY_CODES全局设置时会触发after_change_currency回调见 common/currency.py该回调立即执行强制汇率更新并把“重算所有部件价格”的任务离队到后台执行保证价格数据与新的货币配置保持一致。4. 价格换算落地汇率最终被djmoney的货币换算机制消费在计算部件价格时通过convert_to(currency)将价格从一种货币换算为目标货币见 common/currency.py 中的get_price函数。正是因为汇率插件提供了实时数据InvenTree 才能在采购、销售与库存界面中正确显示不同币种的价格。相关全局设置一览虽然汇率插件本身没有配置项但 InvenTree 通过以下全局设置控制货币与汇率的整体行为。这些设置定义于 common/setting/system.py并收录在 docs/docs/settings/global.md 的 “Pricing and Currency” 小节设置键说明默认值单位/取值INVENTREE_DEFAULT_CURRENCY价格计算的基准货币USDISO 货币代码CURRENCY_CODES受支持的货币代码列表逗号分隔AUD,CAD,CNY,EUR,GBP,JPY,NZD,USD逗号分隔字符串也支持环境变量INVENTREE_CURRENCY_CODESCURRENCY_UPDATE_INTERVAL汇率自动更新频率1天设为 0 禁用自动更新值不能为负CURRENCY_UPDATE_PLUGIN汇率更新使用的插件inventreecurrencyexchange插件 SLUG选项来自所有激活的汇率插件其中CURRENCY_UPDATE_PLUGIN的选择列表由currency_exchange_plugins()动态生成见 common/currency.py它遍历注册表中所有激活的CURRENCY_EXCHANGEMixin 插件并额外提供一个“无插件”选项。若没有激活的汇率插件该函数返回None设置将不提供下拉选项。编写自定义汇率插件官方文档提到“可以在 InvenTree 设置中配置替代的汇率提供器”仓库中的示例插件 plugin/samples/integration/sample_currency_exchange.py 给出了一个最小可用的参考实现class SampleCurrencyExchangePlugin(CurrencyExchangeMixin, InvenTreePlugin): Dummy currency exchange plugin which provides fake exchange rates. NAME Sample Exchange DESCRIPTION _(Sample currency exchange plugin) SLUG samplecurrencyexchange VERSION 0.1.0 AUTHOR _(InvenTree Contributors) def update_exchange_rates(self, base_currency: str, symbols: list[str]) - dict: Return dummy data for some currencies. rates {base_currency: 1.00} for symbol in symbols: rates[symbol] random.randrange(5, 15) * 0.1 return rates编写自定义汇率插件的三个要点继承CurrencyExchangeMixin声明插件具备汇率更新能力从而出现在CURRENCY_UPDATE_PLUGIN设置的可选项中实现update_exchange_rates(base_currency, symbols)按契约返回{代码: 汇率}字典基准货币汇率恒为1.00失败时返回空值或抛出异常上层会捕获并保留旧汇率激活插件插件需处于激活状态才能被后台调度器选用激活后在管理界面把CURRENCY_UPDATE_PLUGIN切换为自定义插件的 SLUG 即可生效。小结InvenTreeCurrencyExchange作为 InvenTree 的默认汇率提供器以极简的插件形态完成了“外部 API 拉取 → 汇率落库 → 价格换算”的完整闭环它强制激活、无配置负担任何情况下都能作为兜底选项而通过CURRENCY_UPDATE_PLUGIN设置用户可以无缝替换为自己的汇率实现体现了 InvenTree 插件体系良好的扩展性。理解其源码实现也能帮助你更自如地为 InvenTree 编写属于自己的货币汇率集成插件。【免费下载链接】InvenTreeOpen Source Inventory Management System项目地址: https://gitcode.com/GitHub_Trending/in/InvenTree创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表