学习

使用 Redis 的手机银行账户仪表板

Will Johnston
作者
Will Johnston, Redis 开发者增长经理
Prasan Kumar
作者
Prasan Kumar, Redis 技术解决方案开发者
GITHUB 代码

用于克隆本教程中所用应用源代码的命令如下:

git clone --branch v1.2.0 https://github.com/redis-developer/mobile-banking-solutions

什么是手机银行账户仪表板?#

账户仪表板是手机银行应用中的一个页面,可立即向用户展示账户概览。客户可以点击仪表板上的任意账户,查看实时账户详情,例如最新交易、剩余按揭金额、支票账户和储蓄账户等。

账户仪表板让客户的财务信息集中在一个地方,一目了然。它降低了客户处理财务的复杂性,并培养了客户忠诚度。

下图是账户仪表板的数据架构示例

  1. 1.银行将信息存储在多个独立的数据库中,这些数据库支持各自的银行产品
  2. 2.使用 Redis Data Integration (RDI) 将银行产品组合中的主要客户账户详情(余额、最近交易)预取到 Redis Cloud 中
  3. 3.Redis Cloud 为客户的账户仪表板提供支持,使手机银行用户登录后即可立即查看余额及其他高优先级信息

为什么您应该在手机银行中使用 Redis 构建账户仪表板?#

  • 弹性:Redis Cloud 提供 99.999% 的正常运行时间和 Active-Active 地理分布,以防止关键用户配置文件数据丢失
  • 可伸缩性:Redis Cloud 在极高规模下提供 < 1ms 的性能,确保应用在峰值负载下表现良好
  • JSON 支持:支持以 JSON 文档形式创建和存储账户信息,并享有 Redis < 1ms 的速度
  • 查询和索引:Redis Cloud 可以快速识别并存储来自多个不同数据库的数据,并对数据进行索引,使其易于搜索
注意

Redis Stack 支持 JSON 数据类型,并允许您索引和查询 JSON 以及 更多数据。因此您的 Redis 数据不限于简单的键值字符串化数据。

使用 Redis 构建账户仪表板#

注意

用于克隆本教程中所用应用源代码的命令如下:

git clone --branch v1.2.0 https://github.com/redis-developer/mobile-banking-solutions

下载上面的源代码并运行以下命令来启动演示应用

docker compose up -d

在 Docker 启动并运行后,在浏览器中打开 http://localhost:8080/ 网址以查看应用

数据填充#

此应用利用了 Redis 核心数据结构、JSON、TimeSeries、搜索和查询功能。填充的数据随后用于展示可搜索的交易概览(带实时更新)以及个人财务管理概览(带实时余额和最大支出者更新)。

在应用启动时(位于 app/server.js 中),会安排一个 cron 定时任务,以定期创建随机银行交易并将其填充到 Redis 中。

app/server.js
//cron job to trigger createBankTransaction() at regular intervals

cron.schedule('*/10 * * * * *', async () => {
  const userName = process.env.REDIS_USERNAME;

  createBankTransaction(userName);

  //...
});
  • 交易生成器会创建随机的银行借记或贷记交易,这将反映在(默认)起始用户余额 $100,000.00 上
  • 交易数据 作为 JSON 文档保存在 Redis 中。
  • 为了捕获 随时间变化的余额,每次交易的 balanceAfter 值都会记录到键为 balance_ts 的 TimeSeries 中。
  • 为了追踪 最大支出者,有序集合 bigspenders 中的关联成员 fromAccountName 会按交易金额进行递增。请注意,此金额可以是正数或负数。
app/transactions/transactionsGenerator.js
let balance = 100000.0;
const BALANCE_TS = 'balance_ts';
const SORTED_SET_KEY = 'bigspenders';

export const createBankTransaction = async () => {
  //to create random bank transaction
  let vendorsList = source.source; //app/transactions/transaction_sources.js
  const random = Math.floor(Math.random() * 9999999999);

  const vendor = vendorsList[random % vendorsList.length]; //random vendor from the list

  const amount = createTransactionAmount(vendor.fromAccountName, random);
  const transaction = {
    id: random * random,
    fromAccount: Math.floor((random / 2) * 3).toString(),
    fromAccountName: vendor.fromAccountName,
    toAccount: '1580783161',
    toAccountName: 'bob',
    amount: amount,
    description: vendor.description,
    transactionDate: new Date(),
    transactionType: vendor.type,
    balanceAfter: balance,
  };

  //redis json feature
  const bankTransaction = await bankTransactionRepository.save(transaction);
  console.log('Created bankTransaction!');
  // ...
};

