用于克隆本教程中所用应用源代码的命令如下:
git clone --branch v1.2.0 https://github.com/redis-developer/mobile-banking-solutions
账户仪表板是手机银行应用中的一个页面,可立即向用户展示账户概览。客户可以点击仪表板上的任意账户,查看实时账户详情,例如最新交易、剩余按揭金额、支票账户和储蓄账户等。
账户仪表板让客户的财务信息集中在一个地方,一目了然。它降低了客户处理财务的复杂性,并培养了客户忠诚度。
下图是账户仪表板的数据架构示例
Redis Stack 支持 JSON 数据类型,并允许您索引和查询 JSON 以及 更多数据。因此您的 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 中。
//cron job to trigger createBankTransaction() at regular intervals
cron.schedule('*/10 * * * * *', async () => {
const userName = process.env.REDIS_USERNAME;
createBankTransaction(userName);
//...
});
balanceAfter
值都会记录到键为 balance_ts
的 TimeSeries 中。bigspenders
中的关联成员 fromAccountName
会按交易金额进行递增。请注意,此金额可以是正数或负数。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
属性。此端点为时间序列图表提供坐标,以绘制随时间变化的余额的可视化图。
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 数组包含与每个成员姓名关联的数值。
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 查询将被触发并返回结果。
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 查询将被触发并返回十条最近的交易。
/* 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。有关此主题的更多资源,请查看以下链接