Skip to content

Commit 817a9ec

Browse files
committed
Add blockchain statistics and caching capabilities
1 parent 10a30c7 commit 817a9ec

File tree

2 files changed

+219
-11
lines changed

2 files changed

+219
-11
lines changed

basic/83-blockchain-indexer/src/api/routes.js

Lines changed: 152 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,52 @@
11
const express = require('express');
22
const router = express.Router();
3+
const { Block } = require('../utils/db');
4+
const NodeCache = require('node-cache');
35

4-
// 获取区块信息
6+
// 创建缓存实例,默认缓存时间10分钟
7+
const cache = new NodeCache({ stdTTL: 600 });
8+
9+
/**
10+
* @api {get} /block/:blockNumber 获取区块信息
11+
* @apiName GetBlock
12+
* @apiGroup Blockchain
13+
* @apiParam {Number} blockNumber 区块号
14+
* @apiSuccess {Boolean} success 请求是否成功
15+
* @apiSuccess {Object} data 区块数据
16+
*/
517
router.get('/block/:blockNumber', async (req, res) => {
618
try {
7-
const blockNumber = req.params.blockNumber;
8-
// TODO: 从数据库获取区块信息
9-
const blockInfo = {}; // 替换为实际的数据库查询
19+
const blockNumber = parseInt(req.params.blockNumber);
20+
21+
// 参数验证
22+
if (isNaN(blockNumber) || blockNumber < 0) {
23+
return res.status(400).json({
24+
success: false,
25+
error: '无效的区块号'
26+
});
27+
}
28+
29+
// 检查缓存
30+
const cacheKey = `block_${blockNumber}`;
31+
const cachedBlock = cache.get(cacheKey);
32+
if (cachedBlock) {
33+
return res.json({
34+
success: true,
35+
data: cachedBlock
36+
});
37+
}
38+
39+
// 从数据库获取区块信息
40+
const blockInfo = await Block.findOne({ number: blockNumber });
41+
if (!blockInfo) {
42+
return res.status(404).json({
43+
success: false,
44+
error: '区块未找到'
45+
});
46+
}
47+
48+
// 设置缓存
49+
cache.set(cacheKey, blockInfo)
1050
res.json({
1151
success: true,
1252
data: blockInfo
@@ -19,12 +59,49 @@ router.get('/block/:blockNumber', async (req, res) => {
1959
}
2060
});
2161

22-
// 获取交易信息
62+
/**
63+
* @api {get} /transaction/:txHash 获取交易信息
64+
* @apiName GetTransaction
65+
* @apiGroup Blockchain
66+
* @apiParam {String} txHash 交易哈希
67+
* @apiSuccess {Boolean} success 请求是否成功
68+
* @apiSuccess {Object} data 交易数据
69+
*/
2370
router.get('/transaction/:txHash', async (req, res) => {
2471
try {
2572
const txHash = req.params.txHash;
26-
// TODO: 从数据库获取交易信息
27-
const txInfo = {}; // 替换为实际的数据库查询
73+
74+
// 参数验证
75+
if (!/^0x[0-9a-fA-F]{64}$/.test(txHash)) {
76+
return res.status(400).json({
77+
success: false,
78+
error: '无效的交易哈希'
79+
});
80+
}
81+
82+
// 检查缓存
83+
const cacheKey = `tx_${txHash}`;
84+
const cachedTx = cache.get(cacheKey);
85+
if (cachedTx) {
86+
return res.json({
87+
success: true,
88+
data: cachedTx
89+
});
90+
}
91+
92+
// 从数据库获取交易信息
93+
const block = await Block.findOne({ 'transactions.hash': txHash });
94+
const txInfo = block ? block.transactions.find(tx => tx.hash === txHash) : null;
95+
96+
if (!txInfo) {
97+
return res.status(404).json({
98+
success: false,
99+
error: '交易未找到'
100+
});
101+
}
102+
103+
// 设置缓存
104+
cache.set(cacheKey, txInfo)
28105
res.json({
29106
success: true,
30107
data: txInfo
@@ -37,16 +114,80 @@ router.get('/transaction/:txHash', async (req, res) => {
37114
}
38115
});
39116

40-
// 获取地址的交易历史
117+
/**
118+
* @api {get} /address/:address/transactions 获取地址的交易历史
119+
* @apiName GetAddressTransactions
120+
* @apiGroup Blockchain
121+
* @apiParam {String} address 以太坊地址
122+
* @apiParam {Number} [page=1] 页码
123+
* @apiParam {Number} [limit=10] 每页记录数
124+
* @apiSuccess {Boolean} success 请求是否成功
125+
* @apiSuccess {Object} data 交易列表和分页信息
126+
*/
41127
router.get('/address/:address/transactions', async (req, res) => {
42128
try {
43129
const address = req.params.address;
44130
const page = parseInt(req.query.page) || 1;
45131
const limit = parseInt(req.query.limit) || 10;
46132

47-
// TODO: 从数据库获取地址的交易历史
48-
const transactions = []; // 替换为实际的数据库查询
49-
const total = 0; // 替换为实际的总记录数
133+
// 参数验证
134+
if (!/^0x[0-9a-fA-F]{40}$/.test(address)) {
135+
return res.status(400).json({
136+
success: false,
137+
error: '无效的以太坊地址'
138+
});
139+
}
140+
141+
if (page < 1 || limit < 1 || limit > 100) {
142+
return res.status(400).json({
143+
success: false,
144+
error: '无效的分页参数'
145+
});
146+
}
147+
148+
// 检查缓存
149+
const cacheKey = `addr_tx_${address}_${page}_${limit}`;
150+
const cachedResult = cache.get(cacheKey);
151+
if (cachedResult) {
152+
return res.json({
153+
success: true,
154+
data: cachedResult
155+
});
156+
}
157+
158+
// 从数据库获取地址的交易历史
159+
const query = {
160+
$or: [
161+
{ 'transactions.from': address },
162+
{ 'transactions.to': address }
163+
]
164+
};
165+
166+
const blocks = await Block.find(query)
167+
.sort({ timestamp: -1 })
168+
.skip((page - 1) * limit)
169+
.limit(limit);
170+
171+
const transactions = blocks.reduce((txs, block) => {
172+
const addressTxs = block.transactions.filter(tx =>
173+
tx.from === address || tx.to === address
174+
);
175+
return [...txs, ...addressTxs];
176+
}, []);
177+
178+
const total = await Block.countDocuments(query);
179+
180+
const result = {
181+
transactions,
182+
pagination: {
183+
page,
184+
limit,
185+
total
186+
}
187+
};
188+
189+
// 设置缓存
190+
cache.set(cacheKey, result)
50191

51192
res.json({
52193
success: true,
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
const express = require('express');
2+
const router = express.Router();
3+
const { Block } = require('../utils/db');
4+
const NodeCache = require('node-cache');
5+
6+
const cache = new NodeCache({ stdTTL: 600 });
7+
8+
/**
9+
* @api {get} /stats 获取区块链统计信息
10+
* @apiName GetBlockchainStats
11+
* @apiGroup Blockchain
12+
* @apiSuccess {Boolean} success 请求是否成功
13+
* @apiSuccess {Object} data 统计数据
14+
*/
15+
router.get('/', async (req, res) => {
16+
try {
17+
// 检查缓存
18+
const cacheKey = 'blockchain_stats';
19+
const cachedStats = cache.get(cacheKey);
20+
if (cachedStats) {
21+
return res.json({
22+
success: true,
23+
data: cachedStats
24+
});
25+
}
26+
27+
// 获取最新区块
28+
const latestBlock = await Block.findOne().sort({ number: -1 });
29+
30+
// 获取24小时内的区块数
31+
const oneDayAgo = Date.now() - 24 * 60 * 60 * 1000;
32+
const blocksLast24h = await Block.countDocuments({
33+
timestamp: { $gte: oneDayAgo }
34+
});
35+
36+
// 获取总交易数
37+
const totalTxs = await Block.aggregate([
38+
{
39+
$group: {
40+
_id: null,
41+
count: { $sum: { $size: '$transactions' } }
42+
}
43+
}
44+
]);
45+
46+
const stats = {
47+
latestBlockNumber: latestBlock ? latestBlock.number : 0,
48+
blocksLast24h,
49+
totalTransactions: totalTxs[0] ? totalTxs[0].count : 0
50+
};
51+
52+
// 设置缓存
53+
cache.set(cacheKey, stats);
54+
55+
res.json({
56+
success: true,
57+
data: stats
58+
});
59+
} catch (error) {
60+
res.status(500).json({
61+
success: false,
62+
error: error.message
63+
});
64+
}
65+
});
66+
67+
module.exports = router;

0 commit comments

Comments
 (0)