const createTransactionAmount = (vendor, random) => {
  let amount = createAmount(); //random amount
  balance += amount;
  balance = parseFloat(balance.toFixed(2));

  //redis time series feature
  redis.ts.add(BALANCE_TS, '*', balance, { DUPLICATE_POLICY: 'first' });
  //redis sorted set as secondary index
  redis.zIncrBy(SORTED_SET_KEY, amount * -1, vendor);

  return amount;
};

使用 RedisInsight 查看 bankTransaction 数据的示例

提示

下载 RedisInsight 以查看您的 Redis 数据或在工作台中使用原始 Redis 命令。

随时间变化的余额#

仪表板小部件

API 端点

端点

/transaction/balance

代码位置

/routers/transaction-router.js

参数

返回值

[{x: timestamp, y: value}, ...]

余额端点利用了 Time Series。它返回时间序列对象 balance_ts 的所有值范围。结果范围被转换为一个对象数组,每个对象包含一个包含时间戳的 x 属性和一个包含关联值的 y 属性。此端点为时间序列图表提供坐标,以绘制随时间变化的余额的可视化图。

app/routers/transaction-router.js
const BALANCE_TS = 'balance_ts';

/* fetch transactions up to sometime ago */
transactionRouter.get('/balance', async (req, res) => {
  //time series range
  const balance = await redis.ts.range(
    BALANCE_TS,
    Date.now() - 1000 * 60 * 5, //from
    Date.now(), //to
  );

  let balancePayload = balance.map((entry) => {
    return {
      x: entry.timestamp,
      y: entry.value,
    };
  });

  res.send(balancePayload);
});

最大支出者#

仪表板小部件

API 端点

端点

/transaction//biggestspenders

代码位置

/routers/transaction-router.js

参数

返回值

{labels:[...], series:[...] }

最大支出者端点利用了 有序集合 作为二级索引。它检索有序集合 bigspenders 中分数大于零的所有成员。返回排名前五或更少的成员,以便为 UI 饼图提供数据。labels 数组包含最大支出者的姓名,series 数组包含与每个成员姓名关联的数值。

app/routers/transaction-router.js
const SORTED_SET_KEY = 'bigspenders';

/* fetch top 5 biggest spenders */
transactionRouter.get('/biggestspenders', async (req, res) => {
  const range = await redis.zRangeByScoreWithScores(
    SORTED_SET_KEY,
    0,
    Infinity,
  );
  let series = [];
  let labels = [];

  range.slice(0, 5).forEach((spender) => {
    series.push(parseFloat(spender.score.toFixed(2)));
    labels.push(spender.value);
  });

  res.send({ series, labels });
});

搜索现有交易#

仪表板小部件

API 端点

端点

/transaction/search

代码位置

/routers/transaction-router.js

查询参数

term

返回值

匹配 term 的结果数组

搜索端点利用了 搜索和查询功能。它接收来自 UI 的 term 查询参数。针对字段 description、 fromAccountName 和 accountType 的 Redis om Node 查询将被触发并返回结果。

app/routers/transaction-router.js
transactionRouter.get('/search', async (req, res) => {
  const term = req.query.term;

  let results;

  if (term.length >= 3) {
    results = await bankRepo
      .search()
      .where('description')
      .matches(term)
      .or('fromAccountName')
      .matches(term)
      .or('transactionType')
      .equals(term)
      .return.all({ pageSize: 1000 });
  }
  res.send(results);
});

获取最近交易#

仪表板小部件

API 端点

端点

/transaction/transactions

代码位置

/routers/transaction-router.js

参数

返回值

结果数组

即使是 transactions 端点也利用了 搜索和查询功能。 Redis om Node 查询将被触发并返回十条最近的交易。

app/routers/transaction-router.js
/* return ten most recent transactions */
transactionRouter.get('/transactions', async (req, res) => {
  const transactions = await bankRepo
    .search()
    .sortBy('transactionDate', 'DESC')
    .return.all({ pageSize: 10 });

  res.send(transactions.slice(0, 10));
});

准备好在账户仪表板中使用 Redis 了吗?#

希望本教程能帮助您了解如何在账户仪表板(特别是手机银行场景)中使用 Redis。有关此主题的更多资源,请查看以下链接

附加资源#